From 478e3e8d6291dbec9ae3c198440456ffdd424df5 Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Mon, 9 Dec 2024 13:37:04 -0700 Subject: [PATCH 01/15] Add vitest. --- eslint.config.js | 2 +- package-lock.json | 931 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 6 +- vite.config.js | 9 + 4 files changed, 921 insertions(+), 27 deletions(-) create mode 100644 vite.config.js diff --git a/eslint.config.js b/eslint.config.js index 90e339e5c..35e16ea03 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -130,7 +130,7 @@ export default [ }, }, { - files: ["test/**/*.{cjs,js}"], + files: ["{test,vtest}/**/*.{cjs,js}"], rules: { "import/extensions": [ "error", diff --git a/package-lock.json b/package-lock.json index fbf0b54d4..5efe13259 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,6 +56,7 @@ "glob": "^11.0.0", "globals": "^15.12.0", "handlebars": "^4.7.8", + "happy-dom": "^15.11.1", "husky": "^9.1.6", "jasmine": "^5.4.0", "jasmine-core": "^5.4.0", @@ -87,7 +88,8 @@ "testcafe-reporter-junit": "^3.0.2", "testcafe-reporter-saucelabs": "^3.5.1", "url-exists-nodejs": "^0.2.4", - "url-parse": "^1.5.10" + "url-parse": "^1.5.10", + "vitest": "^2.1.4" }, "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "^4.24.4" @@ -2187,6 +2189,397 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -3893,32 +4286,118 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/ws": { - "version": "8.5.11", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz", - "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==", + "node_modules/@types/ws": { + "version": "8.5.11", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz", + "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@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/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "license": "ISC" + }, + "node_modules/@vitest/expect": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.4.tgz", + "integrity": "sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.4", + "@vitest/utils": "2.1.4", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz", + "integrity": "sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.4.tgz", + "integrity": "sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.4", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.4.tgz", + "integrity": "sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" + "@vitest/pretty-format": "2.1.4", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "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==", + "node_modules/@vitest/spy": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.4.tgz", + "integrity": "sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@types/node": "*" + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "license": "ISC" + "node_modules/@vitest/utils": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz", + "integrity": "sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.4", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } }, "node_modules/@wdio/config": { "version": "7.33.0", @@ -5943,6 +6422,16 @@ "node": ">= 0.8" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/cacheable-lookup": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", @@ -8597,6 +9086,45 @@ "dev": true, "license": "MIT" }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -9335,6 +9863,16 @@ "node": ">=6" } }, + "node_modules/expect-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", + "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/ext-list": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", @@ -10676,6 +11214,31 @@ "uglify-js": "^3.1.4" } }, + "node_modules/happy-dom": { + "version": "15.11.1", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-15.11.1.tgz", + "integrity": "sha512-c6Ysm3743TQBBaNu26IOFtlb7rl5iS85zOq5PpZwe2NO70/8fs6nZOnC9mN1cxRsAlL7nx6G+/4kk2zBz4afJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.5.0", + "webidl-conversions": "^7.0.0", + "whatwg-mimetype": "^3.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/happy-dom/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/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -13231,14 +13794,11 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", - "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", + "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.1" - } + "license": "MIT" }, "node_modules/lower-case": { "version": "2.0.2", @@ -14410,6 +14970,13 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, "node_modules/pathval": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", @@ -14610,6 +15177,35 @@ "node": ">= 0.4" } }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/prebuild-install": { "version": "5.3.6", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.6.tgz", @@ -16356,6 +16952,13 @@ "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" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -16610,6 +17213,16 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -16752,6 +17365,13 @@ "node": ">=0.10.0" } }, + "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" + }, "node_modules/stackframe": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", @@ -16920,6 +17540,13 @@ "node": ">= 0.6" } }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true, + "license": "MIT" + }, "node_modules/stream-combiner": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", @@ -18637,6 +19264,50 @@ "node": ">=0.10.0" } }, + "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" + }, + "node_modules/tinyexec": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz", + "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", + "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -19303,6 +19974,191 @@ "dev": true, "license": "MIT" }, + "node_modules/vite": { + "version": "5.4.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", + "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.4.tgz", + "integrity": "sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.4.tgz", + "integrity": "sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.4", + "@vitest/mocker": "2.1.4", + "@vitest/pretty-format": "^2.1.4", + "@vitest/runner": "2.1.4", + "@vitest/snapshot": "2.1.4", + "@vitest/spy": "2.1.4", + "@vitest/utils": "2.1.4", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.7.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.4", + "@vitest/ui": "2.1.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.4.tgz", + "integrity": "sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", @@ -20088,6 +20944,16 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -20219,6 +21085,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", diff --git a/package.json b/package.json index ccbbad1f8..02db3e3d6 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "scripts": { "clean": "rimraf dist distTest libEs5 libEs6", "lint": "eslint --cache --fix \"*.{js,cjs,mjs,jsx}\" \"{src,test,scripts}/**/*.{js,cjs,mjs,jsx}\"", - "format": "prettier --write \"*.{html,js,cjs,mjs,jsx}\" \"{sandbox,src,test,scripts}/**/*.{html,js,cjs,mjs,jsx}\"", + "format": "prettier --write \"*.{html,js,cjs,mjs,jsx}\" \"{sandbox,src,test,scripts,vtest}/**/*.{html,js,cjs,mjs,jsx}\"", "test": "npm run test:unit && npm run test:scripts && npm run test:functional", "test:unit": "karma start karma.conf.cjs --single-run", "test:unit:debug": "karma start karma.conf.cjs --browsers=Chrome --single-run=false --debug", @@ -115,6 +115,7 @@ "glob": "^11.0.0", "globals": "^15.12.0", "handlebars": "^4.7.8", + "happy-dom": "^15.11.1", "husky": "^9.1.6", "jasmine": "^5.4.0", "jasmine-core": "^5.4.0", @@ -146,7 +147,8 @@ "testcafe-reporter-junit": "^3.0.2", "testcafe-reporter-saucelabs": "^3.5.1", "url-exists-nodejs": "^0.2.4", - "url-parse": "^1.5.10" + "url-parse": "^1.5.10", + "vitest": "^2.1.4" }, "overrides": { "karma-sauce-launcher": { diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 000000000..bce6eb2bc --- /dev/null +++ b/vite.config.js @@ -0,0 +1,9 @@ +// eslint-disable-next-line import/no-unresolved +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["vtest/**/*.{test,spec}.?(c|m)[jt]s?(x)"], + environment: "happy-dom", + }, +}); From deb6f5677afe48bce204ee04adb2bbf0634fe0ce Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Mon, 9 Dec 2024 13:41:45 -0700 Subject: [PATCH 02/15] Changes made to tests in order to convert them easier. Add script that converts tests from jasmine to vitest. Some changes created by linting fix. New tests for vitest. Fix test. Fix lint error. New batch updated. Undo the changes to the test and add it to the browser suite. Run browser in headless mode. Improve test speed. New batch. New batch. Last batch converted. Fix some flaky tests. --- package-lock.json | 1310 +++++++++++++-- package.json | 5 +- scripts/testConvert.js | 367 +++++ .../dom-actions/dom/createFragment.js | 2 +- .../Personalization/dom-actions/dom/util.js | 2 +- .../StreamingMedia/createTrackMediaSession.js | 2 +- .../validateMediaSessionOptions.js | 4 +- src/utils/request/createRequestParams.js | 7 +- .../createGetInstance.spec.js | 2 +- .../unit/specs/utils/validation/utils.spec.js | 5 +- vite.config.js | 3 + vitest.workspace.js | 36 + vtest/unit/constants/uuidV4Regex.js | 13 + vtest/unit/helpers/assertFunctionCallOrder.js | 25 + vtest/unit/helpers/cleanUpDomChanges.js | 25 + .../createDecoratePropositionForTest.js | 54 + vtest/unit/helpers/createMockProposition.js | 29 + vtest/unit/helpers/createResponse.js | 15 + vtest/unit/helpers/describeRequest.js | 52 + vtest/unit/helpers/describeRequestPayload.js | 89 + vtest/unit/helpers/describeValidation.js | 62 + vtest/unit/helpers/flushPromiseChains.js | 28 + vtest/unit/helpers/pause.js | 16 + vtest/unit/helpers/removeAllCookies.js | 19 + vtest/unit/helpers/testConfigValidators.js | 53 + .../attachClickActivityCollector.spec.js | 121 ++ .../configValidators.spec.js | 50 + .../createClickActivityStorage.spec.js | 54 + .../createClickedElementProperties.spec.js | 252 +++ .../createGetClickedElementProperties.spec.js | 260 +++ ...eateInjectClickedElementProperties.spec.js | 157 ++ ...lAndInjectClickedElementProperties.spec.js | 86 + .../createStorePageViewProperties.spec.js | 49 + .../ActivityCollector/getLinkName.spec.js | 149 ++ .../ActivityCollector/getLinkRegion.spec.js | 109 ++ .../utils/activityMapExtensionEnabled.spec.js | 36 + .../utils/createTransientStorage.spec.js | 40 + .../utils/determineLinkType.spec.js | 53 + .../utils/dom/elementHasClickHandler.spec.js | 60 + .../utils/dom/extractDomain.spec.js | 32 + .../utils/dom/findClickableElement.spec.js | 35 + .../getAbsoluteUrlFromAnchorElement.spec.js | 64 + .../utils/dom/isButtonSubmitElement.spec.js | 41 + .../utils/dom/isDownloadLink.spec.js | 47 + .../utils/dom/isExitLink.spec.js | 42 + .../utils/dom/isInputSubmitElement.spec.js | 37 + .../dom/isSupportedAnchorElement.spec.js | 56 + .../utils/dom/isSupportedTextNode.spec.js | 41 + .../utils/hasPageName.spec.js | 41 + .../utils/isDifferentDomains.spec.js | 28 + .../utils/trimQueryFromUrl.spec.js | 32 + .../utils/truncateWhiteSpace.spec.js | 32 + .../utils/urlStartsWithScheme.spec.js | 42 + .../injectProcessDestinations.spec.js | 172 ++ .../Audiences/injectProcessResponse.spec.js | 50 + .../Consent/computeConsentHash.spec.js | 118 ++ .../Consent/configValidators.spec.js | 92 ++ .../Consent/createComponent.spec.js | 346 ++++ .../Consent/createConsentHashStore.spec.js | 69 + .../Consent/createConsentRequest.spec.js | 42 + .../createConsentRequestPayload.spec.js | 63 + .../Consent/createStoredConsent.spec.js | 55 + .../injectSendSetConsentRequest.spec.js | 175 ++ .../Consent/parseConsentCookie.spec.js | 23 + .../Consent/validateSetConsentOptions.spec.js | 178 ++ .../Context/createComponent.spec.js | 86 + .../Context/implementationDetails.spec.js | 28 + .../components/Context/injectDevice.spec.js | 130 ++ .../Context/injectEnvironment.spec.js | 197 +++ .../injectHighEntropyUserAgentHints.spec.js | 49 + .../Context/injectPlaceContext.spec.js | 75 + .../Context/injectTimestamp.spec.js | 31 + .../components/Context/injectWeb.spec.js | 38 + .../components/DataCollector/index.spec.js | 185 +++ .../validateApplyResponse.spec.js | 41 + .../validateUserEventOptions.spec.js | 68 + .../EventMerge/createComponent.spec.js | 31 + .../EventMerge/createEventMergeId.spec.js | 24 + .../Identity/addEcidToPayload.spec.js | 26 + ...ppendIdentityToUrlOptionsValidator.spec.js | 61 + .../injectAppendIdentityToUrl.spec.js | 46 + .../Identity/configValidators.spec.js | 42 + .../Identity/createComponent.spec.js | 503 ++++++ .../Identity/createLegacyIdentity.spec.js | 137 ++ .../getIdentity/createGetIdentity.spec.js | 185 +++ .../createGetIdentityOptionsValidator.spec.js | 114 ++ .../getIdentity/createIdentityRequest.spec.js | 42 + .../createIdentityRequestPayload.spec.js | 49 + .../getNamespacesFromResponse.spec.js | 56 + .../injectAddEcidQueryToPayload.spec.js | 62 + .../injectAddLegacyEcidToPayload.spec.js | 51 + ...ectAddQueryStringIdentityToPayload.spec.js | 177 ++ .../injectAwaitIdentityCookie.spec.js | 87 + .../injectEnsureSingleIdentity.spec.js | 212 +++ .../injectHandleResponseForIdSyncs.spec.js | 35 + .../Identity/injectProcessIdSyncs.spec.js | 83 + ...SetDomainForInitialIdentityPayload.spec.js | 57 + .../visitorService/awaitVisitorOptIn.spec.js | 79 + .../visitorService/getVisitor.spec.js | 33 + .../injectGetEcidFromVisitor.spec.js | 86 + .../components/LibraryInfo/index.spec.js | 40 + .../createGetInstance.spec.js | 322 ++++ ...reateMediaAnalyticsBridgeComponent.spec.js | 110 ++ .../createMediaHelper.spec.js | 213 +++ .../createActionsProvider.spec.js | 96 ++ .../createApplyPropositions.spec.js | 287 ++++ .../createClickStorage.spec.js | 117 ++ .../Personalization/createComponent.spec.js | 185 +++ .../createFetchDataHandler.spec.js | 249 +++ .../createGetPageLocation.spec.js | 31 + .../createHandleConsentFlicker.spec.js | 66 + .../createInteractionStorage.spec.js | 170 ++ .../createNotificationHandler.spec.js | 58 + .../createOnClickHandler.spec.js | 519 ++++++ .../createOnDecisionHandler.spec.js | 261 +++ .../createPersonalizationDetails.spec.js | 521 ++++++ .../createPreprocessors.spec.js | 41 + .../createSetTargetMigration.spec.js | 46 + .../createViewCacheManager.spec.js | 206 +++ .../createViewChangeHandler.spec.js | 79 + .../addNonceToInlineStyleElements.spec.js | 45 + .../dom-actions/appendHtml.spec.js | 72 + .../dom-actions/clicks/collectClicks.spec.js | 162 ++ .../clicks/collectInteractions.spec.js | 616 +++++++ .../dom-actions/collectInteractions.spec.js | 51 + .../dom-actions/createPreprocess.spec.js | 93 ++ .../dom-actions/createRedirect.spec.js | 41 + .../dom-actions/customCode.spec.js | 78 + .../dom-actions/dom/createFragment.spec.js | 21 + .../dom-actions/dom/getAttribute.spec.js | 30 + .../dom-actions/dom/getChildNodes.spec.js | 35 + .../dom-actions/dom/getChildren.spec.js | 32 + .../dom-actions/dom/getElementById.spec.js | 38 + .../dom-actions/dom/getFirstChild.spec.js | 30 + .../dom-actions/dom/getNextSibling.spec.js | 33 + .../dom-actions/dom/getNonce.spec.js | 38 + .../dom-actions/dom/getParent.spec.js | 41 + .../dom-actions/dom/helperForEq.spec.js | 38 + .../dom-actions/dom/insertAfter.spec.js | 44 + .../dom-actions/dom/isDomElement.spec.js | 43 + .../dom/matchesSelectorWithEq.spec.js | 92 ++ .../dom-actions/dom/removeAttribute.spec.js | 40 + .../dom-actions/dom/selectNodesWithEq.spec.js | 226 +++ .../dom-actions/dom/setAttribute.spec.js | 34 + .../dom-actions/dom/setStyle.spec.js | 42 + .../dom-actions/dom/util.spec.js | 27 + .../dom-actions/images.spec.js | 47 + .../dom-actions/initDomActionsModules.spec.js | 65 + .../dom-actions/insertHtmlAfter.spec.js | 82 + .../dom-actions/insertHtmlBefore.spec.js | 86 + .../Personalization/dom-actions/move.spec.js | 92 ++ .../dom-actions/prependHtml.spec.js | 88 + .../dom-actions/rearrangeChildren.spec.js | 125 ++ .../dom-actions/remapCustomCodeOffers.spec.js | 56 + .../dom-actions/remove.spec.js | 60 + .../dom-actions/replaceHtml.spec.js | 81 + .../dom-actions/resize.spec.js | 92 ++ .../dom-actions/scripts.spec.js | 72 + .../dom-actions/setAttributes.spec.js | 63 + .../dom-actions/setHtml.spec.js | 115 ++ .../dom-actions/setImageSource.spec.js | 63 + .../dom-actions/setStyles.spec.js | 64 + .../dom-actions/setText.spec.js | 62 + .../Personalization/flicker/index.spec.js | 46 + .../createDecorateProposition.spec.js | 250 +++ .../handlers/createProcessDomAction.spec.js | 198 +++ .../handlers/createProcessHtmlContent.spec.js | 131 ++ .../createProcessInAppMessage.spec.js | 139 ++ .../createProcessPropositions.spec.js | 482 ++++++ .../handlers/createProcessRedirect.spec.js | 109 ++ .../handlers/injectCreateProposition.spec.js | 89 + .../handlers/processDefaultContent.spec.js | 24 + .../actions/displayIframeContent.spec.js | 425 +++++ .../initInAppMessageActionsModules.spec.js | 25 + .../in-app-message-actions/utils.spec.js | 34 + .../responsesMock/eventResponses.js | 445 +++++ .../Personalization/topLevel/buildAlloy.js | 254 +++ .../Personalization/topLevel/buildMocks.js | 91 ++ .../topLevel/cartViewDecisions.spec.js | 268 +++ .../topLevel/mergedMetricDecisions.spec.js | 120 ++ .../topLevel/mixedPropositions.spec.js | 293 ++++ ...eDecisionsWithDomActionSchemaItems.spec.js | 179 ++ .../topLevel/pageWideScopeDecisions.spec.js | 206 +++ ...cisionsWithoutDomActionSchemaItems.spec.js | 104 ++ .../topLevel/productsViewDecisions.spec.js | 53 + .../redirectPageWideScopeDecision.spec.js | 70 + .../Personalization/topLevel/resetMocks.js | 29 + .../topLevel/scopesFoo1Foo2Decisions.spec.js | 149 ++ .../addRenderAttemptedToDecisions.spec.js | 35 + .../utils/createAsyncArray.spec.js | 57 + .../utils/isAuthoringModeEnabled.spec.js | 33 + .../Personalization/utils/metaUtils.spec.js | 194 +++ .../utils/surfaceUtils.spec.js | 248 +++ .../validateApplyPropositionsOptions.spec.js | 250 +++ .../inAppMessageConsequenceAdapter.spec.js | 64 + .../schemaTypeConsequenceAdapter.spec.js | 71 + .../RulesEngine/contextTestUtils.js | 154 ++ .../RulesEngine/createApplyResponse.spec.js | 85 + .../createConsequenceAdapter.spec.js | 76 + .../RulesEngine/createContextProvider.spec.js | 171 ++ .../RulesEngine/createDecisionHistory.spec.js | 79 + .../createDecisionProvider.spec.js | 485 ++++++ .../createEvaluableRulesetPayload.spec.js | 364 +++++ .../createEvaluateRulesetsCommand.spec.js | 225 +++ .../RulesEngine/createEventRegistry.spec.js | 401 +++++ .../createOnResponseHandler.spec.js | 390 +++++ .../createSubscribeRulesetItems.spec.js | 1437 +++++++++++++++++ .../decisioningContext.browser.spec.js | 56 + .../decisioningContext.page.spec.js | 337 ++++ .../decisioningContext.referringPage.spec.js | 278 ++++ .../decisioningContext.sdkVersion.spec.js | 58 + .../decisioningContext.timestamp.spec.js | 353 ++++ .../decisioningContext.window.spec.js | 182 +++ .../components/RulesEngine/index.spec.js | 210 +++ .../components/RulesEngine/utils.spec.js | 232 +++ .../StreamingMedia/configValidators.spec.js | 75 + .../createMediaEventManager.spec.js | 151 ++ .../StreamingMedia/createMediaRequest.spec.js | 29 + .../createMediaResponseHandler.spec.js | 94 ++ .../createMediaSessionCacheManager.spec.js | 77 + .../createStreamingMediaComponent.spec.js | 75 + .../createTrackMediaEvent.spec.js | 88 + .../createTrackMediaSession.spec.js | 125 ++ .../validateMediaEventOptions.spec.js | 66 + .../validateMediaSessionOptions.spec.js | 66 + .../specs/core/buildAndValidateConfig.spec.js | 118 ++ .../unit/specs/core/componentCreators.spec.js | 32 + .../specs/core/config/createConfig.spec.js | 61 + .../core/config/createCoreConfigs.spec.js | 161 ++ .../specs/core/consent/createConsent.spec.js | 87 + .../consent/createConsentStateMachine.spec.js | 182 +++ .../core/createComponentRegistry.spec.js | 206 +++ .../specs/core/createCookieTransfer.spec.js | 191 +++ vtest/unit/specs/core/createEvent.spec.js | 493 ++++++ .../specs/core/createEventManager.spec.js | 369 +++++ .../specs/core/createInstanceFunction.spec.js | 68 + vtest/unit/specs/core/createLifecycle.spec.js | 91 ++ .../specs/core/createLogController.spec.js | 240 +++ vtest/unit/specs/core/createLogger.spec.js | 279 ++++ .../edgeNetwork/handleRequestFailure.spec.js | 32 + .../edgeNetwork/injectApplyResponse.spec.js | 282 ++++ .../edgeNetwork/injectExtractEdgeInfo.spec.js | 69 + .../edgeNetwork/injectGetLocationHint.spec.js | 55 + .../injectProcessWarningsAndErrors.spec.js | 122 ++ .../injectSendEdgeNetworkRequest.spec.js | 555 +++++++ .../mergeLifecycleResponses.spec.js | 116 ++ .../specs/core/initializeComponents.spec.js | 99 ++ .../specs/core/injectCreateResponse.spec.js | 152 ++ .../specs/core/injectExecuteCommand.spec.js | 325 ++++ .../unit/specs/core/injectHandleError.spec.js | 49 + .../core/injectShouldTransferCookie.spec.js | 53 + .../core/network/getRequestRetryDelay.spec.js | 99 ++ .../network/injectSendNetworkRequest.spec.js | 235 +++ .../core/network/isRequestRetryable.spec.js | 48 + .../injectSendBeaconRequest.spec.js | 83 + .../injectSendFetchRequest.spec.js | 53 + .../core/requiredComponentCreators.spec.js | 32 + .../specs/core/validateCommandOptions.spec.js | 61 + .../utils/assignConcatArrayValues.spec.js | 129 ++ vtest/unit/specs/utils/clone.spec.js | 30 + vtest/unit/specs/utils/crc32.spec.js | 669 ++++++++ .../utils/createCallbackAggregator.spec.js | 44 + vtest/unit/specs/utils/createCollect.spec.js | 55 + .../utils/createLoggingCookieJar.spec.js | 59 + vtest/unit/specs/utils/createMerger.spec.js | 73 + .../specs/utils/createSubscription.spec.js | 142 ++ .../unit/specs/utils/createTaskQueue.spec.js | 131 ++ .../utils/decodeUriComponentSafely.spec.js | 27 + .../unit/specs/utils/deduplicateArray.spec.js | 57 + vtest/unit/specs/utils/deepAssign.spec.js | 103 ++ vtest/unit/specs/utils/defer.spec.js | 34 + vtest/unit/specs/utils/dom/appendNode.spec.js | 30 + .../specs/utils/dom/awaitSelector.spec.js | 52 + vtest/unit/specs/utils/dom/createNode.spec.js | 53 + .../specs/utils/dom/isShadowSelector.spec.js | 26 + .../specs/utils/dom/matchesSelector.spec.js | 23 + .../specs/utils/dom/querySelectorAll.spec.js | 43 + vtest/unit/specs/utils/dom/removeNode.spec.js | 30 + .../unit/specs/utils/dom/selectNodes.spec.js | 23 + .../utils/dom/selectNodesWithShadow.spec.js | 200 +++ vtest/unit/specs/utils/event.spec.js | 72 + vtest/unit/specs/utils/filterObject.spec.js | 63 + vtest/unit/specs/utils/flattenArray.spec.js | 79 + vtest/unit/specs/utils/flattenObject.spec.js | 113 ++ vtest/unit/specs/utils/getApexDomain.spec.js | 67 + .../specs/utils/getLastArrayItems.spec.js | 25 + .../utils/getNamespacedCookieName.spec.js | 21 + .../specs/utils/getNamespacedStorage.spec.js | 27 + vtest/unit/specs/utils/groupBy.spec.js | 101 ++ ...hirdPartyCookiesSupportedByDefault.spec.js | 49 + .../injectDoesIdentityCookieExist.spec.js | 36 + .../injectFireReferrerHideableImage.spec.js | 78 + .../unit/specs/utils/injectGetBrowser.spec.js | 70 + vtest/unit/specs/utils/injectStorage.spec.js | 112 ++ vtest/unit/specs/utils/intersection.spec.js | 25 + vtest/unit/specs/utils/isBlankString.spec.js | 29 + vtest/unit/specs/utils/isBoolean.spec.js | 27 + vtest/unit/specs/utils/isEmptyObject.spec.js | 31 + vtest/unit/specs/utils/isFunction.spec.js | 26 + vtest/unit/specs/utils/isInteger.spec.js | 27 + .../utils/isNamespacedCookieName.spec.js | 31 + vtest/unit/specs/utils/isNil.spec.js | 31 + .../unit/specs/utils/isNonEmptyArray.spec.js | 30 + .../unit/specs/utils/isNonEmptyString.spec.js | 26 + vtest/unit/specs/utils/isNumber.spec.js | 29 + vtest/unit/specs/utils/isObject.spec.js | 26 + vtest/unit/specs/utils/isString.spec.js | 26 + vtest/unit/specs/utils/isUnique.spec.js | 23 + vtest/unit/specs/utils/isValidRegExp.spec.js | 27 + vtest/unit/specs/utils/lazy.spec.js | 49 + vtest/unit/specs/utils/networkErrors.spec.js | 78 + vtest/unit/specs/utils/noop.spec.js | 20 + vtest/unit/specs/utils/parseUrl.spec.js | 56 + .../prepareConfigOverridesForEdge.spec.js | 81 + .../utils/request/createAddIdentity.spec.js | 81 + .../createDataCollectionRequest.spec.js | 78 + ...createDataCollectionRequestPayload.spec.js | 91 ++ ...eGetAssuranceValidationTokenParams.spec.js | 62 + .../utils/request/createHasIdentity.spec.js | 42 + .../utils/request/createRequestParams.spec.js | 65 + .../utils/sanitizeOrgIdForCookieName.spec.js | 21 + vtest/unit/specs/utils/stackError.spec.js | 39 + .../unit/specs/utils/stringToBoolean.spec.js | 26 + vtest/unit/specs/utils/toArray.spec.js | 33 + vtest/unit/specs/utils/toError.spec.js | 28 + .../unit/specs/utils/toISOStringLocal.spec.js | 36 + vtest/unit/specs/utils/toInteger.spec.js | 42 + .../specs/utils/updateErrorMessage.spec.js | 41 + .../utils/validateConfigOverride.spec.js | 85 + .../specs/utils/validateIdentityMap.spec.js | 162 ++ .../validation/anythingValidator.spec.js | 109 ++ .../utils/validation/booleanValidator.spec.js | 88 + .../validation/callbackValidator.spec.js | 75 + .../validation/createAnyOfValidator.spec.js | 112 ++ .../validation/createArrayOfValidator.spec.js | 78 + .../validation/createDefaultValidator.spec.js | 34 + .../createDeprecatedValidator.spec.js | 122 ++ .../validation/createLiteralValidator.spec.js | 62 + .../createMapOfValuesValidator.spec.js | 99 ++ .../validation/createMaximumValidator.spec.js | 72 + .../validation/createMinimumValidator.spec.js | 75 + .../createNoUnknownFieldsValidator.spec.js | 113 ++ .../createNonEmptyValidator.spec.js | 63 + .../createObjectOfValidator.spec.js | 145 ++ .../validation/createRenamedValidator.spec.js | 91 ++ .../createUniqueItemsValidator.spec.js | 48 + .../validation/createUniqueValidator.spec.js | 46 + .../utils/validation/domainValidator.spec.js | 38 + .../utils/validation/enumOfValidator.spec.js | 102 ++ .../utils/validation/integerValidator.spec.js | 68 + .../validation/matchesRegexpValidator.spec.js | 67 + .../utils/validation/numberValidator.spec.js | 79 + .../utils/validation/regexpValidator.spec.js | 69 + .../validation/requiredValidator.spec.js | 34 + .../utils/validation/stringValidator.spec.js | 70 + .../unit/specs/utils/validation/utils.spec.js | 83 + 356 files changed, 39001 insertions(+), 131 deletions(-) create mode 100755 scripts/testConvert.js create mode 100644 vitest.workspace.js create mode 100644 vtest/unit/constants/uuidV4Regex.js create mode 100644 vtest/unit/helpers/assertFunctionCallOrder.js create mode 100644 vtest/unit/helpers/cleanUpDomChanges.js create mode 100644 vtest/unit/helpers/createDecoratePropositionForTest.js create mode 100644 vtest/unit/helpers/createMockProposition.js create mode 100644 vtest/unit/helpers/createResponse.js create mode 100644 vtest/unit/helpers/describeRequest.js create mode 100644 vtest/unit/helpers/describeRequestPayload.js create mode 100644 vtest/unit/helpers/describeValidation.js create mode 100644 vtest/unit/helpers/flushPromiseChains.js create mode 100644 vtest/unit/helpers/pause.js create mode 100644 vtest/unit/helpers/removeAllCookies.js create mode 100644 vtest/unit/helpers/testConfigValidators.js create mode 100644 vtest/unit/specs/components/ActivityCollector/attachClickActivityCollector.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/configValidators.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/createClickActivityStorage.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/createClickedElementProperties.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/createGetClickedElementProperties.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/createInjectClickedElementProperties.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/createRecallAndInjectClickedElementProperties.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/createStorePageViewProperties.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/getLinkName.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/getLinkRegion.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/activityMapExtensionEnabled.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/createTransientStorage.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/determineLinkType.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/dom/elementHasClickHandler.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/dom/extractDomain.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/dom/findClickableElement.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/dom/isButtonSubmitElement.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/dom/isDownloadLink.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/dom/isExitLink.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/dom/isInputSubmitElement.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/dom/isSupportedAnchorElement.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/dom/isSupportedTextNode.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/hasPageName.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/isDifferentDomains.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/trimQueryFromUrl.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/truncateWhiteSpace.spec.js create mode 100644 vtest/unit/specs/components/ActivityCollector/utils/urlStartsWithScheme.spec.js create mode 100644 vtest/unit/specs/components/Audiences/injectProcessDestinations.spec.js create mode 100644 vtest/unit/specs/components/Audiences/injectProcessResponse.spec.js create mode 100644 vtest/unit/specs/components/Consent/computeConsentHash.spec.js create mode 100644 vtest/unit/specs/components/Consent/configValidators.spec.js create mode 100644 vtest/unit/specs/components/Consent/createComponent.spec.js create mode 100644 vtest/unit/specs/components/Consent/createConsentHashStore.spec.js create mode 100644 vtest/unit/specs/components/Consent/createConsentRequest.spec.js create mode 100644 vtest/unit/specs/components/Consent/createConsentRequestPayload.spec.js create mode 100644 vtest/unit/specs/components/Consent/createStoredConsent.spec.js create mode 100644 vtest/unit/specs/components/Consent/injectSendSetConsentRequest.spec.js create mode 100644 vtest/unit/specs/components/Consent/parseConsentCookie.spec.js create mode 100644 vtest/unit/specs/components/Consent/validateSetConsentOptions.spec.js create mode 100644 vtest/unit/specs/components/Context/createComponent.spec.js create mode 100644 vtest/unit/specs/components/Context/implementationDetails.spec.js create mode 100644 vtest/unit/specs/components/Context/injectDevice.spec.js create mode 100644 vtest/unit/specs/components/Context/injectEnvironment.spec.js create mode 100644 vtest/unit/specs/components/Context/injectHighEntropyUserAgentHints.spec.js create mode 100644 vtest/unit/specs/components/Context/injectPlaceContext.spec.js create mode 100644 vtest/unit/specs/components/Context/injectTimestamp.spec.js create mode 100644 vtest/unit/specs/components/Context/injectWeb.spec.js create mode 100644 vtest/unit/specs/components/DataCollector/index.spec.js create mode 100644 vtest/unit/specs/components/DataCollector/validateApplyResponse.spec.js create mode 100644 vtest/unit/specs/components/DataCollector/validateUserEventOptions.spec.js create mode 100644 vtest/unit/specs/components/EventMerge/createComponent.spec.js create mode 100644 vtest/unit/specs/components/EventMerge/createEventMergeId.spec.js create mode 100644 vtest/unit/specs/components/Identity/addEcidToPayload.spec.js create mode 100644 vtest/unit/specs/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.spec.js create mode 100644 vtest/unit/specs/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.spec.js create mode 100644 vtest/unit/specs/components/Identity/configValidators.spec.js create mode 100644 vtest/unit/specs/components/Identity/createComponent.spec.js create mode 100644 vtest/unit/specs/components/Identity/createLegacyIdentity.spec.js create mode 100644 vtest/unit/specs/components/Identity/getIdentity/createGetIdentity.spec.js create mode 100644 vtest/unit/specs/components/Identity/getIdentity/createGetIdentityOptionsValidator.spec.js create mode 100644 vtest/unit/specs/components/Identity/getIdentity/createIdentityRequest.spec.js create mode 100644 vtest/unit/specs/components/Identity/getIdentity/createIdentityRequestPayload.spec.js create mode 100644 vtest/unit/specs/components/Identity/getNamespacesFromResponse.spec.js create mode 100644 vtest/unit/specs/components/Identity/injectAddEcidQueryToPayload.spec.js create mode 100644 vtest/unit/specs/components/Identity/injectAddLegacyEcidToPayload.spec.js create mode 100644 vtest/unit/specs/components/Identity/injectAddQueryStringIdentityToPayload.spec.js create mode 100644 vtest/unit/specs/components/Identity/injectAwaitIdentityCookie.spec.js create mode 100644 vtest/unit/specs/components/Identity/injectEnsureSingleIdentity.spec.js create mode 100644 vtest/unit/specs/components/Identity/injectHandleResponseForIdSyncs.spec.js create mode 100644 vtest/unit/specs/components/Identity/injectProcessIdSyncs.spec.js create mode 100644 vtest/unit/specs/components/Identity/injectSetDomainForInitialIdentityPayload.spec.js create mode 100644 vtest/unit/specs/components/Identity/visitorService/awaitVisitorOptIn.spec.js create mode 100644 vtest/unit/specs/components/Identity/visitorService/getVisitor.spec.js create mode 100644 vtest/unit/specs/components/Identity/visitorService/injectGetEcidFromVisitor.spec.js create mode 100644 vtest/unit/specs/components/LibraryInfo/index.spec.js create mode 100644 vtest/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js create mode 100644 vtest/unit/specs/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.spec.js create mode 100644 vtest/unit/specs/components/MediaAnalyticsBridge/createMediaHelper.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createActionsProvider.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createApplyPropositions.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createClickStorage.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createComponent.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createFetchDataHandler.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createGetPageLocation.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createHandleConsentFlicker.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createInteractionStorage.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createNotificationHandler.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createOnClickHandler.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createOnDecisionHandler.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createPersonalizationDetails.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createPreprocessors.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createSetTargetMigration.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createViewCacheManager.spec.js create mode 100644 vtest/unit/specs/components/Personalization/createViewChangeHandler.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/addNonceToInlineStyleElements.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/clicks/collectInteractions.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/collectInteractions.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/createPreprocess.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/customCode.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/createFragment.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/getAttribute.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/getChildNodes.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/getChildren.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/getElementById.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/getFirstChild.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/getNextSibling.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/getNonce.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/getParent.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/helperForEq.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/insertAfter.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/removeAttribute.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/setAttribute.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/setStyle.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/dom/util.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/images.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/move.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/remapCustomCodeOffers.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/remove.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/resize.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/scripts.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/setHtml.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/setStyles.spec.js create mode 100644 vtest/unit/specs/components/Personalization/dom-actions/setText.spec.js create mode 100644 vtest/unit/specs/components/Personalization/flicker/index.spec.js create mode 100644 vtest/unit/specs/components/Personalization/handlers/createDecorateProposition.spec.js create mode 100644 vtest/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js create mode 100644 vtest/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js create mode 100644 vtest/unit/specs/components/Personalization/handlers/createProcessInAppMessage.spec.js create mode 100644 vtest/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js create mode 100644 vtest/unit/specs/components/Personalization/handlers/createProcessRedirect.spec.js create mode 100644 vtest/unit/specs/components/Personalization/handlers/injectCreateProposition.spec.js create mode 100644 vtest/unit/specs/components/Personalization/handlers/processDefaultContent.spec.js create mode 100644 vtest/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js create mode 100644 vtest/unit/specs/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.spec.js create mode 100644 vtest/unit/specs/components/Personalization/in-app-message-actions/utils.spec.js create mode 100644 vtest/unit/specs/components/Personalization/responsesMock/eventResponses.js create mode 100644 vtest/unit/specs/components/Personalization/topLevel/buildAlloy.js create mode 100644 vtest/unit/specs/components/Personalization/topLevel/buildMocks.js create mode 100644 vtest/unit/specs/components/Personalization/topLevel/cartViewDecisions.spec.js create mode 100644 vtest/unit/specs/components/Personalization/topLevel/mergedMetricDecisions.spec.js create mode 100644 vtest/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js create mode 100644 vtest/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js create mode 100644 vtest/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js create mode 100644 vtest/unit/specs/components/Personalization/topLevel/pageWideScopeDecisionsWithoutDomActionSchemaItems.spec.js create mode 100644 vtest/unit/specs/components/Personalization/topLevel/productsViewDecisions.spec.js create mode 100644 vtest/unit/specs/components/Personalization/topLevel/redirectPageWideScopeDecision.spec.js create mode 100644 vtest/unit/specs/components/Personalization/topLevel/resetMocks.js create mode 100644 vtest/unit/specs/components/Personalization/topLevel/scopesFoo1Foo2Decisions.spec.js create mode 100644 vtest/unit/specs/components/Personalization/utils/addRenderAttemptedToDecisions.spec.js create mode 100644 vtest/unit/specs/components/Personalization/utils/createAsyncArray.spec.js create mode 100644 vtest/unit/specs/components/Personalization/utils/isAuthoringModeEnabled.spec.js create mode 100644 vtest/unit/specs/components/Personalization/utils/metaUtils.spec.js create mode 100644 vtest/unit/specs/components/Personalization/utils/surfaceUtils.spec.js create mode 100644 vtest/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/contextTestUtils.js create mode 100644 vtest/unit/specs/components/RulesEngine/createApplyResponse.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/createConsequenceAdapter.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/createContextProvider.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/createDecisionHistory.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/createDecisionProvider.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/createEvaluableRulesetPayload.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/createEvaluateRulesetsCommand.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/createEventRegistry.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/createOnResponseHandler.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/createSubscribeRulesetItems.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/decisioningContext.browser.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/decisioningContext.page.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/decisioningContext.referringPage.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/decisioningContext.sdkVersion.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/decisioningContext.timestamp.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/decisioningContext.window.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/index.spec.js create mode 100644 vtest/unit/specs/components/RulesEngine/utils.spec.js create mode 100644 vtest/unit/specs/components/StreamingMedia/configValidators.spec.js create mode 100644 vtest/unit/specs/components/StreamingMedia/createMediaEventManager.spec.js create mode 100644 vtest/unit/specs/components/StreamingMedia/createMediaRequest.spec.js create mode 100644 vtest/unit/specs/components/StreamingMedia/createMediaResponseHandler.spec.js create mode 100644 vtest/unit/specs/components/StreamingMedia/createMediaSessionCacheManager.spec.js create mode 100644 vtest/unit/specs/components/StreamingMedia/createStreamingMediaComponent.spec.js create mode 100644 vtest/unit/specs/components/StreamingMedia/createTrackMediaEvent.spec.js create mode 100644 vtest/unit/specs/components/StreamingMedia/createTrackMediaSession.spec.js create mode 100644 vtest/unit/specs/components/StreamingMedia/validateMediaEventOptions.spec.js create mode 100644 vtest/unit/specs/components/StreamingMedia/validateMediaSessionOptions.spec.js create mode 100644 vtest/unit/specs/core/buildAndValidateConfig.spec.js create mode 100644 vtest/unit/specs/core/componentCreators.spec.js create mode 100644 vtest/unit/specs/core/config/createConfig.spec.js create mode 100644 vtest/unit/specs/core/config/createCoreConfigs.spec.js create mode 100644 vtest/unit/specs/core/consent/createConsent.spec.js create mode 100644 vtest/unit/specs/core/consent/createConsentStateMachine.spec.js create mode 100644 vtest/unit/specs/core/createComponentRegistry.spec.js create mode 100644 vtest/unit/specs/core/createCookieTransfer.spec.js create mode 100644 vtest/unit/specs/core/createEvent.spec.js create mode 100644 vtest/unit/specs/core/createEventManager.spec.js create mode 100644 vtest/unit/specs/core/createInstanceFunction.spec.js create mode 100644 vtest/unit/specs/core/createLifecycle.spec.js create mode 100644 vtest/unit/specs/core/createLogController.spec.js create mode 100644 vtest/unit/specs/core/createLogger.spec.js create mode 100644 vtest/unit/specs/core/edgeNetwork/handleRequestFailure.spec.js create mode 100644 vtest/unit/specs/core/edgeNetwork/injectApplyResponse.spec.js create mode 100644 vtest/unit/specs/core/edgeNetwork/injectExtractEdgeInfo.spec.js create mode 100644 vtest/unit/specs/core/edgeNetwork/injectGetLocationHint.spec.js create mode 100644 vtest/unit/specs/core/edgeNetwork/injectProcessWarningsAndErrors.spec.js create mode 100644 vtest/unit/specs/core/edgeNetwork/injectSendEdgeNetworkRequest.spec.js create mode 100644 vtest/unit/specs/core/edgeNetwork/mergeLifecycleResponses.spec.js create mode 100644 vtest/unit/specs/core/initializeComponents.spec.js create mode 100644 vtest/unit/specs/core/injectCreateResponse.spec.js create mode 100644 vtest/unit/specs/core/injectExecuteCommand.spec.js create mode 100644 vtest/unit/specs/core/injectHandleError.spec.js create mode 100644 vtest/unit/specs/core/injectShouldTransferCookie.spec.js create mode 100644 vtest/unit/specs/core/network/getRequestRetryDelay.spec.js create mode 100644 vtest/unit/specs/core/network/injectSendNetworkRequest.spec.js create mode 100644 vtest/unit/specs/core/network/isRequestRetryable.spec.js create mode 100644 vtest/unit/specs/core/network/requestMethods/injectSendBeaconRequest.spec.js create mode 100644 vtest/unit/specs/core/network/requestMethods/injectSendFetchRequest.spec.js create mode 100644 vtest/unit/specs/core/requiredComponentCreators.spec.js create mode 100644 vtest/unit/specs/core/validateCommandOptions.spec.js create mode 100644 vtest/unit/specs/utils/assignConcatArrayValues.spec.js create mode 100644 vtest/unit/specs/utils/clone.spec.js create mode 100644 vtest/unit/specs/utils/crc32.spec.js create mode 100644 vtest/unit/specs/utils/createCallbackAggregator.spec.js create mode 100644 vtest/unit/specs/utils/createCollect.spec.js create mode 100644 vtest/unit/specs/utils/createLoggingCookieJar.spec.js create mode 100644 vtest/unit/specs/utils/createMerger.spec.js create mode 100644 vtest/unit/specs/utils/createSubscription.spec.js create mode 100644 vtest/unit/specs/utils/createTaskQueue.spec.js create mode 100644 vtest/unit/specs/utils/decodeUriComponentSafely.spec.js create mode 100644 vtest/unit/specs/utils/deduplicateArray.spec.js create mode 100644 vtest/unit/specs/utils/deepAssign.spec.js create mode 100644 vtest/unit/specs/utils/defer.spec.js create mode 100644 vtest/unit/specs/utils/dom/appendNode.spec.js create mode 100644 vtest/unit/specs/utils/dom/awaitSelector.spec.js create mode 100644 vtest/unit/specs/utils/dom/createNode.spec.js create mode 100644 vtest/unit/specs/utils/dom/isShadowSelector.spec.js create mode 100644 vtest/unit/specs/utils/dom/matchesSelector.spec.js create mode 100644 vtest/unit/specs/utils/dom/querySelectorAll.spec.js create mode 100644 vtest/unit/specs/utils/dom/removeNode.spec.js create mode 100644 vtest/unit/specs/utils/dom/selectNodes.spec.js create mode 100644 vtest/unit/specs/utils/dom/selectNodesWithShadow.spec.js create mode 100644 vtest/unit/specs/utils/event.spec.js create mode 100644 vtest/unit/specs/utils/filterObject.spec.js create mode 100644 vtest/unit/specs/utils/flattenArray.spec.js create mode 100644 vtest/unit/specs/utils/flattenObject.spec.js create mode 100644 vtest/unit/specs/utils/getApexDomain.spec.js create mode 100644 vtest/unit/specs/utils/getLastArrayItems.spec.js create mode 100644 vtest/unit/specs/utils/getNamespacedCookieName.spec.js create mode 100644 vtest/unit/specs/utils/getNamespacedStorage.spec.js create mode 100644 vtest/unit/specs/utils/groupBy.spec.js create mode 100644 vtest/unit/specs/utils/injectAreThirdPartyCookiesSupportedByDefault.spec.js create mode 100644 vtest/unit/specs/utils/injectDoesIdentityCookieExist.spec.js create mode 100644 vtest/unit/specs/utils/injectFireReferrerHideableImage.spec.js create mode 100644 vtest/unit/specs/utils/injectGetBrowser.spec.js create mode 100644 vtest/unit/specs/utils/injectStorage.spec.js create mode 100644 vtest/unit/specs/utils/intersection.spec.js create mode 100644 vtest/unit/specs/utils/isBlankString.spec.js create mode 100644 vtest/unit/specs/utils/isBoolean.spec.js create mode 100644 vtest/unit/specs/utils/isEmptyObject.spec.js create mode 100644 vtest/unit/specs/utils/isFunction.spec.js create mode 100644 vtest/unit/specs/utils/isInteger.spec.js create mode 100644 vtest/unit/specs/utils/isNamespacedCookieName.spec.js create mode 100644 vtest/unit/specs/utils/isNil.spec.js create mode 100644 vtest/unit/specs/utils/isNonEmptyArray.spec.js create mode 100644 vtest/unit/specs/utils/isNonEmptyString.spec.js create mode 100644 vtest/unit/specs/utils/isNumber.spec.js create mode 100644 vtest/unit/specs/utils/isObject.spec.js create mode 100644 vtest/unit/specs/utils/isString.spec.js create mode 100644 vtest/unit/specs/utils/isUnique.spec.js create mode 100644 vtest/unit/specs/utils/isValidRegExp.spec.js create mode 100644 vtest/unit/specs/utils/lazy.spec.js create mode 100644 vtest/unit/specs/utils/networkErrors.spec.js create mode 100644 vtest/unit/specs/utils/noop.spec.js create mode 100644 vtest/unit/specs/utils/parseUrl.spec.js create mode 100644 vtest/unit/specs/utils/prepareConfigOverridesForEdge.spec.js create mode 100644 vtest/unit/specs/utils/request/createAddIdentity.spec.js create mode 100644 vtest/unit/specs/utils/request/createDataCollectionRequest.spec.js create mode 100644 vtest/unit/specs/utils/request/createDataCollectionRequestPayload.spec.js create mode 100644 vtest/unit/specs/utils/request/createGetAssuranceValidationTokenParams.spec.js create mode 100644 vtest/unit/specs/utils/request/createHasIdentity.spec.js create mode 100644 vtest/unit/specs/utils/request/createRequestParams.spec.js create mode 100644 vtest/unit/specs/utils/sanitizeOrgIdForCookieName.spec.js create mode 100644 vtest/unit/specs/utils/stackError.spec.js create mode 100644 vtest/unit/specs/utils/stringToBoolean.spec.js create mode 100644 vtest/unit/specs/utils/toArray.spec.js create mode 100644 vtest/unit/specs/utils/toError.spec.js create mode 100644 vtest/unit/specs/utils/toISOStringLocal.spec.js create mode 100644 vtest/unit/specs/utils/toInteger.spec.js create mode 100644 vtest/unit/specs/utils/updateErrorMessage.spec.js create mode 100644 vtest/unit/specs/utils/validateConfigOverride.spec.js create mode 100644 vtest/unit/specs/utils/validateIdentityMap.spec.js create mode 100644 vtest/unit/specs/utils/validation/anythingValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/booleanValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/callbackValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createAnyOfValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createArrayOfValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createDefaultValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createDeprecatedValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createLiteralValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createMapOfValuesValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createMaximumValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createMinimumValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createNoUnknownFieldsValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createNonEmptyValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createObjectOfValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createRenamedValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createUniqueItemsValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/createUniqueValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/domainValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/enumOfValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/integerValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/matchesRegexpValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/numberValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/regexpValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/requiredValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/stringValidator.spec.js create mode 100644 vtest/unit/specs/utils/validation/utils.spec.js diff --git a/package-lock.json b/package-lock.json index 5efe13259..6ce63c27f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "@babel/plugin-transform-runtime": "^7.25.9", "@eslint/js": "^9.14.0", "@octokit/rest": "^21.0.2", + "@vitest/browser": "^2.1.8", "bundlesize": "^0.18.2", "chai": "^5.1.2", "chalk": "^5.3.0", @@ -60,6 +61,7 @@ "husky": "^9.1.6", "jasmine": "^5.4.0", "jasmine-core": "^5.4.0", + "jsdom": "^25.0.1", "karma": "^6.4.4", "karma-chrome-launcher": "^3.2.0", "karma-coverage": "^2.2.1", @@ -73,6 +75,7 @@ "karma-sauce-launcher": "^4.3.6", "karma-spec-reporter": "0.0.36", "lint-staged": "^15.2.10", + "playwright": "^1.49.1", "prettier": "^3.3.3", "read-cache": "^1.0.0", "recursive-readdir": "^2.2.3", @@ -2045,6 +2048,83 @@ "node": ">=6.9.0" } }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz", + "integrity": "sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cookie": "^0.7.2" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "license": "ISC", + "dependencies": { + "statuses": "^2.0.1" + } + }, + "node_modules/@bundled-es-modules/statuses/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@bundled-es-modules/tough-cookie": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz", + "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@types/tough-cookie": "^4.0.5", + "tough-cookie": "^4.1.4" + } + }, + "node_modules/@bundled-es-modules/tough-cookie/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/@bundled-es-modules/tough-cookie/node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@bundled-es-modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -3295,6 +3375,24 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mswjs/interceptors": { + "version": "0.37.3", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.37.3.tgz", + "integrity": "sha512-USvgCL/uOGFtVa6SVyRrC8kIAedzRohxIXN5LISlg5C5vLZCn7dgMFVSNhSF9cuBEFrm/O2spDWEZeMnw4ZXYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@nicolo-ribaudo/chokidar-2": { "version": "2.1.8-no-fsevents.3", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", @@ -3512,6 +3610,31 @@ "@octokit/openapi-types": "^22.2.0" } }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true, + "license": "MIT" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -3536,6 +3659,13 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "dev": true, + "license": "MIT" + }, "node_modules/@promptbook/utils": { "version": "0.58.0", "resolved": "https://registry.npmjs.org/@promptbook/utils/-/utils-0.58.0.tgz", @@ -4115,6 +4245,106 @@ "node": ">=10" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.5.2", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", + "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@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", @@ -4122,6 +4352,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -4279,6 +4516,20 @@ "@types/node": "*" } }, + "node_modules/@types/statuses": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", + "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", @@ -4307,21 +4558,264 @@ "@types/node": "*" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "license": "ISC" - }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "license": "ISC" + }, + "node_modules/@vitest/browser": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-2.1.8.tgz", + "integrity": "sha512-OWVvEJThRgxlNMYNVLEK/9qVkpRcLvyuKLngIV3Hob01P56NjPHprVBYn+rx4xAJudbM9yrCrywPIEuA3Xyo8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@testing-library/dom": "^10.4.0", + "@testing-library/user-event": "^14.5.2", + "@vitest/mocker": "2.1.8", + "@vitest/utils": "2.1.8", + "magic-string": "^0.30.12", + "msw": "^2.6.4", + "sirv": "^3.0.0", + "tinyrainbow": "^1.2.0", + "ws": "^8.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "playwright": "*", + "vitest": "2.1.8", + "webdriverio": "*" + }, + "peerDependenciesMeta": { + "playwright": { + "optional": true + }, + "safaridriver": { + "optional": true + }, + "webdriverio": { + "optional": true + } + } + }, + "node_modules/@vitest/browser/node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/browser/node_modules/@vitest/mocker": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", + "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.8", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/browser/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vitest/browser/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@vitest/browser/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@vitest/browser/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/browser/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/browser/node_modules/msw": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.6.8.tgz", + "integrity": "sha512-nxXxnH6WALZ9a7rsQp4HU2AaD4iGAiouMmE/MY4al7pXTibgA6OZOuKhmN2WBIM6w9qMKwRtX8p2iOb45B2M/Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.1", + "@bundled-es-modules/statuses": "^1.0.1", + "@bundled-es-modules/tough-cookie": "^0.1.6", + "@inquirer/confirm": "^5.0.0", + "@mswjs/interceptors": "^0.37.0", + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "chalk": "^4.1.2", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "path-to-regexp": "^6.3.0", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.26.1", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.8.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vitest/browser/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@vitest/browser/node_modules/type-fest": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.30.1.tgz", + "integrity": "sha512-ojFL7eDMX2NF0xMbDwPZJ8sb7ckqtlAi1GsmgsFXvErT9kFTk1r0DuQKvrCh73M6D4nngeHJmvogF9OluXs7Hw==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/browser/node_modules/typescript": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@vitest/browser/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "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/@vitest/expect": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.4.tgz", - "integrity": "sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", + "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.4", - "@vitest/utils": "2.1.4", + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", "chai": "^5.1.2", "tinyrainbow": "^1.2.0" }, @@ -4330,9 +4824,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz", - "integrity": "sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", + "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4343,13 +4837,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.4.tgz", - "integrity": "sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz", + "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.4", + "@vitest/utils": "2.1.8", "pathe": "^1.1.2" }, "funding": { @@ -4357,13 +4851,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.4.tgz", - "integrity": "sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz", + "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.4", + "@vitest/pretty-format": "2.1.8", "magic-string": "^0.30.12", "pathe": "^1.1.2" }, @@ -4372,9 +4866,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.4.tgz", - "integrity": "sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz", + "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==", "dev": true, "license": "MIT", "dependencies": { @@ -4385,13 +4879,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz", - "integrity": "sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz", + "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.4", + "@vitest/pretty-format": "2.1.8", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" }, @@ -7611,6 +8105,19 @@ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", "license": "MIT" }, + "node_modules/cssstyle": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", + "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "rrweb-cssom": "^0.7.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", @@ -7641,6 +8148,77 @@ "node": ">= 14" } }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/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/data-urls/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/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/data-urls/node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/data-view-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", @@ -7759,6 +8337,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true, + "license": "MIT" + }, "node_modules/decode-uri-component": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", @@ -8360,6 +8945,13 @@ "node": ">=6.0.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, "node_modules/dom-serialize": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", @@ -9023,6 +9615,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", @@ -11178,6 +11777,16 @@ "lodash": "^4.17.15" } }, + "node_modules/graphql": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", + "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, "node_modules/gzip-size": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-4.1.0.tgz", @@ -11402,6 +12011,13 @@ "tslib": "^2.0.3" } }, + "node_modules/headers-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/highlight-es": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/highlight-es/-/highlight-es-1.0.3.tgz", @@ -11482,6 +12098,19 @@ "node": ">=4" } }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -12138,6 +12767,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "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", @@ -12242,6 +12878,13 @@ "node": ">=8" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-reference": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", @@ -12661,90 +13304,260 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "ISC", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jasmine/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/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/jsdom/node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/jsdom/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/jsdom/node_modules/tough-cookie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "brace-expansion": "^2.0.1" + "tldts": "^6.1.32" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=16" } }, - "node_modules/jasmine/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "node_modules/jsdom/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "punycode": "^2.3.1" }, "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" } }, - "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "node_modules/jsdom/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-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" } }, - "node_modules/js-cookie": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", - "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "node_modules/jsdom/node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, "license": "MIT", "engines": { - "node": ">=14" + "node": ">=18" } }, - "node_modules/js-md4": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", - "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=18" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "node_modules/jsdom/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, - "license": "MIT" + "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/jsesc": { "version": "3.0.2", @@ -13829,6 +14642,16 @@ "yallist": "^3.0.2" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/macos-release": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.2.0.tgz", @@ -14118,6 +14941,16 @@ "dev": true, "license": "MIT" }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -14385,6 +15218,13 @@ "node": ">=0.10.0" } }, + "node_modules/nwsapi": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", + "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -14598,6 +15438,13 @@ "node": ">=0.10.0" } }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true, + "license": "MIT" + }, "node_modules/p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -14960,6 +15807,13 @@ "node": "20 || >=22" } }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -15157,6 +16011,53 @@ "node": ">=4" } }, + "node_modules/playwright": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", + "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.49.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", + "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/pngjs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", @@ -15178,9 +16079,9 @@ } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, "funding": [ { @@ -15199,7 +16100,7 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -15284,6 +16185,34 @@ "node": ">=6.0.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -15772,6 +16701,13 @@ "node": ">=0.10.0" } }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -16400,6 +17336,13 @@ "dev": true, "license": "MIT" }, + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true, + "license": "MIT" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -16716,6 +17659,19 @@ "node": ">=10" } }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/seek-bzip": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", @@ -17030,6 +17986,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sirv": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz", + "integrity": "sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -17541,9 +18512,9 @@ } }, "node_modules/std-env": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", - "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", "dev": true, "license": "MIT" }, @@ -17587,6 +18558,13 @@ "bare-events": "^2.2.0" } }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true, + "license": "MIT" + }, "node_modules/strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", @@ -17853,6 +18831,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/synckit": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", @@ -19308,6 +20293,26 @@ "node": ">=14.0.0" } }, + "node_modules/tldts": { + "version": "6.1.66", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.66.tgz", + "integrity": "sha512-l3ciXsYFel/jSRfESbyKYud1nOw7WfhrBEF9I3UiarYk/qEaOOwu3qXNECHw4fHGHGTEOuhf/VdKgoDX5M/dhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.66" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.66", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.66.tgz", + "integrity": "sha512-s07jJruSwndD2X8bVjwioPfqpIc1pDTzszPe9pL1Skbh4bjytL85KNQ3tolqLbCvpQHawIsGfFi9dgerWjqW4g==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -19350,6 +20355,16 @@ "node": ">=0.6" } }, + "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", + "engines": { + "node": ">=6" + } + }, "node_modules/tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", @@ -19975,9 +20990,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "5.4.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", - "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -20035,14 +21050,15 @@ } }, "node_modules/vite-node": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.4.tgz", - "integrity": "sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz", + "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", "pathe": "^1.1.2", "vite": "^5.0.0" }, @@ -20057,31 +21073,31 @@ } }, "node_modules/vitest": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.4.tgz", - "integrity": "sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", + "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "2.1.4", - "@vitest/mocker": "2.1.4", - "@vitest/pretty-format": "^2.1.4", - "@vitest/runner": "2.1.4", - "@vitest/snapshot": "2.1.4", - "@vitest/spy": "2.1.4", - "@vitest/utils": "2.1.4", + "@vitest/expect": "2.1.8", + "@vitest/mocker": "2.1.8", + "@vitest/pretty-format": "^2.1.8", + "@vitest/runner": "2.1.8", + "@vitest/snapshot": "2.1.8", + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", "chai": "^5.1.2", "debug": "^4.3.7", "expect-type": "^1.1.0", "magic-string": "^0.30.12", "pathe": "^1.1.2", - "std-env": "^3.7.0", + "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.1", "tinypool": "^1.0.1", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.4", + "vite-node": "2.1.8", "why-is-node-running": "^2.3.0" }, "bin": { @@ -20096,8 +21112,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.4", - "@vitest/ui": "2.1.4", + "@vitest/browser": "2.1.8", + "@vitest/ui": "2.1.8", "happy-dom": "*", "jsdom": "*" }, @@ -20123,13 +21139,13 @@ } }, "node_modules/vitest/node_modules/@vitest/mocker": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.4.tgz", - "integrity": "sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", + "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.4", + "@vitest/spy": "2.1.8", "estree-walker": "^3.0.3", "magic-string": "^0.30.12" }, @@ -20169,6 +21185,19 @@ "node": ">=0.10.0" } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/wait-on": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.1.tgz", @@ -20944,6 +21973,32 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/whatwg-mimetype": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", @@ -21390,6 +22445,16 @@ } } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, "node_modules/xmlbuilder": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-12.0.0.tgz", @@ -21400,6 +22465,13 @@ "node": ">=6.0" } }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 02db3e3d6..49c1055bf 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ }, "scripts": { "clean": "rimraf dist distTest libEs5 libEs6", - "lint": "eslint --cache --fix \"*.{js,cjs,mjs,jsx}\" \"{src,test,scripts}/**/*.{js,cjs,mjs,jsx}\"", + "lint": "eslint --cache --fix \"*.{js,cjs,mjs,jsx}\" \"{src,test,vtest,scripts}/**/*.{js,cjs,mjs,jsx}\"", "format": "prettier --write \"*.{html,js,cjs,mjs,jsx}\" \"{sandbox,src,test,scripts,vtest}/**/*.{html,js,cjs,mjs,jsx}\"", "test": "npm run test:unit && npm run test:scripts && npm run test:functional", "test:unit": "karma start karma.conf.cjs --single-run", @@ -99,6 +99,7 @@ "@babel/plugin-transform-runtime": "^7.25.9", "@eslint/js": "^9.14.0", "@octokit/rest": "^21.0.2", + "@vitest/browser": "^2.1.8", "bundlesize": "^0.18.2", "chai": "^5.1.2", "chalk": "^5.3.0", @@ -119,6 +120,7 @@ "husky": "^9.1.6", "jasmine": "^5.4.0", "jasmine-core": "^5.4.0", + "jsdom": "^25.0.1", "karma": "^6.4.4", "karma-chrome-launcher": "^3.2.0", "karma-coverage": "^2.2.1", @@ -132,6 +134,7 @@ "karma-sauce-launcher": "^4.3.6", "karma-spec-reporter": "0.0.36", "lint-staged": "^15.2.10", + "playwright": "^1.49.1", "prettier": "^3.3.3", "read-cache": "^1.0.0", "recursive-readdir": "^2.2.3", diff --git a/scripts/testConvert.js b/scripts/testConvert.js new file mode 100755 index 000000000..066e377bd --- /dev/null +++ b/scripts/testConvert.js @@ -0,0 +1,367 @@ +#!/usr/bin/env node + +import { execSync as exec } from "child_process"; +import fs from "fs"; +import path from "path"; +import babel from "@babel/core"; + +const getFilesRecursively = (directory, files) => { + const filesInDirectory = fs.readdirSync(directory); + for (let i = 0; i < filesInDirectory.length; i += 1) { + const file = filesInDirectory[i]; + const absolute = path.join(directory, file); + if (fs.statSync(absolute).isDirectory()) { + getFilesRecursively(absolute, files); + } else { + files.push(absolute); + } + } +}; + +const addTopImport = (output) => { + let optionalImports = [ + "beforeEach", + "afterEach", + "afterAll", + "describe", + "it", + "expect", + ]; + const requiredImports = ["vi"]; + + let importIndex = output.indexOf("import") - 1; + + if (importIndex < -1) { + importIndex = output.indexOf("License.\n*/") + 11; + } + + optionalImports = optionalImports.filter((i) => output.indexOf(i) !== -1); + + output = `${output.slice(0, importIndex)}\nimport { ${requiredImports.concat(optionalImports.join(", "))} } from 'vitest';\n${output.slice(importIndex + 1)}`; + return output; +}; + +const start = 0; +const end = 400; + +exec("mkdir -p ./vtest/unit"); +// exec("rm -rf ./vtest/unit/*"); +// +if (!fs.existsSync("./vtest/unit/constants")) { + exec("cp -r ./test/unit/constants ./vtest/unit/constants"); +} + +if (!fs.existsSync("./vtest/unit/helpers")) { + exec("cp -r ./test/unit/helpers ./vtest/unit/helpers"); +} +exec("mkdir -p ./vtest/unit/specs"); + +const t = babel.types; +const f = []; +getFilesRecursively("./vtest/unit/helpers", f); +getFilesRecursively("./test/unit/specs", f); + +f.sort((a, b) => { + const z = [ + "responsesMock/eventResponses.js", + "resetMocks.js", + "functional/helpers/createResponse.js", + ]; + + for (let i = 0; i < z.length; i += 1) { + if (a.includes(z[i])) { + return -1; + } + + if (b.includes(z[i])) { + return 1; + } + } + + return 0; +}); + +for (let fileIndex = start; fileIndex < f.length; fileIndex += 1) { + const filePath = f[fileIndex]; + const newDir = path.dirname(filePath).replace(/^test\/unit/, "vtest/unit"); + const newFilePath = path.join(newDir, path.basename(filePath)); + + if (!fs.existsSync(newFilePath)) { + let output = babel.transformFileSync(filePath, { + plugins: [ + { + visitor: { + Identifier(babelPath) { + if (babelPath.node.name === "callFake") { + // callFake => mockImplementation + babelPath.replaceWith(t.identifier("mockImplementation")); + } else if (babelPath.node.name === "and") { + if (babelPath.parentPath.node.type === "MemberExpression") { + babelPath.parentPath.replaceWith( + babelPath.parentPath.get("object"), + ); + } + } else if (babelPath.node.name === "returnValue") { + babelPath.replaceWith(t.identifier("mockReturnValue")); + } else if ( + ["toBeTrue", "toBeFalse"].includes(babelPath.node.name) + ) { + const booleanValue = babelPath.node.name === "toBeTrue"; + babelPath.replaceWith(t.identifier("toBe")); + + let v = babelPath; + while (v.parentPath.type !== "ExpressionStatement") { + v = v.parentPath; + } + + v.replaceWith( + t.callExpression(v.get("callee").node, [ + t.booleanLiteral(booleanValue), + ]), + ); + + v.stop(); + } else if (babelPath.node.name === "expectAsync") { + const v = babelPath.findParent( + (p) => p.type === "ReturnStatement", + ); + + if (!v) { + return; + } + + let command = "resolves.toBe"; + if ( + v + .get("argument.arguments") + .map((n) => n.node.type) + .includes("ObjectExpression") + ) { + command = "resolves.toStrictEqual"; + } + + if ( + ["toBeRejectedWithError", "toBeRejected"].includes( + v.get("argument.callee.property").node.name, + ) + ) { + command = "rejects.toThrowError"; + } + + if (v.get("argument.callee.property").node.name !== "then") { + const vPath = v.get("argument.callee.property"); + vPath.replaceWith(t.identifier(`${command}`)); + vPath.stop(); + } + + babelPath.replaceWith(t.identifier("expect")); + } else if (babelPath.node.name === "toThrowMatching") { + const parent = babelPath.findParent( + (p) => p.type === "CallExpression", + ); + const regExp = parent.get( + "arguments.0.body.body.0.argument.callee.object", + ); + parent + .get("callee.property") + .replaceWith(t.identifier("toThrowError")); + parent.get("arguments.0").replaceWith(regExp); + } else if (babelPath.node.name === "returnValues") { + const callExpression = babelPath.findParent((p) => + p.isCallExpression(), + ); + + const args = callExpression + .get("arguments") + .map((n) => n.node.name); + + callExpression.replaceWithSourceString( + `${callExpression.get("callee.object").node.name}.${args.map((v) => `mockReturnValueOnce(${v})`).join(".")}`, + ); + } else if (babelPath.node.name === "toHaveBeenCalledOnceWith") { + const callExpression = babelPath + .findParent((p) => p.isExpressionStatement()) + .get("expression"); + + const args = callExpression.get("arguments").map((a) => a.node); + + babelPath.replaceWith(t.identifier("toHaveBeenNthCalledWith")); + + callExpression.replaceWith( + t.callExpression(callExpression.get("callee").node, [ + t.identifier("1"), + ...args, + ]), + ); + } else if (babelPath.node.name === "toBeResolvedTo") { + babelPath.replaceWith(t.identifier("resolves.toStrictEqual")); + } else if (babelPath.node.name === "callThrough") { + const parentMemberExpression = babelPath.findParent((p) => + p.isMemberExpression(), + ); + const parentCallExpression = parentMemberExpression.findParent( + (pp) => pp.isCallExpression(), + ); + const object = parentMemberExpression.get("object"); + parentCallExpression.replaceWith(object); + } else if (babelPath.node.name === "calls") { + const parentPath = babelPath.parentPath.parentPath; + const callExpressionPath = babelPath.findParent((p) => + p.isCallExpression(), + ); + if (callExpressionPath) { + const pathToKeep = callExpressionPath.get("callee"); + const args = callExpressionPath.get("arguments"); + if (parentPath.isMemberExpression()) { + babelPath.parentPath.parentPath.replaceWith( + babelPath.parentPath, + ); + callExpressionPath.replaceWith(pathToKeep); + + babelPath.replaceWith( + t.identifier(`mock.calls[${args[0]}]`), + ); + } + } + } + }, + + MemberExpression(babelPath) { + if ( + babelPath.node.object.name === "jasmine" && + babelPath.node.property.name === "createSpy" + ) { + babelPath.parentPath.replaceWith( + t.callExpression( + t.memberExpression(t.identifier("vi"), t.identifier("fn")), + [], + ), + ); + } else if ( + babelPath.node.object.name === "jasmine" && + [ + "stringMatching", + "any", + "objectContaining", + "stringContaining", + "arrayContaining", + ].includes(babelPath.node.property.name) + ) { + babelPath.replaceWith( + t.memberExpression( + t.identifier("expect"), + babelPath.node.property, + ), + ); + } + }, + + CallExpression(babelPath) { + if ( + babelPath.node.callee.type === "MemberExpression" && + babelPath.node.callee.object.name === "jasmine" && + babelPath.node.callee.property.name === "createSpyObj" + ) { + // jasmine.createSpyObj => object with mocks + const args = babelPath.get("arguments"); + let newProps = []; + + for (let a = 0; a < args.length; a += 1) { + const obj = args[a]; + + if (obj && obj.type === "ObjectExpression") { + const objectProperties = obj.get("properties"); + + for (let i = 0; i < objectProperties.length; i += 1) { + const v = objectProperties[i].get("value"); + const n = v.node; + + newProps.push( + t.objectProperty( + t.identifier( + objectProperties[i].get("key").node.name, + ), + t.callExpression( + t.memberExpression( + t.callExpression( + t.memberExpression( + t.identifier("vi"), + t.identifier("fn"), + ), + [], + ), + t.identifier("mockReturnValue"), + ), + [n], + ), + ), + ); + } + } else if (obj && obj.type === "ArrayExpression") { + try { + // obj.node.elements + newProps = newProps.concat( + obj.node.elements.map((element) => + t.objectProperty( + t.identifier(element.value), + t.identifier("vi.fn()"), + ), + ), + ); + } catch (e) { + // sss + } + } + } + + if (newProps.length) { + babelPath.replaceWith(t.objectExpression(newProps)); + } + } else if ( + babelPath.node.callee.type === "Identifier" && + babelPath.node.callee.name === "spyOn" + ) { + // spyOn => vi.spyOn + const v = babelPath.get("callee"); + v.replaceWith( + t.memberExpression(t.identifier("vi"), t.identifier("spyOn")), + ); + } else if (babelPath.get("callee").node.name === "done") { + babelPath.remove(); + } + }, + }, + }, + ], + }).code; + + output = addTopImport(output); + exec(`mkdir -p ${newDir}`); + if ( + ![ + "vtest/unit/specs/components/Personalization/dom-actions/action.spec.js", + "vtest/unit/specs/components/Personalization/dom-actions/dom/insertBefore.spec.js", + "vtest/unit/specs/components/Personalization/dom-actions/swapImage.spec.js", + "vtest/unit/specs/components/Personalization/dom-actions/remapHeadOffers.spec.js", + "vtest/unit/specs/components/RulesEngine/constants.spec.js", + "vtest/unit/specs/karmaEntry.spec.cjs", + "vtest/unit/specs/utils/cookieJar.spec.js", + "vtest/unit/specs/utils/fireImage.spec.js", + "vtest/unit/specs/utils/querystring.spec.js", + "vtest/unit/specs/utils/request/createRequest.spec.js", + "vtest/unit/specs/utils/request/createRequestPayload.spec.js", + "vtest/unit/specs/utils/uuid.spec.js", + ].includes(newFilePath) + ) { + exec(`touch ${newFilePath}`); + fs.writeFileSync(newFilePath, output); + console.log(`Converted ${newFilePath}`, fileIndex); + } + } else { + console.log(`Skipping ${newFilePath}`, fileIndex); + } + + if (fileIndex === end) { + break; + } +} diff --git a/src/components/Personalization/dom-actions/dom/createFragment.js b/src/components/Personalization/dom-actions/dom/createFragment.js index dc9f8a8f9..8ef84cb17 100644 --- a/src/components/Personalization/dom-actions/dom/createFragment.js +++ b/src/components/Personalization/dom-actions/dom/createFragment.js @@ -13,6 +13,6 @@ governing permissions and limitations under the License. import { createNode } from "../../../../utils/dom/index.js"; import { DIV } from "../../../../constants/tagName.js"; -export default (content) => { +export default (content = "undefined") => { return createNode(DIV, {}, { innerHTML: content }); }; diff --git a/src/components/Personalization/dom-actions/dom/util.js b/src/components/Personalization/dom-actions/dom/util.js index 223ef787a..33b180ee2 100644 --- a/src/components/Personalization/dom-actions/dom/util.js +++ b/src/components/Personalization/dom-actions/dom/util.js @@ -11,7 +11,7 @@ governing permissions and limitations under the License. */ export const addPxIfMissing = (value) => { - const hasPx = ("" + value).endsWith("px"); + const hasPx = `${value}`.endsWith("px"); return hasPx ? value : `${value}px`; }; diff --git a/src/components/StreamingMedia/createTrackMediaSession.js b/src/components/StreamingMedia/createTrackMediaSession.js index 22f9f713c..f82aca490 100644 --- a/src/components/StreamingMedia/createTrackMediaSession.js +++ b/src/components/StreamingMedia/createTrackMediaSession.js @@ -38,7 +38,7 @@ export default ({ getPlayerDetails, legacy, }, - edgeConfigOverrides + edgeConfigOverrides, }); mediaSessionCacheManager.storeSession({ diff --git a/src/components/StreamingMedia/validateMediaSessionOptions.js b/src/components/StreamingMedia/validateMediaSessionOptions.js index 08f61b3fd..89c608d87 100644 --- a/src/components/StreamingMedia/validateMediaSessionOptions.js +++ b/src/components/StreamingMedia/validateMediaSessionOptions.js @@ -30,7 +30,7 @@ export default ({ options }) => { sessionDetails: objectOf(anything()).required(), }), }), - edgeConfigOverrides: objectOf({}) + edgeConfigOverrides: objectOf({}), }).required(), objectOf({ @@ -40,7 +40,7 @@ export default ({ options }) => { sessionDetails: objectOf(anything()).required(), }), }), - edgeConfigOverrides: objectOf({}) + edgeConfigOverrides: objectOf({}), }).required(), ], diff --git a/src/utils/request/createRequestParams.js b/src/utils/request/createRequestParams.js index 6c091a9e3..aed49132d 100644 --- a/src/utils/request/createRequestParams.js +++ b/src/utils/request/createRequestParams.js @@ -9,7 +9,7 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import {isEmptyObject} from "../index.js"; +import { isEmptyObject } from "../index.js"; /** * @typedef {{ datastreamId: string, [k: string]: Object }} Override @@ -33,7 +33,10 @@ export default ({ localConfigOverrides, globalConfigOverrides, payload }) => { payload.mergeConfigOverride(globalConfigOverrides); } - if (localConfigOverridesWithoutDatastreamId && !isEmptyObject(localConfigOverridesWithoutDatastreamId)) { + if ( + localConfigOverridesWithoutDatastreamId && + !isEmptyObject(localConfigOverridesWithoutDatastreamId) + ) { payload.mergeConfigOverride(localConfigOverridesWithoutDatastreamId); } return requestParams; diff --git a/test/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js b/test/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js index 26e268788..cb75b2ff3 100644 --- a/test/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js +++ b/test/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js @@ -9,7 +9,7 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const createGetInstance = require("../../../../../src/components/MediaAnalyticsBridge/createGetInstance.js"); +import createGetInstance from "../../../../../src/components/MediaAnalyticsBridge/createGetInstance.js"; describe("createGetInstance", () => { const logger = { diff --git a/test/unit/specs/utils/validation/utils.spec.js b/test/unit/specs/utils/validation/utils.spec.js index 70cd3b591..02122c6b1 100644 --- a/test/unit/specs/utils/validation/utils.spec.js +++ b/test/unit/specs/utils/validation/utils.spec.js @@ -73,10 +73,9 @@ describe("validation::utils", () => { expect(() => assertValid(false, "myValue", "myPath", "myMessage"), ).toThrowMatching((e) => { - expect(e.message).toEqual( - `'myPath': Expected myMessage, but got "myValue".`, + return /'myPath': Expected myMessage, but got "myValue"\./.test( + e.message, ); - return true; }); }); diff --git a/vite.config.js b/vite.config.js index bce6eb2bc..b1955dd21 100644 --- a/vite.config.js +++ b/vite.config.js @@ -3,7 +3,10 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { + name: "happy-dom", include: ["vtest/**/*.{test,spec}.?(c|m)[jt]s?(x)"], environment: "happy-dom", + isolate: false, + pool: "threads", }, }); diff --git a/vitest.workspace.js b/vitest.workspace.js new file mode 100644 index 000000000..95eb35ff3 --- /dev/null +++ b/vitest.workspace.js @@ -0,0 +1,36 @@ +// eslint-disable-next-line import/no-unresolved +import { defineWorkspace } from "vitest/config"; + +const filesForBrowser = [ + "vtest/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js", + "vtest/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js", + "vtest/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js", + "vtest/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js", + "vtest/unit/specs/utils/dom/selectNodesWithShadow.spec.js", + "vtest/unit/specs/utils/updateErrorMessage.spec.js", +]; + +// defineWorkspace provides a nice type hinting DX +export default defineWorkspace([ + { + // add "extends" to merge two configs together + extends: "./vite.config.js", + test: { + exclude: filesForBrowser, + }, + }, + { + test: { + name: "browser", + include: filesForBrowser, + isolate: false, + pool: "threads", + browser: { + name: "chromium", + enabled: true, + headless: true, + provider: "playwright", + }, + }, + }, +]); diff --git a/vtest/unit/constants/uuidV4Regex.js b/vtest/unit/constants/uuidV4Regex.js new file mode 100644 index 000000000..bf9c0bd8b --- /dev/null +++ b/vtest/unit/constants/uuidV4Regex.js @@ -0,0 +1,13 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +export default /^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i; diff --git a/vtest/unit/helpers/assertFunctionCallOrder.js b/vtest/unit/helpers/assertFunctionCallOrder.js new file mode 100644 index 000000000..84cd323c3 --- /dev/null +++ b/vtest/unit/helpers/assertFunctionCallOrder.js @@ -0,0 +1,25 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { expect } from "vitest"; + +/** + * Asserts that functions were called in a particular order. + * @param {Array} orderedFunctions The array of functions in the order + * they should have been called. + */ +export default (orderedFunctions) => { + for (let i = 0; i < orderedFunctions.length - 1; i += 1) { + const callOrder1 = orderedFunctions[i].mock.invocationCallOrder[0]; + const callOrder2 = orderedFunctions[i + 1].mock.invocationCallOrder[0]; + expect(callOrder1).toBeLessThan(callOrder2); + } +}; diff --git a/vtest/unit/helpers/cleanUpDomChanges.js b/vtest/unit/helpers/cleanUpDomChanges.js new file mode 100644 index 000000000..2413a6725 --- /dev/null +++ b/vtest/unit/helpers/cleanUpDomChanges.js @@ -0,0 +1,25 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { selectNodes, removeNode } from "../../../src/utils/dom/index.js"; + +/** + * Removes container DOM nodes used for all the + * personalization actions + */ +export default (id) => { + selectNodes(`#${id}`).forEach(removeNode); + selectNodes("style").forEach((node) => { + if (node.textContent.indexOf(id) !== -1) { + removeNode(node); + } + }); +}; diff --git a/vtest/unit/helpers/createDecoratePropositionForTest.js b/vtest/unit/helpers/createDecoratePropositionForTest.js new file mode 100644 index 000000000..418baa23a --- /dev/null +++ b/vtest/unit/helpers/createDecoratePropositionForTest.js @@ -0,0 +1,54 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { + ADOBE_JOURNEY_OPTIMIZER, + ADOBE_TARGET, +} from "../../../src/constants/decisionProvider.js"; +import createInteractionStorage from "../../../src/components/Personalization/createInteractionStorage.js"; +import createDecorateProposition from "../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { + ALWAYS, + NEVER, +} from "../../../src/constants/propositionInteractionType.js"; + +export default ({ + autoCollectPropositionInteractions = { + [ADOBE_JOURNEY_OPTIMIZER]: ALWAYS, + [ADOBE_TARGET]: NEVER, + }, + type, + propositionId = "propositionID", + itemId = "itemId", + trackingLabel = "trackingLabel", + scopeType = "page", + notification = { + id: "notifyId", + scope: "web://mywebsite.com", + scopeDetails: { + something: true, + decisionProvider: ADOBE_JOURNEY_OPTIMIZER, + }, + }, +} = {}) => { + const { storeInteractionMeta } = createInteractionStorage(); + return createDecorateProposition( + autoCollectPropositionInteractions, + type, + propositionId, + itemId, + trackingLabel, + scopeType, + notification, + storeInteractionMeta, + ); +}; diff --git a/vtest/unit/helpers/createMockProposition.js b/vtest/unit/helpers/createMockProposition.js new file mode 100644 index 000000000..eb59cc1cc --- /dev/null +++ b/vtest/unit/helpers/createMockProposition.js @@ -0,0 +1,29 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import injectCreateProposition from "../../../src/components/Personalization/handlers/injectCreateProposition.js"; + +const createProposition = injectCreateProposition({ + preprocess: (data) => data, + isPageWideSurface: () => false, +}); +export default (item, scopeDetails = {}) => { + return createProposition({ + id: "id", + scope: "scope", + scopeDetails: { + decisionProvider: "AJO", + ...scopeDetails, + }, + items: [item], + }); +}; diff --git a/vtest/unit/helpers/createResponse.js b/vtest/unit/helpers/createResponse.js new file mode 100644 index 000000000..1523b9eba --- /dev/null +++ b/vtest/unit/helpers/createResponse.js @@ -0,0 +1,15 @@ +/* +Copyright 2021 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import injectCreateResponse from "../../../src/core/injectCreateResponse.js"; + +export default injectCreateResponse({ logger: console }); diff --git a/vtest/unit/helpers/describeRequest.js b/vtest/unit/helpers/describeRequest.js new file mode 100644 index 000000000..78d202a3a --- /dev/null +++ b/vtest/unit/helpers/describeRequest.js @@ -0,0 +1,52 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, describe, it, expect } from "vitest"; +import uuidV4Regex from "../constants/uuidV4Regex.js"; + +/** + * Tests the base methods that all types of requests share. + */ +export default (createRequest) => { + describe("base request functionality", () => { + let payload; + let request; + beforeEach(() => { + payload = {}; + request = createRequest({ + payload, + }); + }); + + // getAction and getUseSendBeacon will be covered in the tests + // for the request modules that leverage this base request. + + it("provides an ID", () => { + expect(request.getId()).toMatch(uuidV4Regex); + }); + it("provides payload", () => { + expect(request.getPayload()).toBe(payload); + }); + it("provides useThirdPartyDomain", () => { + expect(request.getUseIdThirdPartyDomain()).toBe(false); + request.setUseIdThirdPartyDomain(); + expect(request.getUseIdThirdPartyDomain()).toBe(true); + }); + it("sets isIdentityEstablished", () => { + // We only test that isIdentityEstablished is a function. + // It sets an internal variable that's passed into + // getAction and getUseSendBeacon. This part will be covered in the tests + // for the request modules that leverage this base request. + expect(request.setIsIdentityEstablished).toEqual(expect.any(Function)); + }); + }); +}; diff --git a/vtest/unit/helpers/describeRequestPayload.js b/vtest/unit/helpers/describeRequestPayload.js new file mode 100644 index 000000000..030e8b36c --- /dev/null +++ b/vtest/unit/helpers/describeRequestPayload.js @@ -0,0 +1,89 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, describe, it, expect } from "vitest"; + +/** + * Tests the base methods that all types of request payloads share. + */ +export default (createPayload) => { + describe("base request payload functionality", () => { + let payload; + beforeEach(() => { + payload = createPayload(); + }); + it("merges state", () => { + payload.mergeState({ + fruit: { + name: "banana", + }, + vegetable: { + name: "carrot", + }, + }); + payload.mergeState({ + fruit: { + name: "apple", + calories: 105, + }, + vegetable: { + calories: 25, + }, + }); + + // We don't evaluate the entire `state` object because the request + // modules that leverage this base request may have added other things + // to `state` as well. For that reason, we just evaluate the fruit + // and vegetable. + const postSerializationPayload = JSON.parse(JSON.stringify(payload)); + expect(postSerializationPayload.meta.state.fruit).toEqual({ + name: "apple", + calories: 105, + }); + expect(postSerializationPayload.meta.state.vegetable).toEqual({ + name: "carrot", + calories: 25, + }); + }); + it("merges query", () => { + payload.mergeQuery({ + fruit: { + name: "banana", + }, + vegetable: { + name: "carrot", + }, + }); + payload.mergeQuery({ + fruit: { + name: "apple", + calories: 105, + }, + vegetable: { + calories: 25, + }, + }); + // We don't evaluate the entire `query` object because the request + // modules that leverage this base request may have added other things + // to `query` as well. For that reason, we just evaluate the fruit + // and vegetable. + const postSerializationPayload = JSON.parse(JSON.stringify(payload)); + expect(postSerializationPayload.query.fruit).toEqual({ + name: "apple", + calories: 105, + }); + expect(postSerializationPayload.query.vegetable).toEqual({ + name: "carrot", + calories: 25, + }); + }); + }); +}; diff --git a/vtest/unit/helpers/describeValidation.js b/vtest/unit/helpers/describeValidation.js new file mode 100644 index 000000000..f17f04997 --- /dev/null +++ b/vtest/unit/helpers/describeValidation.js @@ -0,0 +1,62 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, describe, it, expect } from "vitest"; + +export default (description, validator, specObjects) => { + describe(description, () => { + specObjects.forEach( + ({ value, expected = value, error = false, warning = false }) => { + if (error) { + it(`rejects ${JSON.stringify(value)}`, () => { + const logger = { + warn: vi.fn(), + }; + expect(() => + validator.call( + { + logger, + }, + value, + "mykey", + ), + ).toThrowError(/'mykey[^']*'(:| is)/); + if (warning) { + expect(logger.warn).toHaveBeenCalled(); + } else { + expect(logger.warn).not.toHaveBeenCalled(); + } + }); + } else { + it(`transforms \`${JSON.stringify(value)}\` to \`${JSON.stringify(expected)}\``, () => { + const logger = { + warn: vi.fn(), + }; + expect( + validator.call( + { + logger, + }, + value, + "mykey", + ), + ).toEqual(expected); + if (warning) { + expect(logger.warn).toHaveBeenCalled(); + } else { + expect(logger.warn).not.toHaveBeenCalled(); + } + }); + } + }, + ); + }); +}; diff --git a/vtest/unit/helpers/flushPromiseChains.js b/vtest/unit/helpers/flushPromiseChains.js new file mode 100644 index 000000000..7e05a6b18 --- /dev/null +++ b/vtest/unit/helpers/flushPromiseChains.js @@ -0,0 +1,28 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +/** + * Returns a promise that will be resolved after all outstanding promise chains + * have been flushed. This assumes (1) that the promise chains to be flushed + * resolve their promises promptly (rather than doing something like + * setTimeout(..., 100) somewhere in the chain) and (2) that the chains + * are no longer than 10 promises long. + * @returns {Promise} + */ +export default () => { + let promise; + for (let i = 0; i < 10; i += 1) { + promise = promise + ? promise.then(() => Promise.resolve()) + : Promise.resolve(); + } + return promise; +}; diff --git a/vtest/unit/helpers/pause.js b/vtest/unit/helpers/pause.js new file mode 100644 index 000000000..d28f841bb --- /dev/null +++ b/vtest/unit/helpers/pause.js @@ -0,0 +1,16 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +export default (ms) => + new Promise((resolve) => { + setTimeout(resolve, ms); + }); diff --git a/vtest/unit/helpers/removeAllCookies.js b/vtest/unit/helpers/removeAllCookies.js new file mode 100644 index 000000000..9d77539ca --- /dev/null +++ b/vtest/unit/helpers/removeAllCookies.js @@ -0,0 +1,19 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { cookieJar } from "../../../src/utils/index.js"; + +export default () => { + Object.keys(cookieJar.get()).forEach((cookieName) => { + cookieJar.remove(cookieName); + }); +}; diff --git a/vtest/unit/helpers/testConfigValidators.js b/vtest/unit/helpers/testConfigValidators.js new file mode 100644 index 000000000..b573fef58 --- /dev/null +++ b/vtest/unit/helpers/testConfigValidators.js @@ -0,0 +1,53 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, it, expect } from "vitest"; + +export default ({ + configValidators, + validConfigurations, + invalidConfigurations, + deprecatedConfigurations = [], + defaultValues, +}) => { + validConfigurations.forEach((cfg, i) => { + it(`validates configuration (${i})`, () => { + configValidators(cfg); + }); + }); + invalidConfigurations.forEach((cfg, i) => { + it(`invalidates configuration (${i})`, () => { + expect(() => { + configValidators(cfg); + }).toThrowError(); + }); + }); + it("provides default values", () => { + const config = configValidators({}); + Object.keys(defaultValues).forEach((key) => { + expect(config[key]).toBe(defaultValues[key]); + }); + }); + deprecatedConfigurations.forEach((cfg, i) => { + it(`outputs messages for deprecated fields (${i})`, () => { + const logger = { + warn: vi.fn(), + }; + configValidators.call( + { + logger, + }, + cfg, + ); + expect(logger.warn).toHaveBeenCalled(); + }); + }); +}; diff --git a/vtest/unit/specs/components/ActivityCollector/attachClickActivityCollector.spec.js b/vtest/unit/specs/components/ActivityCollector/attachClickActivityCollector.spec.js new file mode 100644 index 000000000..d5fc9346f --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/attachClickActivityCollector.spec.js @@ -0,0 +1,121 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import attachClickActivityCollector from "../../../../../src/components/ActivityCollector/attachClickActivityCollector.js"; +import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; + +describe("ActivityCollector::attachClickActivityCollector", () => { + const config = {}; + let eventManager; + let lifecycle; + let clickHandler; + let handleError; + beforeEach(() => { + config.clickCollectionEnabled = true; + eventManager = { + createEvent: vi.fn().mockReturnValue({ + isEmpty: () => false, + documentMayUnload: () => false, + }), + sendEvent: vi.fn().mockReturnValue(Promise.resolve()), + }; + lifecycle = { + onClick: vi.fn().mockReturnValue(Promise.resolve()), + }; + // eslint-disable-next-line no-unused-vars + vi.spyOn(document, "addEventListener").mockImplementation( + (name, handler) => { + clickHandler = handler; + }, + ); + handleError = vi.fn(); + }); + const build = () => { + attachClickActivityCollector({ + config, + eventManager, + lifecycle, + handleError, + }); + }; + it("Attaches click handler if clickCollectionEnabled is set to true", () => { + build(); + expect(document.addEventListener).toHaveBeenCalled(); + }); + it("Attaches click handler if clickCollectionEnabled is set to false", () => { + config.clickCollectionEnabled = false; + build(); + expect(document.addEventListener).toHaveBeenCalled(); + }); + it("Publishes onClick lifecycle events at clicks when clickCollectionEnabled is set to true", () => { + build(); + clickHandler({}); + expect(lifecycle.onClick).toHaveBeenCalled(); + }); + it("Does not publish onClick lifecycle events for AppMeasurement repropagated click-events", () => { + build(); + const clickEvent = { + s_fe: 1, + }; + clickHandler(clickEvent); + expect(lifecycle.onClick).not.toHaveBeenCalled(); + }); + it("Handles errors inside onClick lifecycle", () => { + const error = new Error("Bad thing happened."); + lifecycle.onClick.mockReturnValue(Promise.reject(error)); + build(); + return clickHandler({}) + .then(() => { + return flushPromiseChains(); + }) + .then(() => { + expect(handleError).toHaveBeenCalledWith(error, "click collection"); + }); + }); + it("Sends populated events", () => { + build(); + return clickHandler({}) + .then(() => { + return flushPromiseChains(); + }) + .then(() => { + expect(eventManager.sendEvent).toHaveBeenCalled(); + }); + }); + it("Does not send empty events", () => { + eventManager.createEvent.mockReturnValue({ + isEmpty: () => true, + documentMayUnload: () => false, + }); + build(); + return clickHandler({}) + .then(() => { + return flushPromiseChains(); + }) + .then(() => { + expect(eventManager.sendEvent).not.toHaveBeenCalled(); + }); + }); + it("handles errors thrown in sendEvent", () => { + const error = new Error("Network Error"); + eventManager.sendEvent.mockReturnValue(Promise.reject(error)); + build(); + return clickHandler({}) + .then(() => { + return flushPromiseChains(); + }) + .then(() => { + expect(handleError).toHaveBeenCalledWith(error, "click collection"); + }); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/configValidators.spec.js b/vtest/unit/specs/components/ActivityCollector/configValidators.spec.js new file mode 100644 index 000000000..6736cb31a --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/configValidators.spec.js @@ -0,0 +1,50 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import configValidators from "../../../../../src/components/ActivityCollector/configValidators.js"; +import testConfigValidators from "../../../helpers/testConfigValidators.js"; + +describe("ActivityCollector config validators", () => { + testConfigValidators({ + configValidators, + validConfigurations: [ + {}, + { + clickCollectionEnabled: false, + }, + { + clickCollectionEnabled: false, + downloadLinkQualifier: "", + }, + ], + invalidConfigurations: [ + { + clickCollectionEnabled: "", + }, + { + clickCollectionEnabled: true, + downloadLinkQualifier: "[", + }, + ], + defaultValues: { + clickCollectionEnabled: true, + downloadLinkQualifier: + "\\.(exe|zip|wav|mp3|mov|mpg|avi|wmv|pdf|doc|docx|xls|xlsx|ppt|pptx)$", + }, + deprecatedConfigurations: [ + { + onBeforeLinkClickSend: () => undefined, + }, + ], + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/createClickActivityStorage.spec.js b/vtest/unit/specs/components/ActivityCollector/createClickActivityStorage.spec.js new file mode 100644 index 000000000..ea9951f67 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/createClickActivityStorage.spec.js @@ -0,0 +1,54 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createClickActivityStorage from "../../../../../src/components/ActivityCollector/createClickActivityStorage.js"; +import { CLICK_ACTIVITY_DATA } from "../../../../../src/constants/sessionDataKeys.js"; + +describe("ActivityCollector::createClickActivityStorage", () => { + let storage; + let clickActivityStorage; + beforeEach(() => { + storage = { + getItem: vi.fn(), + setItem: vi.fn(), + removeItem: vi.fn(), + }; + clickActivityStorage = createClickActivityStorage({ + storage, + }); + }); + it("saves data", () => { + clickActivityStorage.save({ + key: "value", + }); + expect(storage.setItem).toHaveBeenCalledWith( + CLICK_ACTIVITY_DATA, + '{"key":"value"}', + ); + }); + it("loads data", () => { + storage.getItem.mockReturnValue('{"key":"value"}'); + const data = clickActivityStorage.load(); + expect(data).toEqual({ + key: "value", + }); + }); + it("loads null when no data is present", () => { + const data = clickActivityStorage.load(); + expect(data).toBeNull(); + }); + it("removes data", () => { + clickActivityStorage.remove(); + expect(storage.removeItem).toHaveBeenCalledWith(CLICK_ACTIVITY_DATA); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/createClickedElementProperties.spec.js b/vtest/unit/specs/components/ActivityCollector/createClickedElementProperties.spec.js new file mode 100644 index 000000000..f27876067 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/createClickedElementProperties.spec.js @@ -0,0 +1,252 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createClickedElementProperties from "../../../../../src/components/ActivityCollector/createClickedElementProperties.js"; + +describe("ActivityCollector::createClickedElementProperties", () => { + let properties; + beforeEach(() => { + properties = { + pageName: "testPageName", + linkName: "testLinkName", + linkRegion: "testLinkRegion", + linkType: "testLinkType", + linkUrl: "testLinkUrl", + pageIDType: 0, + }; + }); + it("Should return object with the init properties", () => { + const props = createClickedElementProperties({ + properties, + }); + expect(props.properties).toEqual(properties); + }); + it("Can determine it holds valid link properties", () => { + const props = createClickedElementProperties({ + properties, + }); + expect(props.isValidLink()).toBe(true); + }); + it("Can determine it holds invalid link properties", () => { + let props = createClickedElementProperties({}); + expect(props.isValidLink()).toBe(false); + props = createClickedElementProperties({ + properties, + }); + props.linkName = ""; + expect(props.isValidLink()).toBe(false); + }); + it("Can determine it holds internal link properties", () => { + const props = createClickedElementProperties({ + properties, + }); + expect(props.isInternalLink()).toBe(false); + props.linkType = "other"; + expect(props.isInternalLink()).toBe(true); + }); + it("Can determine it holds valid ActivityMap properties", () => { + const props = createClickedElementProperties({ + properties, + }); + expect(props.isValidActivityMapData()).toBe(true); + props.pageName = ""; + expect(props.isValidActivityMapData()).toBe(false); + }); + it("Can convert properties to a populated DATA Analytics schema with ActivityMap data", () => { + const props = createClickedElementProperties({ + properties, + }); + const data = props.data; + expect(data).toEqual({ + __adobe: { + analytics: { + contextData: { + a: { + activitymap: { + page: "testPageName", + link: "testLinkName", + region: "testLinkRegion", + pageIDType: 0, + }, + }, + }, + }, + }, + }); + }); + it("Can convert properties to a populated XDM Analytics schema with ActivityMap data", () => { + const props = createClickedElementProperties({ + properties, + }); + const data = props.xdm; + expect(data).toEqual({ + eventType: "web.webinteraction.linkClicks", + web: { + webInteraction: { + name: "testLinkName", + region: "testLinkRegion", + type: "testLinkType", + URL: "testLinkUrl", + linkClicks: { + value: 1, + }, + }, + }, + }); + }); + it("Can populate properties from options", () => { + const props = createClickedElementProperties(); + const options = { + xdm: { + web: { + webInteraction: { + name: "xdmName", + region: "xdmRegion", + type: "xdmType", + URL: "xdmUrl", + linkClicks: { + value: 2, + }, + }, + }, + }, + data: { + __adobe: { + analytics: { + contextData: { + a: { + activitymap: { + page: "dataPage", + link: "dataLink", + region: "dataRegion", + pageIDType: 1, + }, + }, + }, + }, + }, + }, + clickedElement: {}, + }; + props.options = options; + // The DATA portion takes priority + expect(props.properties).toEqual({ + pageName: "dataPage", + linkName: "dataLink", + linkRegion: "dataRegion", + linkType: "xdmType", + linkUrl: "xdmUrl", + pageIDType: 1, + }); + }); + it("Can apply a property filter", () => { + const props = createClickedElementProperties({ + properties, + }); + // Need a clickedElement for the filter to be executed + props.clickedElement = {}; + const filter = (p) => { + p.linkType = "filtered"; + }; + props.applyPropertyFilter(filter); + expect(props.linkType).toBe("filtered"); + }); + it("Prints message when filter rejects properties", () => { + const logger = { + info: vi.fn(), + }; + const props = createClickedElementProperties({ + properties, + logger, + }); + // Need a clickedElement for the filter to be executed + props.clickedElement = {}; + const filter = (p) => { + p.linkType = "filtered"; + return false; + }; + props.applyPropertyFilter(filter); + expect(logger.info).toHaveBeenCalledWith( + expect.stringMatching( + /^Clicked element properties were rejected by filter function/, + ), + ); + }); + it("Can apply a property filter for all properties", () => { + const props = createClickedElementProperties({ + properties, + }); + props.clickedElement = {}; + const filter = (p) => { + p.pageName = "filtered"; + p.linkName = "filtered"; + p.linkRegion = "filtered"; + p.linkType = "filtered"; + p.linkUrl = "filtered"; + p.pageIDType = 1; + }; + props.applyPropertyFilter(filter); + expect(props.linkType).toBe("filtered"); + expect(props.pageName).toBe("filtered"); + }); + it("Can apply an options property filter", () => { + const props = createClickedElementProperties({ + properties, + }); + // Need a clickedElement for the filter to be executed + props.clickedElement = {}; + const filter = (options) => { + options.xdm.web.webInteraction.type = "filtered"; + }; + props.applyOptionsFilter(filter); + expect(props.linkType).toBe("filtered"); + }); + it("Can apply an options property filter for all properties", () => { + const props = createClickedElementProperties({ + properties, + }); + props.clickedElement = {}; + const filter = (options) => { + if ( + options && + options.xdm && + options.xdm.web && + options.xdm.web.webInteraction + ) { + const webInteraction = options.xdm.web.webInteraction; + webInteraction.name = "filtered"; // Link name + webInteraction.region = "filtered"; // Link region + webInteraction.type = "filtered"; // Link type + webInteraction.URL = "filtered"; // Link URL + } + /* eslint no-underscore-dangle: 0 */ + if ( + options && + options.data && + options.data.__adobe && + options.data.__adobe.analytics + ) { + const { contextData } = options.data.__adobe.analytics; + if (contextData && contextData.a && contextData.a.activitymap) { + const activitymap = contextData.a.activitymap; + activitymap.page = "filtered"; // Page name + activitymap.link = "filtered"; // Link name + activitymap.region = "filtered"; // Link region + } + } + }; + props.applyOptionsFilter(filter); + expect(props.linkType).toBe("filtered"); + expect(props.pageName).toBe("filtered"); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/createGetClickedElementProperties.spec.js b/vtest/unit/specs/components/ActivityCollector/createGetClickedElementProperties.spec.js new file mode 100644 index 000000000..370e5707f --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/createGetClickedElementProperties.spec.js @@ -0,0 +1,260 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createGetClickedElementProperties from "../../../../../src/components/ActivityCollector/createGetClickedElementProperties.js"; +import createClickActivityStorage from "../../../../../src/components/ActivityCollector/createClickActivityStorage.js"; + +describe("ActivityCollector::createGetClickedElementProperties", () => { + const mockWindow = { + location: { + protocol: "https:", + host: "example.com", + hostname: "example.com", + pathname: "/", + href: "https://example.com/", + }, + }; + const supportedLinkElement = { + tagName: "A", + href: "index.html", + nodeType: 1, + }; + let getLinkName; + let getLinkRegion; + let getAbsoluteUrlFromAnchorElement; + let findClickableElement; + let determineLinkType; + let logger; + let clickActivityStorage; + beforeEach(() => { + getLinkName = vi.fn(); + getLinkRegion = vi.fn(); + getAbsoluteUrlFromAnchorElement = vi.fn(); + findClickableElement = vi.fn(); + determineLinkType = vi.fn(); + logger = { + info: vi.fn(), + }; + clickActivityStorage = createClickActivityStorage({ + storage: { + getItem: () => {}, + setItem: () => {}, + removeItem: () => {}, + }, + }); + }); + it("Returns complete linkDetails when it is a supported anchor element", () => { + const config = { + onBeforeLinkClickSend: (options) => { + options.data.custom = "test data field"; + return true; + }, + clickCollection: { + externalLink: true, + }, + }; + getLinkRegion.mockReturnValue("root"); + getLinkName.mockReturnValue("Go to cart"); + getAbsoluteUrlFromAnchorElement.mockReturnValue("http://blah.com"); + findClickableElement.mockReturnValue(supportedLinkElement); + determineLinkType.mockReturnValue("exit"); + const getClickedElementProperties = createGetClickedElementProperties({ + getLinkRegion, + getLinkName, + getAbsoluteUrlFromAnchorElement, + findClickableElement, + determineLinkType, + window: mockWindow, + }); + const result = getClickedElementProperties({ + clickedElement: {}, + config, + logger, + clickActivityStorage, + }); + // I have to set this manually because of passing in {} as the clickedElement + result.pageIDType = 0; + expect(result.options).toEqual({ + xdm: { + eventType: "web.webinteraction.linkClicks", + web: { + webInteraction: { + name: "Go to cart", + region: "root", + type: "exit", + URL: "http://blah.com", + linkClicks: { + value: 1, + }, + }, + }, + }, + data: { + __adobe: { + analytics: { + contextData: { + a: { + activitymap: { + page: "https://example.com/", + link: "Go to cart", + region: "root", + pageIDType: 0, + }, + }, + }, + }, + }, + custom: "test data field", + }, + clickedElement: {}, + }); + }); + it("Returns undefined when the customer callback returns false", () => { + const config = { + onBeforeLinkClickSend: () => { + return false; + }, + clickCollection: { + externalLink: true, + }, + }; + getLinkRegion.mockReturnValue("root"); + getLinkName.mockReturnValue("Go to cart"); + getAbsoluteUrlFromAnchorElement.mockReturnValue("http://blah.com"); + findClickableElement.mockReturnValue(supportedLinkElement); + determineLinkType.mockReturnValue("exit"); + const getClickedElementProperties = createGetClickedElementProperties({ + getLinkRegion, + getLinkName, + getAbsoluteUrlFromAnchorElement, + findClickableElement, + determineLinkType, + window: mockWindow, + }); + const result = getClickedElementProperties({ + clickedElement: {}, + config, + logger, + clickActivityStorage, + }); + expect(result.options).toEqual(undefined); + }); + it("Returns undefined when not supported anchor element", () => { + const config = { + onBeforeLinkClickSend: () => { + return true; + }, + clickCollection: { + externalLink: true, + }, + }; + getLinkRegion.mockReturnValue(undefined); + getLinkName.mockReturnValue("Go to cart"); + getAbsoluteUrlFromAnchorElement.mockReturnValue("http://blah.com"); + findClickableElement.mockReturnValue(undefined); + determineLinkType.mockReturnValue("exit"); + const getClickedElementProperties = createGetClickedElementProperties({ + getLinkRegion, + getLinkName, + getAbsoluteUrlFromAnchorElement, + findClickableElement, + determineLinkType, + window: mockWindow, + }); + const result = getClickedElementProperties({ + clickedElement: {}, + config, + logger, + clickActivityStorage, + }); + expect(result.options).toEqual(undefined); + }); + it("Returns only options with data element if clickable element is missing href", () => { + const config = { + onBeforeLinkClickSend: () => { + return true; + }, + clickCollection: { + externalLink: true, + }, + }; + getLinkRegion.mockReturnValue("root"); + getLinkName.mockReturnValue("Go to cart"); + getAbsoluteUrlFromAnchorElement.mockReturnValue(undefined); + findClickableElement.mockReturnValue(supportedLinkElement); + determineLinkType.mockReturnValue("exit"); + const getClickedElementProperties = createGetClickedElementProperties({ + getLinkRegion, + getLinkName, + getAbsoluteUrlFromAnchorElement, + findClickableElement, + determineLinkType, + window: mockWindow, + }); + const result = getClickedElementProperties({ + clickedElement: {}, + config, + logger, + clickActivityStorage, + }); + // I have to set this manually because of passing in {} as the clickedElement + result.pageIDType = 0; + expect(result.options).toEqual({ + data: { + __adobe: { + analytics: { + contextData: { + a: { + activitymap: { + page: "https://example.com/", + link: "Go to cart", + region: "root", + pageIDType: 0, + }, + }, + }, + }, + }, + }, + clickedElement: {}, + }); + }); + it("Returns the object with link details when callback does not return explicit false ", () => { + const config = { + onBeforeLinkClickSend: () => {}, + clickCollection: { + externalLink: true, + }, + }; + getLinkRegion.mockReturnValue("root"); + getLinkName.mockReturnValue("Go to cart"); + getAbsoluteUrlFromAnchorElement.mockReturnValue("http://blah.com"); + findClickableElement.mockReturnValue(supportedLinkElement); + determineLinkType.mockReturnValue("exit"); + const getClickedElementProperties = createGetClickedElementProperties({ + getLinkRegion, + getLinkName, + getAbsoluteUrlFromAnchorElement, + findClickableElement, + determineLinkType, + window: mockWindow, + }); + const result = getClickedElementProperties({ + clickedElement: {}, + config, + logger, + clickActivityStorage, + }); + expect(result).not.toBe(undefined); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/createInjectClickedElementProperties.spec.js b/vtest/unit/specs/components/ActivityCollector/createInjectClickedElementProperties.spec.js new file mode 100644 index 000000000..b909d7b8e --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/createInjectClickedElementProperties.spec.js @@ -0,0 +1,157 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import createInjectClickedElementProperties from "../../../../../src/components/ActivityCollector/createInjectClickedElementProperties.js"; +import createEvent from "../../../../../src/core/createEvent.js"; +import { downloadLinkQualifier as dlwValidator } from "../../../../../src/components/ActivityCollector/configValidators.js"; + +describe("ActivityCollector::createInjectClickedElementProperties", () => { + const getClickedElementProperties = vi.fn(); + const clickActivityStorage = { + save: vi.fn(), + }; + const downloadLinkQualifier = dlwValidator(); + it("Extends event XDM data with link information for supported anchor elements when clickCollectionEnabled", () => { + const config = { + downloadLinkQualifier, + clickCollectionEnabled: true, + clickCollection: {}, + }; + const injectClickedElementProperties = createInjectClickedElementProperties( + { + getClickedElementProperties, + clickActivityStorage, + config, + }, + ); + const event = createEvent(); + getClickedElementProperties.mockReturnValue({ + xdm: { + web: { + webInteraction: { + name: "test1", + }, + }, + }, + data: {}, + isValidLink: () => true, + isInternalLink: () => false, + isValidActivityMapData: () => true, + }); + injectClickedElementProperties({ + event, + clickedElement: {}, + }); + expect(event.isEmpty()).toBe(false); + }); + it("Does not extend event XDM data when clickCollectionEnabled is false", () => { + const event = createEvent(); + const config = { + downloadLinkQualifier, + clickCollectionEnabled: false, + }; + const injectClickedElementProperties = createInjectClickedElementProperties( + { + getClickedElementProperties, + config, + }, + ); + getClickedElementProperties.mockReturnValue({ + xdm: { + web: { + webInteraction: { + name: "test1", + }, + }, + }, + data: {}, + }); + injectClickedElementProperties({ + clickedElement: {}, + event, + }); + expect(event.isEmpty()).toBe(true); + }); + it("Does not extend event XDM data with link information for unsupported anchor elements", () => { + const event = createEvent(); + const config = { + downloadLinkQualifier, + clickCollectionEnabled: true, + clickCollection: {}, + }; + const injectClickedElementProperties = createInjectClickedElementProperties( + { + getClickedElementProperties, + clickActivityStorage, + config, + }, + ); + getClickedElementProperties.mockReturnValue({ + data: {}, + isValidLink: () => false, + isInternalLink: () => false, + isValidActivityMapData: () => true, + }); + injectClickedElementProperties({ + clickedElement: {}, + event, + }); + expect(event.isEmpty()).toBe(true); + }); + it("Does not save click data to storage if onBeforeLinkClickSend is defined", () => { + const config = { + clickCollectionEnabled: true, + clickCollection: { + internalLinkEnabled: true, + eventGroupingEnabled: true, + }, + onBeforeLinkClickSend: () => {}, + }; + const logger = { + info: vi.fn(), + }; + getClickedElementProperties.mockReturnValue({ + isValidLink: () => true, + isInternalLink: () => true, + pageName: "testPage", + pageIDType: 1, + linkName: "testLink", + linkType: "other", + }); + const injectClickedElementProperties = createInjectClickedElementProperties( + { + config, + logger, + getClickedElementProperties, + clickActivityStorage, + }, + ); + const event = { + mergeXdm: vi.fn(), + mergeData: vi.fn(), + }; + injectClickedElementProperties({ + clickedElement: {}, + event, + }); + // No click data should be saved to storage, only the page data. + expect(clickActivityStorage.save).toHaveBeenCalledWith({ + pageName: "testPage", + pageIDType: 1, + }); + // If mergeXdm and mergeData are called, it means that the click data was not saved and + // will instead go out with the event. + expect(event.mergeXdm).toHaveBeenCalled(); + expect(event.mergeData).toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/createRecallAndInjectClickedElementProperties.spec.js b/vtest/unit/specs/components/ActivityCollector/createRecallAndInjectClickedElementProperties.spec.js new file mode 100644 index 000000000..f9da1e4cc --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/createRecallAndInjectClickedElementProperties.spec.js @@ -0,0 +1,86 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createRecallAndInjectClickedElementProperties from "../../../../../src/components/ActivityCollector/createRecallAndInjectClickedElementProperties.js"; + +describe("ActivityCollector::createRecallAndInjectClickedElementProperties", () => { + let props; + let clickActivityStorage; + let event; + beforeEach(() => { + props = {}; + clickActivityStorage = { + load: vi.fn().mockReturnValue(props), + save: vi.fn(), + }; + event = { + mergeXdm: vi.fn(), + mergeData: vi.fn(), + }; + }); + it("should return a function", () => { + const recallAndInjectClickedElementProperties = + createRecallAndInjectClickedElementProperties({ + clickActivityStorage, + }); + expect(recallAndInjectClickedElementProperties).toEqual( + expect.any(Function), + ); + }); + it("should merge stored clicked element properties to event XDM and DATA", () => { + const recallClickElementProperties = + createRecallAndInjectClickedElementProperties({ + clickActivityStorage, + }); + props.pageName = "examplePage"; + props.linkName = "example"; + props.linkRegion = "exampleRegion"; + props.linkType = "external"; + props.linkUrl = "https://example.com"; + props.pageIDType = 1; + recallClickElementProperties(event); + expect(event.mergeXdm).toHaveBeenCalledWith({ + web: { + webInteraction: { + name: "example", + region: "exampleRegion", + type: "external", + URL: "https://example.com", + linkClicks: { + value: 1, + }, + }, + }, + }); + expect(event.mergeData).toHaveBeenCalledWith({ + __adobe: { + analytics: { + contextData: { + a: { + activitymap: { + page: "examplePage", + link: "example", + region: "exampleRegion", + pageIDType: 1, + }, + }, + }, + }, + }, + }); + expect(clickActivityStorage.save).toHaveBeenCalledWith({ + pageName: "examplePage", + pageIDType: 1, + }); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/createStorePageViewProperties.spec.js b/vtest/unit/specs/components/ActivityCollector/createStorePageViewProperties.spec.js new file mode 100644 index 000000000..b163d5f6d --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/createStorePageViewProperties.spec.js @@ -0,0 +1,49 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createStorePageViewProperties from "../../../../../src/components/ActivityCollector/createStorePageViewProperties.js"; + +describe("ActivityCollector::createStorePageViewProperties", () => { + let clickActivityStorage; + beforeEach(() => { + clickActivityStorage = { + save: vi.fn(), + }; + }); + it("should return a function", () => { + const storePageViewProperties = createStorePageViewProperties({ + clickActivityStorage, + }); + expect(storePageViewProperties).toEqual(expect.any(Function)); + }); + it("stores page view properties when available in event", () => { + const storePageViewProperties = createStorePageViewProperties({ + clickActivityStorage, + }); + storePageViewProperties({ + getContent: () => ({ + xdm: { + web: { + webPageDetails: { + name: "testPageName", + }, + }, + }, + }), + }); + expect(clickActivityStorage.save).toHaveBeenCalledWith({ + pageName: "testPageName", + pageIDType: 1, + }); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/getLinkName.spec.js b/vtest/unit/specs/components/ActivityCollector/getLinkName.spec.js new file mode 100644 index 000000000..44a1bbaef --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/getLinkName.spec.js @@ -0,0 +1,149 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import getLinkName from "../../../../../src/components/ActivityCollector/getLinkName.js"; + +const createNodeWithAttribute = (nodeName, attributeName, attributeValue) => { + const node = { + nodeName, + getAttribute: (name) => { + if (name === attributeName) { + return attributeValue; + } + return null; + }, + }; + node[attributeName] = attributeValue; + return node; +}; +describe("ActivityCollector::getLinkName", () => { + it("Returns empty string if no link-name can be constructed", () => { + expect(getLinkName({})).toBe(""); + }); + it("Prioritizes node innerText over textContent", () => { + // The innerText always takes priority when determining the link-name + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText + expect( + getLinkName({ + nodeName: "A", + innerText: "inner", + textContent: "content", + }), + ).toBe("inner"); + // Child nodes are ignored unless they include non-supported elements + // which can end up corrupting the innerText value + expect( + getLinkName({ + nodeName: "A", + innerText: "inner", + textContent: "content", + childNodes: [createNodeWithAttribute("IMG", "src", "image.jpg")], + }), + ).toBe("inner"); + expect( + getLinkName({ + nodeName: "A", + textContent: "content", + }), + ).toBe("content"); + }); + // this is an use case when in the children nodes there is an unsupported node, + // thus the innerText might contain some unrelated data + it("Excludes unsupported nodes", () => { + expect( + getLinkName({ + innerText: "Not picked due to non-supported child nodes", + nodeName: "A", + childNodes: [ + // Title attributes would contribute to the link-name + // However LINK is a non-supported element + createNodeWithAttribute("LINK", "title", "Link Title"), + createNodeWithAttribute("IMG", "src", "image.jpg"), + ], + }), + ).toBe("image.jpg"); + }); + + // This test could look like: Click Here + it("Prioritizes nodeValue over other element properties", () => { + expect( + getLinkName({ + nodeName: "A", + childNodes: [ + createNodeWithAttribute("#text", "nodeValue", "Click Here"), + createNodeWithAttribute("IMG", "src", "image.jpg"), + ], + }), + ).toBe("Click Here"); + }); + it("Select link-name based on a property hierarchy", () => { + expect( + getLinkName({ + nodeName: "A", + childNodes: [ + createNodeWithAttribute("IMG", "title", "title"), + createNodeWithAttribute("IMG", "src", "image.jpg"), + createNodeWithAttribute("IMG", "alt", "alt"), + createNodeWithAttribute("INPUT", "value", "input"), + ], + }), + ).toBe("alt"); + expect( + getLinkName({ + nodeName: "A", + childNodes: [ + createNodeWithAttribute("IMG", "title", "title"), + createNodeWithAttribute("IMG", "src", "image.jpg"), + createNodeWithAttribute("INPUT", "value", "input"), + ], + }), + ).toBe("title"); + expect( + getLinkName({ + nodeName: "A", + childNodes: [ + createNodeWithAttribute("IMG", "src", "image.jpg"), + createNodeWithAttribute("INPUT", "value", "input"), + ], + }), + ).toBe("input"); + expect( + getLinkName({ + nodeName: "A", + childNodes: [createNodeWithAttribute("IMG", "src", "image.jpg")], + }), + ).toBe("image.jpg"); + }); + it("Truncates excess whitespace in link-name", () => { + expect( + getLinkName({ + nodeName: "A", + innerText: " ab c", + textContent: "content", + }), + ).toBe("ab c"); + }); +}); +it("Ignores the spaces attributes", () => { + expect( + getLinkName({ + nodeName: "A", + childNodes: [ + createNodeWithAttribute("IMG", "title", "title"), + createNodeWithAttribute("IMG", "src", "image.jpg"), + createNodeWithAttribute("IMG", "alt", " "), + createNodeWithAttribute("IMG", "alt", "alt"), + createNodeWithAttribute("INPUT", "value", "input"), + ], + }), + ).toBe("alt"); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/getLinkRegion.spec.js b/vtest/unit/specs/components/ActivityCollector/getLinkRegion.spec.js new file mode 100644 index 000000000..025877f52 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/getLinkRegion.spec.js @@ -0,0 +1,109 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import getLinkRegion from "../../../../../src/components/ActivityCollector/getLinkRegion.js"; + +const createChildElement = (element) => { + return { + parentNode: element, + }; +}; +describe("ActivityCollector::getLinkRegion", () => { + it("Returns BODY if no region is found", () => { + expect(getLinkRegion({})).toBe("BODY"); + }); + it("Picks region properties based on priority", () => { + const tests = [ + { + element: { + id: "id", + role: "region", + "aria-label": "aria", + nodeName: "HEADER", + }, + result: "id", + }, + { + element: { + role: "region", + "aria-label": "aria", + nodeName: "HEADER", + }, + result: "aria", + }, + { + element: { + nodeName: "HEADER", + }, + result: "HEADER", + }, + ]; + tests.forEach((test) => { + const anchor = createChildElement(test.element); + expect(getLinkRegion(anchor)).toBe(test.result); + }); + expect(getLinkRegion({})).toBe("BODY"); + }); + it("Traverses up the DOM to find a region", () => { + const element = { + id: "3-levels", + }; + const anchor = createChildElement(createChildElement(element)); + expect(getLinkRegion(anchor)).toBe("3-levels"); + }); + it("Supports setting region as semantic element name for supported elements", () => { + const tests = [ + { + element: { + nodeName: "HEADER", + }, + result: "HEADER", + }, + { + element: { + nodeName: "MAIN", + }, + result: "MAIN", + }, + { + element: { + nodeName: "FOOTER", + }, + result: "FOOTER", + }, + { + element: { + nodeName: "NAV", + }, + result: "NAV", + }, + { + element: { + nodeName: "SECTION", + }, + result: "BODY", + }, + ]; + tests.forEach((test) => { + const anchor = createChildElement(test.element); + expect(getLinkRegion(anchor)).toBe(test.result); + }); + expect(getLinkRegion({})).toBe("BODY"); + }); + it("Truncates excess whitespace in region", () => { + const element = { + id: " ab c", + }; + const anchor = createChildElement(element); + expect(getLinkRegion(anchor)).toBe("ab c"); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/activityMapExtensionEnabled.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/activityMapExtensionEnabled.spec.js new file mode 100644 index 000000000..48e107827 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/activityMapExtensionEnabled.spec.js @@ -0,0 +1,36 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import activityMapExtensionEnabled from "../../../../../../src/components/ActivityCollector/utils/activityMapExtensionEnabled.js"; + +const ACTIVITY_MAP_EXTENSION_ID = "cppXYctnr"; +describe("ActivityCollector::activityMapExtensionEnabled", () => { + it("should return true if the activity map extension is enabled", () => { + const context = { + getElementById: vi.fn().mockReturnValue({}), + }; + expect(activityMapExtensionEnabled(context)).toBe(true); + expect(context.getElementById).toHaveBeenCalledWith( + ACTIVITY_MAP_EXTENSION_ID, + ); + }); + it("should return false if the activity map extension is not enabled", () => { + const context = { + getElementById: vi.fn().mockReturnValue(null), + }; + expect(activityMapExtensionEnabled(context)).toBe(false); + expect(context.getElementById).toHaveBeenCalledWith( + ACTIVITY_MAP_EXTENSION_ID, + ); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/createTransientStorage.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/createTransientStorage.spec.js new file mode 100644 index 000000000..dca47d510 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/createTransientStorage.spec.js @@ -0,0 +1,40 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import createTransientStorage from "../../../../../../src/components/ActivityCollector/utils/createTransientStorage.js"; + +describe("ActivityCollector::createTransientStorage", () => { + it("should return an object with the expected methods", () => { + const transientStorage = createTransientStorage(window); + expect(transientStorage).toEqual({ + setItem: expect.any(Function), + getItem: expect.any(Function), + removeItem: expect.any(Function), + }); + }); + it("should support storing and retrieving values", () => { + const transientStorage = createTransientStorage(window); + transientStorage.setItem("key1", "value1"); + transientStorage.setItem("key2", "value2"); + expect(transientStorage.getItem("key1")).toBe("value1"); + expect(transientStorage.getItem("key2")).toBe("value2"); + }); + it("should support removing values", () => { + const transientStorage = createTransientStorage(window); + transientStorage.setItem("key1", "value1"); + transientStorage.setItem("key2", "value2"); + transientStorage.removeItem("key1"); + expect(transientStorage.getItem("key1")).toBeFalsy(); + expect(transientStorage.getItem("key2")).toBe("value2"); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/determineLinkType.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/determineLinkType.spec.js new file mode 100644 index 000000000..e7d61123f --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/determineLinkType.spec.js @@ -0,0 +1,53 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, describe, it, expect } from "vitest"; +import determineLinkType from "../../../../../../src/components/ActivityCollector/utils/determineLinkType.js"; + +describe("ActivityCollector::determineLinkType", () => { + let window; + let config; + let linkUrl; + let clickedObj; + beforeEach(() => { + window = {}; + config = {}; + linkUrl = ""; + clickedObj = {}; + }); + it("returns 'other' if linkUrl is an empty string", () => { + const result = determineLinkType(window, config, linkUrl, clickedObj); + expect(result).toBe("other"); + }); + it("returns 'download' if linkUrl qualify as download link", () => { + linkUrl = "https://example.com/download.pdf"; + config.downloadLinkQualifier = /\.pdf$/; + const result = determineLinkType(window, config, linkUrl, clickedObj); + expect(result).toBe("download"); + }); + it("returns 'exit' if linkUrl is an exit link", () => { + linkUrl = "https://adobe.com"; + window.location = { + hostname: "example.com", + }; + const result = determineLinkType(window, config, linkUrl, clickedObj); + expect(result).toBe("exit"); + }); + it("returns 'other' if linkUrl is not a download or exit link", () => { + linkUrl = "https://example.com"; + window.location = { + hostname: "example.com", + }; + const result = determineLinkType(window, config, linkUrl, clickedObj); + expect(result).toBe("other"); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/elementHasClickHandler.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/dom/elementHasClickHandler.spec.js new file mode 100644 index 000000000..d30695a73 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/dom/elementHasClickHandler.spec.js @@ -0,0 +1,60 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import elementHasClickHandler from "../../../../../../../src/components/ActivityCollector/utils/dom/elementHasClickHandler.js"; + +describe("ActivityCollector::elementHasClickHandler", () => { + it("should handle invalid elements", () => { + const invalidElements = [ + { + element: null, + }, + { + element: undefined, + }, + { + element: {}, + }, + { + element: { + onclick: null, + }, + }, + { + element: { + onclick: undefined, + }, + }, + ]; + invalidElements.forEach(({ element }) => { + expect(elementHasClickHandler(element)).toBe(false); + }); + }); + it("should handle elements with click handlers", () => { + const clickHandlerElements = [ + { + element: { + onclick: () => {}, + }, + }, + { + element: { + onclick() {}, + }, + }, + ]; + clickHandlerElements.forEach(({ element }) => { + expect(elementHasClickHandler(element)).toBe(true); + }); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/extractDomain.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/dom/extractDomain.spec.js new file mode 100644 index 000000000..4dd3e0428 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/dom/extractDomain.spec.js @@ -0,0 +1,32 @@ +/* +Copyright 2024 example. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import extractDomain from "../../../../../../../src/components/ActivityCollector/utils/dom/extractDomain.js"; + +describe("ActivityCollector::extractDomain", () => { + it("should extract the domain from a URL", () => { + expect(extractDomain("www.example.com")).toBe("www.example.com"); + expect(extractDomain("http://www.example.com")).toBe("www.example.com"); + expect(extractDomain("https://www.example.com")).toBe("www.example.com"); + expect(extractDomain("https://www.example.com/")).toBe("www.example.com"); + expect(extractDomain("https://www.example.com/cool/page")).toBe( + "www.example.com", + ); + }); + it("should handle URLs without a protocol", () => { + expect(extractDomain("example.com")).toBe("example.com"); + expect(extractDomain("www.example.com")).toBe("www.example.com"); + expect(extractDomain("www.example.com/")).toBe("www.example.com"); + expect(extractDomain("www.example.com/cool/page")).toBe("www.example.com"); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/findClickableElement.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/dom/findClickableElement.spec.js new file mode 100644 index 000000000..11d43aff3 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/dom/findClickableElement.spec.js @@ -0,0 +1,35 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import findClickableElement from "../../../../../../../src/components/ActivityCollector/utils/dom/findClickableElement.js"; + +describe("ActivityCollector::findClickableElement", () => { + it("returns null if no clickable element is found", () => { + const element = document.createElement("div"); + const parentElement = document.createElement("div"); + parentElement.appendChild(element); + expect(findClickableElement(element)).toBeNull(); + }); + it("returns the target element if it is clickable", () => { + const element = document.createElement("a"); + element.href = "http://www.example.com"; + expect(findClickableElement(element)).toBe(element); + }); + it("returns the target element's parent if it is not clickable", () => { + const element = document.createElement("div"); + const parentElement = document.createElement("a"); + parentElement.href = "http://www.example.com"; + parentElement.appendChild(element); + expect(findClickableElement(element)).toBe(parentElement); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.spec.js new file mode 100644 index 000000000..fcd4777b5 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.spec.js @@ -0,0 +1,64 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import getAbsoluteUrlFromAnchorElement from "../../../../../../../src/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.js"; + +const initAnchorState = (window, element, anchorState) => { + element.href = anchorState["element.href"]; + element.protocol = anchorState["element.protocol"]; + element.host = anchorState["element.host"]; + window.location.protocol = anchorState["window.location.protocol"]; + window.location.host = anchorState["window.location.host"]; + window.location.pathname = anchorState["window.location.pathname"]; +}; +describe("ActivityCollector::getAbsoluteUrlFromAnchorElement", () => { + it("Makes best attempt to constructs absolute URLs", () => { + const window = { + location: { + protocol: "", + host: "", + pathname: "", + }, + }; + const element = { + protocol: "", + host: "", + }; + const anchorStates = [ + { + "element.href": "http://example.com/example.html", + "element.protocol": "", + "element.host": "", + "window.location.protocol": "http:", + "window.location.host": "example.com", + "window.location.pathname": "/", + expectedResult: "http://example.com/example.html", + }, + { + "element.href": "example.html", + "element.protocol": "", + "element.host": "", + "window.location.protocol": "https:", + "window.location.host": "example.com", + "window.location.pathname": "/", + expectedResult: "https://example.com/example.html", + }, + ]; + anchorStates.forEach((anchorState) => { + initAnchorState(window, element, anchorState); + expect(getAbsoluteUrlFromAnchorElement(window, element)).toBe( + anchorState.expectedResult, + ); + }); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/isButtonSubmitElement.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/dom/isButtonSubmitElement.spec.js new file mode 100644 index 000000000..ea2092211 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/dom/isButtonSubmitElement.spec.js @@ -0,0 +1,41 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isButtonSubmitElement from "../../../../../../../src/components/ActivityCollector/utils/dom/isButtonSubmitElement.js"; + +describe("ActivityCollector::isButtonSubmitElement", () => { + it("should return true for submit button", () => { + const button = document.createElement("button"); + button.type = "submit"; + expect(isButtonSubmitElement(button)).toBe(true); + }); + it("should return true for button with no type", () => { + // This is the default type for a button element + const button = document.createElement("button"); + expect(isButtonSubmitElement(button)).toBe(true); + }); + it("should return false for non-submit button", () => { + const button = document.createElement("button"); + button.type = "button"; + expect(isButtonSubmitElement(button)).toBe(false); + }); + it("should return false for input element", () => { + const input = document.createElement("input"); + input.type = "submit"; + expect(isButtonSubmitElement(input)).toBe(false); + }); + it("should return false for non-button element", () => { + const div = document.createElement("div"); + expect(isButtonSubmitElement(div)).toBe(false); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/isDownloadLink.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/dom/isDownloadLink.spec.js new file mode 100644 index 000000000..dc2107ef7 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/dom/isDownloadLink.spec.js @@ -0,0 +1,47 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isDownloadLink from "../../../../../../../src/components/ActivityCollector/utils/dom/isDownloadLink.js"; +import { downloadLinkQualifier } from "../../../../../../../src/components/ActivityCollector/configValidators.js"; + +describe("ActivityCollector::isDownloadLink", () => { + it("Returns true if the clicked element has a download attribute", () => { + const clickedElement = { + download: "filename", + }; + expect(isDownloadLink(null, "https://example.com/", clickedElement)).toBe( + true, + ); + }); + it("Returns true if the link matches the download link qualifying regular expression", () => { + const downloadLinks = [ + "download.pdf", + "http://example.com/download.zip", + "https://example.com/download.docx", + ]; + // this runs the validator with undefined input which returns the default regex + downloadLinks.forEach((downloadLink) => { + expect(isDownloadLink(downloadLinkQualifier(), downloadLink, {})).toBe( + true, + ); + }); + }); + it("Returns false if the link does not match the download link qualifying regular expression", () => { + const downloadLinks = ["download.mod", "http://example.com/download.png"]; + downloadLinks.forEach((downloadLink) => { + expect(isDownloadLink(downloadLinkQualifier(), downloadLink, {})).toBe( + false, + ); + }); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/isExitLink.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/dom/isExitLink.spec.js new file mode 100644 index 000000000..ed3b19ee1 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/dom/isExitLink.spec.js @@ -0,0 +1,42 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isExitLink from "../../../../../../../src/components/ActivityCollector/utils/dom/isExitLink.js"; + +describe("ActivityCollector::isExitLink", () => { + it("Returns true if the link leads away from the current hostname", () => { + const mockWindow = { + location: { + hostname: "adobe.com", + }, + }; + const clickedLinks = [ + "https://example.com", + "http://example.com/index.html", + ]; + clickedLinks.forEach((clickedLink) => { + expect(isExitLink(mockWindow, clickedLink)).toBe(true); + }); + }); + it("Returns false if the link leads to the current hostname", () => { + const mockWindow = { + location: { + hostname: "adobe.com", + }, + }; + const clickedLinks = ["https://adobe.com", "http://adobe.com/index.html"]; + clickedLinks.forEach((clickedLink) => { + expect(isExitLink(mockWindow, clickedLink)).toBe(false); + }); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/isInputSubmitElement.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/dom/isInputSubmitElement.spec.js new file mode 100644 index 000000000..0b4b36af5 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/dom/isInputSubmitElement.spec.js @@ -0,0 +1,37 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isInputSubmitElement from "../../../../../../../src/components/ActivityCollector/utils/dom/isInputSubmitElement.js"; + +describe("ActivityCollector::isInputSubmitElement", () => { + it("should return true for submit input", () => { + const input = document.createElement("input"); + input.type = "submit"; + expect(isInputSubmitElement(input)).toBe(true); + }); + it("should return true for image input", () => { + const input = document.createElement("input"); + input.type = "image"; + input.src = "https://example.com/image.png"; + expect(isInputSubmitElement(input)).toBe(true); + }); + it("should return false for non-submit input", () => { + const input = document.createElement("input"); + input.type = "text"; + expect(isInputSubmitElement(input)).toBe(false); + }); + it("should return false for non-input element", () => { + const div = document.createElement("div"); + expect(isInputSubmitElement(div)).toBe(false); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/isSupportedAnchorElement.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/dom/isSupportedAnchorElement.spec.js new file mode 100644 index 000000000..95cc2ba93 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/dom/isSupportedAnchorElement.spec.js @@ -0,0 +1,56 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isSupportedAnchorElement from "../../../../../../../src/components/ActivityCollector/utils/dom/isSupportedAnchorElement.js"; + +describe("ActivityCollector::isSupportedAnchorElement", () => { + it("Returns true for supported anchor elements", () => { + const validAnchorElements = [ + { + href: "http://example.com", + tagName: "A", + }, + { + href: "http://example.com", + tagName: "AREA", + }, + ]; + validAnchorElements.forEach((element) => { + expect(isSupportedAnchorElement(element)).toBe(true); + }); + }); + it("Returns false for unsupported anchor elements", () => { + const invalidAnchorElements = [ + {}, + { + href: "", + }, + { + href: "http://example.com", + }, + { + href: "http://example.com", + tagName: "LINK", + }, + { + href: "http://example.com", + tagName: "A", + onclick: "example();", + protocol: " javascript:", + }, + ]; + invalidAnchorElements.forEach((element) => { + expect(isSupportedAnchorElement(element)).toBe(false); + }); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/isSupportedTextNode.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/dom/isSupportedTextNode.spec.js new file mode 100644 index 000000000..656e15a3e --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/dom/isSupportedTextNode.spec.js @@ -0,0 +1,41 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isSupportedTextNode from "../../../../../../../src/components/ActivityCollector/utils/dom/isSupportedTextNode.js"; + +describe("ActivityCollector::isSupportedTextNode", () => { + it("should return true for text node", () => { + const textNode = document.createTextNode("text"); + expect(isSupportedTextNode(textNode)).toBe(true); + }); + it("should return false for comment node", () => { + const commentNode = document.createComment("comment"); + expect(isSupportedTextNode(commentNode)).toBe(false); + }); + it("should return true for a paragraph node", () => { + const paragraphNode = document.createElement("p"); + expect(isSupportedTextNode(paragraphNode)).toBe(true); + }); + it("should return false for a script node", () => { + const scriptNode = document.createElement("script"); + expect(isSupportedTextNode(scriptNode)).toBe(false); + }); + it("should return false for a style node", () => { + const styleNode = document.createElement("style"); + expect(isSupportedTextNode(styleNode)).toBe(false); + }); + it("should return false for a link node", () => { + const linkNode = document.createElement("link"); + expect(isSupportedTextNode(linkNode)).toBe(false); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/hasPageName.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/hasPageName.spec.js new file mode 100644 index 000000000..f20cca6bc --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/hasPageName.spec.js @@ -0,0 +1,41 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import hasPageName from "../../../../../../src/components/ActivityCollector/utils/hasPageName.js"; + +describe("ActivityCollector::hasPageName", () => { + it("should return true if event has page name", () => { + const event = { + getContent: () => ({ + xdm: { + web: { + webPageDetails: { + name: "test", + }, + }, + }, + }), + }; + expect(hasPageName(event)).toBe(true); + }); + it("should return false if event does not have page name", () => { + const event = { + getContent: () => ({ + xdm: { + web: {}, + }, + }), + }; + expect(hasPageName(event)).toBe(false); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/isDifferentDomains.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/isDifferentDomains.spec.js new file mode 100644 index 000000000..c3f06868f --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/isDifferentDomains.spec.js @@ -0,0 +1,28 @@ +/* +Copyright 2024 example. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isDifferentDomains from "../../../../../../src/components/ActivityCollector/utils/isDifferentDomains.js"; + +describe("ActivityCollector::isDifferentDomains", () => { + it("should return true if the domains are different", () => { + expect(isDifferentDomains("www.example.com", "www.example.org")).toBe(true); + }); + it("should return false if the domains are the same", () => { + expect( + isDifferentDomains("https://www.example.com", "www.example.com"), + ).toBe(false); + expect(isDifferentDomains("www.example.com", "www.example.com")).toBe( + false, + ); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/trimQueryFromUrl.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/trimQueryFromUrl.spec.js new file mode 100644 index 000000000..6c336f88e --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/trimQueryFromUrl.spec.js @@ -0,0 +1,32 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import trimQueryFromUrl from "../../../../../../src/components/ActivityCollector/utils/trimQueryFromUrl.js"; + +describe("ActivityCollector::trimQueryFromUrl", () => { + it("Removes query portion from URL", () => { + const urls = [ + ["http://example.com", "http://example.com"], + [ + "https://example.com:123/example?example=123", + "https://example.com:123/example", + ], + ["file://example.txt", "file://example.txt"], + ["http://example.com/?example=123", "http://example.com/"], + ["http://example.com/#example", "http://example.com/"], + ]; + urls.forEach((url) => { + expect(trimQueryFromUrl(url[0])).toBe(url[1]); + }); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/truncateWhiteSpace.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/truncateWhiteSpace.spec.js new file mode 100644 index 000000000..b383cda90 --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/truncateWhiteSpace.spec.js @@ -0,0 +1,32 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import truncateWhiteSpace from "../../../../../../src/components/ActivityCollector/utils/truncateWhiteSpace.js"; + +describe("ActivityCollector::truncateWhiteSpace", () => { + it("it trims leading and trailing white spaces and limits contained white space to one character", () => { + const testCases = [ + [" hello world ", "hello world"], + [" hello world ", "hello world"], + ["hello world", "hello world"], + ["hello world ", "hello world"], + [" hello world", "hello world"], + ["", ""], + [" ", ""], + [" ", ""], + ]; + testCases.forEach((testCase) => { + expect(truncateWhiteSpace(testCase[0])).toBe(testCase[1]); + }); + }); +}); diff --git a/vtest/unit/specs/components/ActivityCollector/utils/urlStartsWithScheme.spec.js b/vtest/unit/specs/components/ActivityCollector/utils/urlStartsWithScheme.spec.js new file mode 100644 index 000000000..269ac718b --- /dev/null +++ b/vtest/unit/specs/components/ActivityCollector/utils/urlStartsWithScheme.spec.js @@ -0,0 +1,42 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import urlStartsWithScheme from "../../../../../../src/components/ActivityCollector/utils/urlStartsWithScheme.js"; + +describe("ActivityCollector::urlStartsWithScheme", () => { + it("Returns true for URLs that starts with a scheme", () => { + const urlsThatStartsWithScheme = [ + "http://example.com", + "https://example.com", + "https://example.com:123/example?example=123", + "file://example.txt", + ]; + urlsThatStartsWithScheme.forEach((url) => { + expect(urlStartsWithScheme(url)).toBe(true); + }); + }); + it("Returns false for URLs that does not start with a scheme", () => { + const urlsThatDoesNotStartWithScheme = [ + "example.com", + "example.txt/http://example", + "https:", + "//example.html", + "", + null, + undefined, + ]; + urlsThatDoesNotStartWithScheme.forEach((url) => { + expect(urlStartsWithScheme(url)).toBe(false); + }); + }); +}); diff --git a/vtest/unit/specs/components/Audiences/injectProcessDestinations.spec.js b/vtest/unit/specs/components/Audiences/injectProcessDestinations.spec.js new file mode 100644 index 000000000..24d9baf12 --- /dev/null +++ b/vtest/unit/specs/components/Audiences/injectProcessDestinations.spec.js @@ -0,0 +1,172 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectProcessDestinations from "../../../../../src/components/Audiences/injectProcessDestinations.js"; + +describe("Audiences::injectProcessDestinations", () => { + let fireReferrerHideableImage; + let cookieJar; + let logger; + let isPageSsl; + let processDestinations; + beforeEach(() => { + fireReferrerHideableImage = vi.fn().mockReturnValue(Promise.resolve()); + logger = { + info: vi.fn(), + error: vi.fn(), + }; + cookieJar = { + set: vi.fn(), + }; + isPageSsl = true; + }); + const inject = () => { + processDestinations = injectProcessDestinations({ + fireReferrerHideableImage, + logger, + cookieJar, + isPageSsl, + }); + }; + const SAMPLE_DESTINATIONS1 = [ + { + type: "url", + id: 2097728, + spec: { + url: "http://test.abc", + hideReferrer: true, + }, + }, + { + type: "cookie", + spec: { + name: "audlabcookie", + value: "dgtest\u003ddevicegraphtestdestination1", + }, + }, + { + type: "cookie", + spec: { + name: "testCookieDestination", + value: "destination\u003ds2", + domain: "adobe.com", + ttlDays: 30, + }, + }, + ]; + it("sets cookie destinations", () => { + inject(); + return processDestinations(SAMPLE_DESTINATIONS1).then(() => { + expect(cookieJar.set).toHaveBeenCalledWith( + "audlabcookie", + "dgtest\u003ddevicegraphtestdestination1", + { + domain: "", + expires: 10, + sameSite: "none", + secure: true, + }, + ); + expect(cookieJar.set).toHaveBeenCalledWith( + "testCookieDestination", + "destination\u003ds2", + { + domain: "adobe.com", + expires: 30, + sameSite: "none", + secure: true, + }, + ); + }); + }); + it("sets cookie destinations without sameSite flag", () => { + isPageSsl = false; + inject(); + return processDestinations(SAMPLE_DESTINATIONS1).then(() => { + expect(cookieJar.set).toHaveBeenCalledWith( + "audlabcookie", + "dgtest\u003ddevicegraphtestdestination1", + { + domain: "", + expires: 10, + }, + ); + expect(cookieJar.set).toHaveBeenCalledWith( + "testCookieDestination", + "destination\u003ds2", + { + domain: "adobe.com", + expires: 30, + }, + ); + }); + }); + it("calls fireReferrerHideableImage for all destinations of type URL and logs results", () => { + inject(); + fireReferrerHideableImage.mockImplementation(({ url }) => { + return url === "http://test.zyx" ? Promise.resolve() : Promise.reject(); + }); + return processDestinations([ + { + type: "url", + id: 2097728, + spec: { + url: "http://test.abc", + hideReferrer: true, + }, + }, + { + type: "cookie", + spec: { + name: "testCookieDestination", + value: "destination\u003ds2", + domain: "", + ttlDays: 30, + }, + }, + { + type: "url", + id: 2097729, + spec: { + url: "http://test.zyx", + hideReferrer: false, + }, + }, + ]).then(() => { + expect(fireReferrerHideableImage).toHaveBeenCalledWith({ + url: "http://test.abc", + hideReferrer: true, + }); + expect(fireReferrerHideableImage).toHaveBeenCalledWith({ + url: "http://test.zyx", + hideReferrer: false, + }); + expect(logger.info).toHaveBeenCalledWith( + "URL destination succeeded: http://test.zyx", + ); + }); + }); + it("doesn't return a value", () => { + inject(); + const destinations = [ + { + type: "url", + id: 2097728, + spec: { + url: "http://test.abc", + hideReferrer: true, + }, + }, + ]; + return expect(processDestinations(destinations)).resolves.toBe(undefined); + }); +}); diff --git a/vtest/unit/specs/components/Audiences/injectProcessResponse.spec.js b/vtest/unit/specs/components/Audiences/injectProcessResponse.spec.js new file mode 100644 index 000000000..afb0e1343 --- /dev/null +++ b/vtest/unit/specs/components/Audiences/injectProcessResponse.spec.js @@ -0,0 +1,50 @@ +/* +Copyright 2021 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectProcessResponse from "../../../../../src/components/Audiences/injectProcessResponse.js"; + +describe("injectProcessResponse", () => { + let response; + let processResponse; + let processDestinations; + beforeEach(() => { + processDestinations = vi.fn().mockReturnValue(Promise.resolve()); + processResponse = injectProcessResponse({ + processDestinations, + }); + response = { + getPayloadsByType: vi.fn().mockReturnValue(["An Edge Destination"]), + }; + }); + it("fetches destinations from the response", () => { + return processResponse({ + response, + }).then((result) => { + expect(processDestinations).toHaveBeenCalled(); + expect(result).toEqual({ + destinations: ["An Edge Destination"], + }); + }); + }); + it("returns [] if no destinations were found", () => { + const responseWithNoDestinations = { + getPayloadsByType: vi.fn().mockReturnValue([]), + }; + return processResponse({ + response: responseWithNoDestinations, + }).then((result) => { + expect(result).toEqual({ + destinations: [], + }); + }); + }); +}); diff --git a/vtest/unit/specs/components/Consent/computeConsentHash.spec.js b/vtest/unit/specs/components/Consent/computeConsentHash.spec.js new file mode 100644 index 000000000..bb0e034d2 --- /dev/null +++ b/vtest/unit/specs/components/Consent/computeConsentHash.spec.js @@ -0,0 +1,118 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import computeConsentHash from "../../../../../src/components/Consent/computeConsentHash.js"; + +describe("computeConsentHash", () => { + it("works", () => { + expect( + computeConsentHash([ + { + standard: "Adobe", + version: "1.0", + value: { + general: "in", + }, + }, + ]), + ).toBe(2905535662); + }); + [ + [ + { + a: 1, + b: 2, + }, + { + b: 2, + a: 1, + }, + ], + [ + [ + { + a: 1, + b: 2, + }, + ], + [ + { + b: 2, + a: 1, + }, + ], + ], + [ + { + a: { + b: 2, + c: 3, + }, + }, + { + a: { + c: 3, + b: 2, + }, + }, + ], + [ + { + a: [1], + b: [2], + }, + { + b: [2], + a: [1], + }, + ], + [ + { + a: undefined, + }, + {}, + ], + ].forEach(([a, b], index) => { + it(`computes the same hash ${index}`, () => { + expect(computeConsentHash(a)).toBe(computeConsentHash(b)); + }); + }); + [ + [ + [1, 2], + [2, 1], + ], + ["1", 1], + [ + { + a: null, + }, + { + a: undefined, + }, + ], + [ + { + "xdm:key": "value", + }, + { + xdm: "key:value", + }, + ], + [null, {}], + ].forEach(([a, b], index) => { + it(`computes a different hash ${index}`, () => { + expect(computeConsentHash(a)).not.toBe(computeConsentHash(b)); + }); + }); +}); diff --git a/vtest/unit/specs/components/Consent/configValidators.spec.js b/vtest/unit/specs/components/Consent/configValidators.spec.js new file mode 100644 index 000000000..9ae9642a6 --- /dev/null +++ b/vtest/unit/specs/components/Consent/configValidators.spec.js @@ -0,0 +1,92 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import configValidators from "../../../../../src/components/Consent/configValidators.js"; + +describe("defaultConsent", () => { + it("validates defaultConsent=undefined", () => { + const config = configValidators({}); + expect(config.defaultConsent).toEqual("in"); + }); + it("validates defaultConsent={}", () => { + expect(() => { + configValidators({ + defaultConsent: {}, + }); + }).toThrowError(); + }); + it("validates defaultConsent='in'", () => { + const config = configValidators({ + defaultConsent: "in", + }); + expect(config.defaultConsent).toEqual("in"); + }); + it("validates defaultConsent='pending'", () => { + const config = configValidators({ + defaultConsent: "pending", + }); + expect(config.defaultConsent).toEqual("pending"); + }); + it("validates defaultConsent=123", () => { + expect(() => { + configValidators({ + defaultConsent: 123, + }); + }).toThrowError(); + }); + it("validates defaultConsent='out'", () => { + const config = configValidators({ + defaultConsent: "out", + }); + expect(config.defaultConsent).toEqual("out"); + }); +}); + +describe("defaultConsent", () => { + it("validates defaultConsent=undefined", () => { + const config = configValidators({}); + expect(config.defaultConsent).toEqual("in"); + }); + it("validates defaultConsent={}", () => { + expect(() => { + configValidators({ + defaultConsent: {}, + }); + }).toThrowError(); + }); + it("validates defaultConsent='in'", () => { + const config = configValidators({ + defaultConsent: "in", + }); + expect(config.defaultConsent).toEqual("in"); + }); + it("validates defaultConsent='pending'", () => { + const config = configValidators({ + defaultConsent: "pending", + }); + expect(config.defaultConsent).toEqual("pending"); + }); + it("validates defaultConsent=123", () => { + expect(() => { + configValidators({ + defaultConsent: 123, + }); + }).toThrowError(); + }); + it("validates defaultConsent='out'", () => { + const config = configValidators({ + defaultConsent: "out", + }); + expect(config.defaultConsent).toEqual("out"); + }); +}); diff --git a/vtest/unit/specs/components/Consent/createComponent.spec.js b/vtest/unit/specs/components/Consent/createComponent.spec.js new file mode 100644 index 000000000..cba7c13c2 --- /dev/null +++ b/vtest/unit/specs/components/Consent/createComponent.spec.js @@ -0,0 +1,346 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createComponent from "../../../../../src/components/Consent/createComponent.js"; +import { createTaskQueue, defer } from "../../../../../src/utils/index.js"; +import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; + +const createConsent = (generalConsent) => ({ + consent: [ + { + standard: "Adobe", + version: "1.0", + value: { + general: generalConsent, + }, + }, + ], +}); +const CONSENT_IN = createConsent("in"); +const CONSENT_OUT = createConsent("out"); +describe("consent:createComponent", () => { + let storedConsent; + let taskQueue; + let defaultConsent; + let consent; + let sendSetConsentRequest; + let validateSetConsentOptions; + let consentHashStore; + let consentHashes; + let doesIdentityCookieExist; + let requestFailureError; + let component; + let config; + const setIdentityCookie = () => { + doesIdentityCookieExist.mockReturnValue(true); + }; + const clearIdentityCookie = () => { + doesIdentityCookieExist.mockReturnValue(false); + }; + beforeEach(() => { + storedConsent = { + read: vi.fn(), + clear: vi.fn(), + }; + taskQueue = createTaskQueue(); + defaultConsent = "in"; + consent = { + initializeConsent: vi.fn(), + setConsent: vi.fn(), + suspend: vi.fn(), + }; + sendSetConsentRequest = vi.fn(); + validateSetConsentOptions = vi + .fn() + .mockImplementation((options) => options); + consentHashStore = { + clear: vi.fn(), + lookup: vi.fn(), + }; + consentHashes = { + isNew: vi.fn(), + save: vi.fn(), + }; + doesIdentityCookieExist = vi.fn(); + consentHashStore.lookup.mockReturnValue(consentHashes); + setIdentityCookie(); + consentHashes.isNew.mockReturnValue(true); + requestFailureError = new Error("Request for setting test consent failed."); + config = { + edgeConfigOverrides: {}, + }; + }); + const build = () => { + component = createComponent({ + storedConsent, + taskQueue, + defaultConsent, + consent, + sendSetConsentRequest, + validateSetConsentOptions, + consentHashStore, + doesIdentityCookieExist, + config, + }); + }; + const clearConsentCookie = function clearConsentCookie() { + storedConsent.read.mockReturnValue({}); + }; + const setConsentCookieIn = function setConsentCookieIn() { + storedConsent.read.mockReturnValue({ + general: "in", + }); + }; + const setConsentCookieOut = function setConsentCookieOut() { + storedConsent.read.mockReturnValue({ + general: "out", + }); + }; + const mockSetConsent = () => { + const deferred = defer(); + sendSetConsentRequest.mockReturnValue(deferred.promise); + // ensure that there will be no uncaught promise rejections + deferred.promise.catch(() => {}); + return { + respondWithIn() { + setConsentCookieIn(); + deferred.resolve(); + }, + respondWithOut() { + setConsentCookieOut(); + deferred.resolve(); + }, + respondWithNoCookie() { + deferred.resolve(); + }, + respondWithError() { + deferred.reject(requestFailureError); + }, + }; + }; + it("initializes consent", () => { + defaultConsent = "mydefaultconsent"; + storedConsent.read.mockReturnValue({ + general: "myinitialconsent", + }); + build(); + expect(consent.initializeConsent).toHaveBeenCalledWith( + { + general: "mydefaultconsent", + }, + { + general: "myinitialconsent", + }, + ); + }); + it("handles the setConsent command", () => { + defaultConsent = "pending"; + clearConsentCookie(); + build(); + const setConsentMock = mockSetConsent(); + const onResolved = vi.fn(); + component.commands.setConsent + .run({ + identityMap: { + my: "map", + }, + ...CONSENT_IN, + }) + .then(onResolved); + expect(consent.suspend).toHaveBeenCalled(); + setConsentMock.respondWithIn(); + return flushPromiseChains().then(() => { + expect(sendSetConsentRequest).toHaveBeenCalledWith( + expect.objectContaining({ + consentOptions: CONSENT_IN.consent, + identityMap: { + my: "map", + }, + }), + ); + expect(consent.setConsent).toHaveBeenCalledWith({ + general: "in", + }); + expect(onResolved).toHaveBeenCalledWith(undefined); + }); + }); + it("handles the setConsent command with overrides, if provided", () => { + defaultConsent = "pending"; + clearConsentCookie(); + build(); + const setConsentMock = mockSetConsent(); + const onResolved = vi.fn(); + component.commands.setConsent + .run({ + identityMap: { + my: "map", + }, + edgeConfigOverrides: { + com_adobe_identity: { + idSyncContainerId: "1234", + }, + }, + ...CONSENT_IN, + }) + .then(onResolved); + expect(consent.suspend).toHaveBeenCalled(); + setConsentMock.respondWithIn(); + return flushPromiseChains().then(() => { + expect(sendSetConsentRequest).toHaveBeenCalledWith({ + consentOptions: CONSENT_IN.consent, + identityMap: { + my: "map", + }, + edgeConfigOverrides: { + com_adobe_identity: { + idSyncContainerId: "1234", + }, + }, + }); + expect(consent.setConsent).toHaveBeenCalledWith({ + general: "in", + }); + expect(onResolved).toHaveBeenCalledWith(undefined); + }); + }); + it("updates the consent object even after a request failure", () => { + defaultConsent = "pending"; + clearConsentCookie(); + build(); + const setConsentMock = mockSetConsent(); + const onRejected = vi.fn(); + component.commands.setConsent.run(CONSENT_IN).catch(onRejected); + setConsentCookieIn(); + setConsentMock.respondWithError(); + return flushPromiseChains().then(() => { + expect(consent.setConsent).toHaveBeenCalledWith({ + general: "in", + }); + expect(onRejected).toHaveBeenCalledWith(requestFailureError); + }); + }); + it("only updates the consent object after the response returns", () => { + defaultConsent = "pending"; + clearConsentCookie(); + build(); + const setConsentMock = mockSetConsent(); + component.commands.setConsent.run(CONSENT_IN); + return flushPromiseChains() + .then(() => { + expect(sendSetConsentRequest).toHaveBeenCalledWith( + expect.objectContaining({ + consentOptions: CONSENT_IN.consent, + }), + ); + expect(consent.setConsent).not.toHaveBeenCalledWith({ + general: "in", + }); + setConsentMock.respondWithIn(); + return flushPromiseChains(); + }) + .then(() => { + expect(consent.setConsent).toHaveBeenCalledWith({ + general: "in", + }); + }); + }); + it("only calls setConsent once with multiple consent requests", () => { + defaultConsent = "pending"; + clearConsentCookie(); + consentHashes.isNew.mockReturnValue(true); + build(); + const setConsentMock1 = mockSetConsent(); + let setConsentMock2; + component.commands.setConsent.run(CONSENT_IN); + return flushPromiseChains() + .then(() => { + expect(sendSetConsentRequest).toHaveBeenCalledWith( + expect.objectContaining({ + consentOptions: CONSENT_IN.consent, + }), + ); + setConsentMock2 = mockSetConsent(); + component.commands.setConsent.run(CONSENT_OUT); + setConsentMock1.respondWithIn(); + return flushPromiseChains(); + }) + .then(() => { + expect(sendSetConsentRequest).toHaveBeenCalledWith( + expect.objectContaining({ + consentOptions: CONSENT_OUT.consent, + }), + ); + setConsentMock2.respondWithOut(); + return flushPromiseChains(); + }) + .then(() => { + expect(consent.setConsent).not.toHaveBeenCalledWith({ + general: "in", + }); + expect(consent.setConsent).toHaveBeenCalledTimes(1); + expect(consent.setConsent).toHaveBeenCalledWith({ + general: "out", + }); + }); + }); + it("checks the cookie after an event", () => { + clearConsentCookie(); + build(); + setConsentCookieOut(); + component.lifecycle.onResponse(); + expect(consent.setConsent).toHaveBeenCalledWith({ + general: "out", + }); + }); + it("checks the cookie after an error response", () => { + clearConsentCookie(); + build(); + setConsentCookieOut(); + component.lifecycle.onRequestFailure(); + expect(consent.setConsent).toHaveBeenCalledWith({ + general: "out", + }); + }); + it("clears storage when the identity cookie is missing", () => { + setConsentCookieIn(); + clearIdentityCookie(); + build(); + expect(consentHashStore.clear).toHaveBeenCalled(); + expect(storedConsent.clear).toHaveBeenCalled(); + expect(consent.initializeConsent).toHaveBeenCalledWith( + { + general: "in", + }, + {}, + ); + }); + it("clears storage when the consent cookie is missing", () => { + clearConsentCookie(); + setIdentityCookie(); + build(); + expect(consentHashStore.clear).toHaveBeenCalled(); + expect(storedConsent.clear).not.toHaveBeenCalled(); + }); + it("doesn't call setConsent when there is no cookie after onResponse", () => { + clearConsentCookie(); + build(); + component.lifecycle.onResponse(); + expect(consent.setConsent).not.toHaveBeenCalled(); + }); + it("doesn't call setConsent when there is no cookie after onRequestFailure", () => { + clearConsentCookie(); + build(); + component.lifecycle.onRequestFailure(); + expect(consent.setConsent).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Consent/createConsentHashStore.spec.js b/vtest/unit/specs/components/Consent/createConsentHashStore.spec.js new file mode 100644 index 000000000..1e8b374e2 --- /dev/null +++ b/vtest/unit/specs/components/Consent/createConsentHashStore.spec.js @@ -0,0 +1,69 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createConsentHashStore from "../../../../../src/components/Consent/createConsentHashStore.js"; + +const CONSENT_IN = { + standard: "Adobe", + version: "1.0", + value: { + general: "in", + }, +}; +const CONSENT_OUT = { + standard: "Adobe", + version: "1.0", + value: { + general: "out", + }, +}; +describe("createConsentHashStore", () => { + let storage; + let subject; + beforeEach(() => { + storage = { + getItem: vi.fn(), + setItem: vi.fn(), + clear: vi.fn(), + }; + subject = createConsentHashStore({ + storage, + }); + }); + it("clears", () => { + subject.clear(); + expect(storage.clear).toHaveBeenCalled(); + }); + it("is new when storage is empty", () => { + storage.getItem.mockReturnValue(null); + const consentHashes = subject.lookup([CONSENT_IN]); + expect(consentHashes.isNew()).toBe(true); + }); + it("saves the hash", () => { + const consentHashes = subject.lookup([CONSENT_IN]); + consentHashes.save(); + expect(storage.setItem).toHaveBeenCalledWith("Adobe.1.0", "3165644325"); + }); + it("is not new when lookup is the same", () => { + storage.getItem.mockReturnValue("3165644325"); + const consentHashes = subject.lookup([CONSENT_IN]); + expect(consentHashes.isNew()).toBe(false); + expect(storage.getItem).toHaveBeenCalledWith("Adobe.1.0"); + }); + it("is new when lookup is different", () => { + storage.getItem.mockReturnValue("3165644325"); + const consentHashes = subject.lookup([CONSENT_OUT]); + expect(consentHashes.isNew()).toBe(true); + expect(storage.getItem).toHaveBeenCalledWith("Adobe.1.0"); + }); +}); diff --git a/vtest/unit/specs/components/Consent/createConsentRequest.spec.js b/vtest/unit/specs/components/Consent/createConsentRequest.spec.js new file mode 100644 index 000000000..bba62e842 --- /dev/null +++ b/vtest/unit/specs/components/Consent/createConsentRequest.spec.js @@ -0,0 +1,42 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import describeRequest from "../../../helpers/describeRequest.js"; +import createConsentRequest from "../../../../../src/components/Consent/createConsentRequest.js"; + +describe("createConsentRequest", () => { + describeRequest(createConsentRequest); + it("provides the appropriate action", () => { + const payload = {}; + const request = createConsentRequest({ + payload, + }); + expect(request.getAction()).toBe("privacy/set-consent"); + }); + it("never uses sendBeacon", () => { + const payload = {}; + const request = createConsentRequest({ + payload, + }); + expect(request.getUseSendBeacon()).toBe(false); + }); + it("passes the datastreamIdOverride to the request", () => { + const payload = {}; + const datastreamIdOverride = "my-edge-config-id-override"; + const request = createConsentRequest({ + payload, + datastreamIdOverride, + }); + expect(request.getDatastreamIdOverride()).toBe(datastreamIdOverride); + }); +}); diff --git a/vtest/unit/specs/components/Consent/createConsentRequestPayload.spec.js b/vtest/unit/specs/components/Consent/createConsentRequestPayload.spec.js new file mode 100644 index 000000000..82001fc5a --- /dev/null +++ b/vtest/unit/specs/components/Consent/createConsentRequestPayload.spec.js @@ -0,0 +1,63 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import createConsentRequestPayload from "../../../../../src/components/Consent/createConsentRequestPayload.js"; +import describeRequestPayload from "../../../helpers/describeRequestPayload.js"; + +describe("createConsentRequestPayload", () => { + describeRequestPayload(createConsentRequestPayload); + it("adds an identity", () => { + const payload = createConsentRequestPayload(); + payload.addIdentity("IDNS", { + id: "ABC123", + }); + payload.addIdentity("IDNS", { + id: "DEF456", + }); + expect(JSON.parse(JSON.stringify(payload))).toEqual({ + identityMap: { + IDNS: [ + { + id: "ABC123", + }, + { + id: "DEF456", + }, + ], + }, + }); + }); + it("sets consent", () => { + const payload = createConsentRequestPayload(); + payload.setConsent([ + { + standard: "Adobe", + version: "1.0", + value: { + general: "in", + }, + }, + ]); + expect(JSON.parse(JSON.stringify(payload))).toEqual({ + consent: [ + { + standard: "Adobe", + version: "1.0", + value: { + general: "in", + }, + }, + ], + }); + }); +}); diff --git a/vtest/unit/specs/components/Consent/createStoredConsent.spec.js b/vtest/unit/specs/components/Consent/createStoredConsent.spec.js new file mode 100644 index 000000000..449177989 --- /dev/null +++ b/vtest/unit/specs/components/Consent/createStoredConsent.spec.js @@ -0,0 +1,55 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createStoredConsent from "../../../../../src/components/Consent/createStoredConsent.js"; + +describe("Consent:createStoredConsent", () => { + let parseConsentCookie; + const orgId = "myorgid@mycompany"; + let cookieJar; + let storedConsent; + beforeEach(() => { + parseConsentCookie = vi.fn(); + cookieJar = { + get: vi.fn(), + remove: vi.fn(), + }; + storedConsent = createStoredConsent({ + parseConsentCookie, + orgId, + cookieJar, + }); + }); + it("gets the cookie", () => { + cookieJar.get.mockReturnValue("cookieValue"); + parseConsentCookie.mockReturnValue("parsedConsentValue"); + expect(storedConsent.read()).toEqual("parsedConsentValue"); + expect(parseConsentCookie).toHaveBeenCalledWith("cookieValue"); + }); + it("returns empty object if the cookie is not there", () => { + cookieJar.get.mockReturnValue(undefined); + expect(storedConsent.read()).toEqual({}); + expect(parseConsentCookie).not.toHaveBeenCalled(); + }); + it("uses the correct cookie name", () => { + storedConsent.read(); + expect(cookieJar.get).toHaveBeenCalledWith( + "kndctr_myorgid_mycompany_consent", + ); + }); + it("removes the cookie", () => { + storedConsent.clear(); + expect(cookieJar.remove).toHaveBeenCalledWith( + "kndctr_myorgid_mycompany_consent", + ); + }); +}); diff --git a/vtest/unit/specs/components/Consent/injectSendSetConsentRequest.spec.js b/vtest/unit/specs/components/Consent/injectSendSetConsentRequest.spec.js new file mode 100644 index 000000000..3223e3628 --- /dev/null +++ b/vtest/unit/specs/components/Consent/injectSendSetConsentRequest.spec.js @@ -0,0 +1,175 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectSendSetConsentRequest from "../../../../../src/components/Consent/injectSendSetConsentRequest.js"; + +describe("Consent:injectSendSetConsentRequest", () => { + let sendEdgeNetworkRequest; + let requestPayload; + let request; + let createConsentRequestPayload; + let createConsentRequest; + let sendSetConsentRequest; + let globalEdgeConfigOverrides; + beforeEach(() => { + sendEdgeNetworkRequest = vi.fn(); + requestPayload = { + setConsent: vi.fn(), + addIdentity: vi.fn(), + mergeConfigOverride: vi.fn(), + }; + createConsentRequestPayload = vi.fn().mockReturnValue(requestPayload); + request = { + getPayload() { + return requestPayload; + }, + }; + createConsentRequest = vi.fn().mockReturnValue(request); + globalEdgeConfigOverrides = {}; + sendSetConsentRequest = injectSendSetConsentRequest({ + createConsentRequestPayload, + createConsentRequest, + sendEdgeNetworkRequest, + edgeConfigOverrides: globalEdgeConfigOverrides, + }); + }); + it("sets consent level and on requestPayload and sends the request", () => { + sendEdgeNetworkRequest.mockReturnValue(Promise.resolve()); + return sendSetConsentRequest({ + consentOptions: "anything", + identityMap: { + a: [ + { + id: "1", + }, + { + id: "2", + }, + ], + b: [ + { + id: "3", + }, + ], + }, + }).then((resolvedValue) => { + expect(requestPayload.setConsent).toHaveBeenCalledWith("anything"); + expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ + request, + }); + expect(resolvedValue).toBeUndefined(); + expect(requestPayload.addIdentity).toHaveBeenCalledWith("a", { + id: "1", + }); + expect(requestPayload.addIdentity).toHaveBeenCalledWith("a", { + id: "2", + }); + expect(requestPayload.addIdentity).toHaveBeenCalledWith("b", { + id: "3", + }); + }); + }); + it("sets the configuration overrides on the payload, if provided", () => { + sendEdgeNetworkRequest.mockReturnValue(Promise.resolve()); + return sendSetConsentRequest({ + consentOptions: "anything", + identityMap: { + a: [ + { + id: "1", + }, + { + id: "2", + }, + ], + b: [ + { + id: "3", + }, + ], + }, + edgeConfigOverrides: { + com_adobe_identity: { + idSyncContainerId: "123", + }, + }, + }).then(() => { + expect(requestPayload.setConsent).toHaveBeenCalledWith("anything"); + expect(requestPayload.mergeConfigOverride).toHaveBeenCalledWith({ + com_adobe_identity: { + idSyncContainerId: "123", + }, + }); + }); + }); + it("sets the configuration overrides on the payload, if provided, from the global config", () => { + sendEdgeNetworkRequest.mockReturnValue(Promise.resolve()); + globalEdgeConfigOverrides.com_adobe_identity = { + idSyncContainerId: "123", + }; + return sendSetConsentRequest({ + consentOptions: "anything", + identityMap: { + a: [ + { + id: "1", + }, + { + id: "2", + }, + ], + b: [ + { + id: "3", + }, + ], + }, + }).then(() => { + expect(requestPayload.setConsent).toHaveBeenCalledWith("anything"); + expect(requestPayload.mergeConfigOverride).toHaveBeenCalledWith({ + com_adobe_identity: { + idSyncContainerId: "123", + }, + }); + }); + }); + it("sets the override for the datastreamId, if provided", () => { + sendEdgeNetworkRequest.mockReturnValue(Promise.resolve()); + return sendSetConsentRequest({ + consentOptions: "anything", + identityMap: { + a: [ + { + id: "1", + }, + { + id: "2", + }, + ], + b: [ + { + id: "3", + }, + ], + }, + edgeConfigOverrides: { + datastreamId: "123", + }, + }).then(() => { + expect(requestPayload.setConsent).toHaveBeenCalledWith("anything"); + expect(createConsentRequest).toHaveBeenCalledWith({ + payload: expect.any(Object), + datastreamIdOverride: "123", + }); + }); + }); +}); diff --git a/vtest/unit/specs/components/Consent/parseConsentCookie.spec.js b/vtest/unit/specs/components/Consent/parseConsentCookie.spec.js new file mode 100644 index 000000000..e0b479ab4 --- /dev/null +++ b/vtest/unit/specs/components/Consent/parseConsentCookie.spec.js @@ -0,0 +1,23 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import parseConsentCookie from "../../../../../src/components/Consent/parseConsentCookie.js"; + +describe("parseConsentCookie", () => { + it("returns preferences by purpose", () => { + expect(parseConsentCookie("foo=in;bar=out")).toEqual({ + foo: "in", + bar: "out", + }); + }); +}); diff --git a/vtest/unit/specs/components/Consent/validateSetConsentOptions.spec.js b/vtest/unit/specs/components/Consent/validateSetConsentOptions.spec.js new file mode 100644 index 000000000..f6238b56f --- /dev/null +++ b/vtest/unit/specs/components/Consent/validateSetConsentOptions.spec.js @@ -0,0 +1,178 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import validateSetConsentOptions from "../../../../../src/components/Consent/validateSetConsentOptions.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +const validGeneralConsent = [ + { + standard: "Adobe", + version: "1.0", + value: { + general: "in", + }, + }, +]; +describeValidation( + "Consent:validateSetConsentOptions", + validateSetConsentOptions, + [ + { + value: { + consent: [ + { + standard: "Adobe", + version: "1.0", + value: { + general: "in", + }, + }, + ], + }, + }, + { + value: { + consent: [], + }, + error: true, + }, + { + value: { + consent: null, + }, + error: true, + }, + { + value: { + consent: undefined, + }, + error: true, + }, + { + value: "in", + error: true, + }, + { + value: undefined, + error: true, + }, + { + value: null, + error: true, + }, + { + value: { + consent: [ + { + standard: "IAB", + version: "2.0", + value: "1234abcd", + gdprApplies: true, + }, + ], + }, + }, + { + value: { + consent: [ + { + standard: "IAB", + version: "2.0", + value: "1234abcd", + gdprApplies: true, + }, + { + standard: "Adobe", + version: "1.0", + value: { + general: "in", + }, + }, + ], + }, + }, + { + value: { + consent: validGeneralConsent, + identityMap: { + HYP: [{}], + }, + }, + }, + { + value: { + consent: validGeneralConsent, + identityMap: { + HYP: [ + { + id: "1234", + authenticatedState: "ambiguous", + }, + ], + }, + }, + }, + { + value: { + consent: validGeneralConsent, + identityMap: { + HYP: [ + { + blah: "1234", + }, + ], + }, + }, + error: true, + }, + { + value: { + consent: validGeneralConsent, + identityMap: [], + }, + error: true, + }, + { + value: { + consent: validGeneralConsent, + identityMap: { + email: [], + }, + }, + }, + { + value: { + consent: validGeneralConsent, + identityMap: { + email: [[]], + }, + }, + error: true, + }, + { + value: { + consent: validGeneralConsent, + edgeConfigOverrides: { + identity: { + idSyncContainerId: "123", + }, + }, + }, + }, + { + value: { + consent: validGeneralConsent, + edgeConfigOverrides: {}, + }, + }, + ], +); diff --git a/vtest/unit/specs/components/Context/createComponent.spec.js b/vtest/unit/specs/components/Context/createComponent.spec.js new file mode 100644 index 000000000..261a7bc20 --- /dev/null +++ b/vtest/unit/specs/components/Context/createComponent.spec.js @@ -0,0 +1,86 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createComponent from "../../../../../src/components/Context/createComponent.js"; +import createConfig from "../../../../../src/core/config/createConfig.js"; + +describe("Context::createComponent", () => { + const logger = { + log() {}, + warn() {}, + }; + const context1 = (xdm) => { + xdm.a = "1"; + }; + const context2 = (xdm) => { + xdm.b = "2"; + }; + const requiredContext = (xdm) => { + xdm.c = "3"; + }; + const availableContexts = { + context1, + context2, + }; + let event; + beforeEach(() => { + event = { + mergeXdm: vi.fn(), + }; + }); + it("enables the configured contexts", async () => { + const config = createConfig({ + context: ["context1", "context2"], + }); + const component = createComponent(config, logger, availableContexts, [ + requiredContext, + ]); + await component.lifecycle.onBeforeEvent({ + event, + }); + expect(event.mergeXdm).toHaveBeenCalledWith({ + a: "1", + b: "2", + c: "3", + }); + }); + it("ignores unknown contexts", async () => { + const config = createConfig({ + context: ["unknowncontext", "context1"], + }); + const component = createComponent(config, logger, availableContexts, [ + requiredContext, + ]); + await component.lifecycle.onBeforeEvent({ + event, + }); + expect(event.mergeXdm).toHaveBeenCalledWith({ + a: "1", + c: "3", + }); + }); + it("can disable non-required contexts", async () => { + const config = createConfig({ + context: [], + }); + const component = createComponent(config, logger, availableContexts, [ + requiredContext, + ]); + await component.lifecycle.onBeforeEvent({ + event, + }); + expect(event.mergeXdm).toHaveBeenCalledWith({ + c: "3", + }); + }); +}); diff --git a/vtest/unit/specs/components/Context/implementationDetails.spec.js b/vtest/unit/specs/components/Context/implementationDetails.spec.js new file mode 100644 index 000000000..2bd0b2c41 --- /dev/null +++ b/vtest/unit/specs/components/Context/implementationDetails.spec.js @@ -0,0 +1,28 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import implementationDetails from "../../../../../src/components/Context/implementationDetails.js"; + +describe("Context::implementationDetails", () => { + it("works", () => { + const xdm = {}; + implementationDetails(xdm); + expect(xdm).toEqual({ + implementationDetails: { + name: "https://ns.adobe.com/experience/alloy", + version: "__VERSION__", + environment: "browser", + }, + }); + }); +}); diff --git a/vtest/unit/specs/components/Context/injectDevice.spec.js b/vtest/unit/specs/components/Context/injectDevice.spec.js new file mode 100644 index 000000000..6d3ca5155 --- /dev/null +++ b/vtest/unit/specs/components/Context/injectDevice.spec.js @@ -0,0 +1,130 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, describe, it, expect } from "vitest"; +import injectDevice from "../../../../../src/components/Context/injectDevice.js"; + +describe("Context::injectDevice", () => { + let window; + beforeEach(() => { + window = { + screen: { + width: 600, + height: 800, + }, + }; + }); + const run = () => { + const xdm = {}; + injectDevice(window)(xdm); + return xdm; + }; + it("handles the happy path", () => { + window.screen.orientation = { + type: "landscape-primary", + }; + expect(run()).toEqual({ + device: { + screenHeight: 800, + screenWidth: 600, + screenOrientation: "landscape", + }, + }); + }); + it("handles portrait orientation type", () => { + window.screen.orientation = { + type: "portrait-secondary", + }; + expect(run()).toEqual({ + device: { + screenHeight: 800, + screenWidth: 600, + screenOrientation: "portrait", + }, + }); + }); + it("handles matchMedia queries: portrait", () => { + window.matchMedia = (query) => ({ + matches: query === "(orientation: portrait)", + }); + expect(run()).toEqual({ + device: { + screenHeight: 800, + screenWidth: 600, + screenOrientation: "portrait", + }, + }); + }); + it("handles matchMedia queries: landscape", () => { + window.matchMedia = (query) => ({ + matches: query === "(orientation: landscape)", + }); + expect(run()).toEqual({ + device: { + screenHeight: 800, + screenWidth: 600, + screenOrientation: "landscape", + }, + }); + }); + it("handles string values for the height and width", () => { + window = { + screen: { + width: "600", + height: "800", + }, + }; + expect(run()).toEqual({ + device: { + screenHeight: 800, + screenWidth: 600, + }, + }); + }); + it("handles no good values", () => { + window = { + screen: { + width: null, + height: undefined, + }, + }; + expect(run()).toEqual({}); + }); + [ + undefined, + null, + {}, + { + type: "foo", + }, + { + type: "a-b", + }, + { + type: null, + }, + ].forEach((orientation) => { + it(`handles a bad screen orientation: ${JSON.stringify(orientation)}`, () => { + if (orientation !== undefined) { + window.screen.orientation = orientation; + } + window.matchMedia = () => ({ + matches: false, + }); + expect(run()).toEqual({ + device: { + screenHeight: 800, + screenWidth: 600, + }, + }); + }); + }); +}); diff --git a/vtest/unit/specs/components/Context/injectEnvironment.spec.js b/vtest/unit/specs/components/Context/injectEnvironment.spec.js new file mode 100644 index 000000000..c873b6365 --- /dev/null +++ b/vtest/unit/specs/components/Context/injectEnvironment.spec.js @@ -0,0 +1,197 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import injectEnvironment from "../../../../../src/components/Context/injectEnvironment.js"; + +describe("Context::injectEnvironment", () => { + const run = (description, mywindow, expectedXdm) => { + it(description, () => { + const xdm = {}; + injectEnvironment(mywindow)(xdm); + expect(xdm).toEqual(expectedXdm); + }); + }; + run( + "uses the correct width and height", + { + screen: { + width: 1001, + height: 1002, + }, + innerWidth: 1003, + innerHeight: 1004, + document: { + documentElement: { + clientWidth: 1005, + clientHeight: 1006, + }, + }, + }, + { + environment: { + type: "browser", + browserDetails: { + viewportWidth: 1005, + viewportHeight: 1006, + }, + }, + }, + ); + run( + "handles negative width and height", + { + document: { + documentElement: { + clientWidth: -10, + clientHeight: -20, + }, + }, + }, + { + environment: { + type: "browser", + }, + }, + ); + run( + "handles negative width", + { + document: { + documentElement: { + clientWidth: -10, + clientHeight: -42, + }, + }, + }, + { + environment: { + type: "browser", + }, + }, + ); + run( + "handles missing width and height", + { + document: { + documentElement: {}, + }, + }, + { + environment: { + type: "browser", + }, + }, + ); + run( + "handles missing documentElement", + { + document: {}, + }, + { + environment: { + type: "browser", + }, + }, + ); + run( + "handles 0 height and width", + { + document: { + documentElement: { + clientWidth: 0, + clientHeight: 0, + }, + }, + }, + { + environment: { + type: "browser", + browserDetails: { + viewportWidth: 0, + viewportHeight: 0, + }, + }, + }, + ); + run( + "handles floating point height and width", + { + document: { + documentElement: { + clientWidth: 10, + clientHeight: 4.2, + }, + }, + }, + { + environment: { + type: "browser", + browserDetails: { + viewportWidth: 10, + viewportHeight: 4, + }, + }, + }, + ); + run( + "handles null values", + { + document: { + documentElement: { + clientWidth: null, + clientHeight: null, + }, + }, + }, + { + environment: { + type: "browser", + }, + }, + ); + run( + "handles only width", + { + document: { + documentElement: { + clientWidth: 1234, + }, + }, + }, + { + environment: { + type: "browser", + browserDetails: { + viewportWidth: 1234, + }, + }, + }, + ); + run( + "handles only height", + { + document: { + documentElement: { + clientHeight: "1234.5", + }, + }, + }, + { + environment: { + type: "browser", + browserDetails: { + viewportHeight: 1235, + }, + }, + }, + ); +}); diff --git a/vtest/unit/specs/components/Context/injectHighEntropyUserAgentHints.spec.js b/vtest/unit/specs/components/Context/injectHighEntropyUserAgentHints.spec.js new file mode 100644 index 000000000..b39715666 --- /dev/null +++ b/vtest/unit/specs/components/Context/injectHighEntropyUserAgentHints.spec.js @@ -0,0 +1,49 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import injectHighEntropyUserAgentHints from "../../../../../src/components/Context/injectHighEntropyUserAgentHints.js"; + +describe("Context::injectHighEntropyUserAgentHints", () => { + const navigator = { + userAgentData: { + getHighEntropyValues() { + return Promise.resolve({ + architecture: "x86", + bitness: "64", + model: "alloy", + platformVersion: "1.2.3", + wow64: false, + invalidHint: true, + }); + }, + }, + }; + it("works", () => { + const xdm = {}; + injectHighEntropyUserAgentHints(navigator)(xdm, console).then(() => { + expect(xdm).toEqual({ + environment: { + browserDetails: { + userAgentClientHints: { + architecture: "x86", + bitness: "64", + model: "alloy", + platformVersion: "1.2.3", + wow64: false, + }, + }, + }, + }); + }); + }); +}); diff --git a/vtest/unit/specs/components/Context/injectPlaceContext.spec.js b/vtest/unit/specs/components/Context/injectPlaceContext.spec.js new file mode 100644 index 000000000..633d64ac2 --- /dev/null +++ b/vtest/unit/specs/components/Context/injectPlaceContext.spec.js @@ -0,0 +1,75 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import injectPlaceContext from "../../../../../src/components/Context/injectPlaceContext.js"; + +describe("Context::injectPlaceContext", () => { + it("adds placeContext", () => { + const date = new Date("March 25, 2019 21:56:18"); + vi.spyOn(date, "getTimezoneOffset").mockReturnValue(7 * 60); + const xdm = {}; + injectPlaceContext(() => date)(xdm); + expect(xdm).toEqual({ + placeContext: { + localTime: "2019-03-25T21:56:18.000-07:00", + localTimezoneOffset: 7 * 60, + }, + }); + }); + it("handles string values from timezoneOffset", () => { + const date = new Date("May 19, 2022 13:43:42"); + vi.spyOn(date, "getTimezoneOffset").mockReturnValue("55.1"); + const xdm = {}; + injectPlaceContext(() => date)(xdm); + expect(xdm).toEqual({ + placeContext: { + localTime: "2022-05-19T13:43:42.000-00:55", + localTimezoneOffset: 55, + }, + }); + }); + it("handles NaN timezoneOffsets", () => { + const date = new Date("May 19, 2022 13:43:42"); + vi.spyOn(date, "getTimezoneOffset").mockReturnValue("foo"); + const xdm = {}; + injectPlaceContext(() => date)(xdm); + expect(xdm).toEqual({ + placeContext: { + localTime: "2022-05-19T13:43:42.000+00:00", + }, + }); + }); + it("handles large timezoneOffsets 1", () => { + const date = new Date("October 28, 2022 11:57:42"); + vi.spyOn(date, "getTimezoneOffset").mockReturnValue(-5999); + const xdm = {}; + injectPlaceContext(() => date)(xdm); + expect(xdm).toEqual({ + placeContext: { + localTime: "2022-10-28T11:57:42.000+99:59", + localTimezoneOffset: -5999, + }, + }); + }); + it("handles large timezoneOffsets 2", () => { + const date = new Date("October 28, 2022 11:57:42"); + vi.spyOn(date, "getTimezoneOffset").mockReturnValue(-6000); + const xdm = {}; + injectPlaceContext(() => date)(xdm); + expect(xdm).toEqual({ + placeContext: { + localTimezoneOffset: -6000, + }, + }); + }); +}); diff --git a/vtest/unit/specs/components/Context/injectTimestamp.spec.js b/vtest/unit/specs/components/Context/injectTimestamp.spec.js new file mode 100644 index 000000000..51b221152 --- /dev/null +++ b/vtest/unit/specs/components/Context/injectTimestamp.spec.js @@ -0,0 +1,31 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, describe, it, expect } from "vitest"; +import injectTimestamp from "../../../../../src/components/Context/injectTimestamp.js"; + +describe("Context::injectTimestamp", () => { + let dateProvider; + const date = new Date("November 25, 2019 10:09:42 UTC"); + beforeEach(() => { + dateProvider = () => { + return date; + }; + }); + it("adds timestamp", () => { + const xdm = {}; + injectTimestamp(dateProvider)(xdm); + expect(xdm).toEqual({ + timestamp: "2019-11-25T10:09:42.000Z", + }); + }); +}); diff --git a/vtest/unit/specs/components/Context/injectWeb.spec.js b/vtest/unit/specs/components/Context/injectWeb.spec.js new file mode 100644 index 000000000..963033e29 --- /dev/null +++ b/vtest/unit/specs/components/Context/injectWeb.spec.js @@ -0,0 +1,38 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import injectWeb from "../../../../../src/components/Context/injectWeb.js"; + +describe("Context::injectWeb", () => { + const window = { + location: { + href: "http://mylocation.com", + }, + document: { + referrer: "http://myreferrer.com", + }, + }; + it("works", () => { + const xdm = {}; + injectWeb(window)(xdm); + expect(xdm).toEqual({ + web: { + webPageDetails: { + URL: "http://mylocation.com", + }, + webReferrer: { + URL: "http://myreferrer.com", + }, + }, + }); + }); +}); diff --git a/vtest/unit/specs/components/DataCollector/index.spec.js b/vtest/unit/specs/components/DataCollector/index.spec.js new file mode 100644 index 000000000..fc823f25c --- /dev/null +++ b/vtest/unit/specs/components/DataCollector/index.spec.js @@ -0,0 +1,185 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createDataCollector from "../../../../../src/components/DataCollector/index.js"; +import { noop } from "../../../../../src/utils/index.js"; + +describe("Event Command", () => { + let event; + let eventManager; + let logger; + let sendEventCommand; + beforeEach(() => { + event = { + documentMayUnload: vi.fn(), + setUserData: vi.fn(), + setUserXdm: vi.fn(), + mergeXdm: vi.fn(), + mergeMeta: vi.fn(), + mergeConfigOverride: vi.fn(), + }; + logger = { + warn: vi.fn(), + }; + eventManager = { + createEvent() { + return event; + }, + sendEvent: vi + .fn() + .mockImplementation((_event, { applyUserProvidedData = noop }) => { + applyUserProvidedData(); + return Promise.resolve("sendEventResult"); + }), + }; + const dataCollector = createDataCollector({ + eventManager, + logger, + }); + sendEventCommand = dataCollector.commands.sendEvent; + }); + it("sends event", () => { + const xdm = { + a: "b", + }; + const data = { + c: "d", + }; + const options = { + otherSetting: "foo", + type: "test", + xdm, + data, + documentUnloading: true, + }; + return sendEventCommand.run(options).then((result) => { + expect(event.documentMayUnload).toHaveBeenCalled(); + expect(event.setUserXdm).toHaveBeenCalledWith(xdm); + expect(event.setUserData).toHaveBeenCalledWith(data); + expect(eventManager.sendEvent).toHaveBeenCalledWith(event, { + otherSetting: "foo", + }); + expect(result).toEqual("sendEventResult"); + }); + }); + it("sends event with decisionScopes parameter when decisionScopes is not empty", () => { + const options = { + renderDecisions: true, + decisionScopes: ["Foo1"], + personalization: { + decisionScopes: ["Foo2"], + }, + }; + return sendEventCommand.run(options).then((result) => { + expect(eventManager.sendEvent).toHaveBeenCalledWith(event, { + renderDecisions: true, + decisionScopes: ["Foo1"], + personalization: { + decisionScopes: ["Foo2"], + }, + }); + expect(result).toEqual("sendEventResult"); + }); + }); + it("sends event with surfaces parameter when surfaces is not empty", () => { + const options = { + renderDecisions: true, + personalization: { + surfaces: ["Foo1", "Foo2"], + }, + }; + return sendEventCommand.run(options).then((result) => { + expect(eventManager.sendEvent).toHaveBeenCalledWith(event, { + renderDecisions: true, + personalization: { + surfaces: ["Foo1", "Foo2"], + }, + }); + expect(result).toEqual("sendEventResult"); + }); + }); + it("does not call documentMayUnload if documentUnloading is not defined", () => { + return sendEventCommand.run({}).then(() => { + expect(event.documentMayUnload).not.toHaveBeenCalled(); + }); + }); + it("merges eventType", () => { + return sendEventCommand + .run({ + type: "mytype", + }) + .then(() => { + expect(event.mergeXdm).toHaveBeenCalledWith({ + eventType: "mytype", + }); + }); + }); + it("merges eventMergeID", () => { + return sendEventCommand + .run({ + mergeId: "mymergeid", + }) + .then(() => { + expect(event.mergeXdm).toHaveBeenCalledWith({ + eventMergeId: "mymergeid", + }); + }); + }); + it("merges datasetId into the override configuration", () => { + const datasetId = "mydatasetId"; + return sendEventCommand + .run({ + datasetId, + }) + .then(() => { + expect(eventManager.sendEvent).toHaveBeenCalledWith( + expect.any(Object), + { + edgeConfigOverrides: { + com_adobe_experience_platform: { + datasets: { + event: { + datasetId, + }, + }, + }, + }, + }, + ); + expect(logger.warn).toHaveBeenCalled(); + }); + }); + it("includes configuration if provided", () => { + return sendEventCommand + .run({ + renderDecisions: true, + edgeConfigOverrides: { + target: { + propertyToken: "hello", + }, + }, + }) + .then(() => { + expect(eventManager.sendEvent).toHaveBeenCalledWith( + expect.any(Object), + { + renderDecisions: true, + edgeConfigOverrides: { + target: { + propertyToken: "hello", + }, + }, + }, + ); + }); + }); +}); diff --git a/vtest/unit/specs/components/DataCollector/validateApplyResponse.spec.js b/vtest/unit/specs/components/DataCollector/validateApplyResponse.spec.js new file mode 100644 index 000000000..771dd44c6 --- /dev/null +++ b/vtest/unit/specs/components/DataCollector/validateApplyResponse.spec.js @@ -0,0 +1,41 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import validateApplyResponse from "../../../../../src/components/DataCollector/validateApplyResponse.js"; + +describe("DataCollector::validateApplyResponse", () => { + it("does not throw error for valid options", () => { + expect(() => { + validateApplyResponse({ + options: { + responseBody: { + handle: [ + { + type: "something:special", + payload: {}, + }, + ], + }, + }, + }); + }).not.toThrowError(); + }); + it("throws error for invalid options", () => { + expect(() => { + validateApplyResponse({ + options: { + who_dis: true, + }, + }); + }).toThrowError(); + }); +}); diff --git a/vtest/unit/specs/components/DataCollector/validateUserEventOptions.spec.js b/vtest/unit/specs/components/DataCollector/validateUserEventOptions.spec.js new file mode 100644 index 000000000..5a04b9253 --- /dev/null +++ b/vtest/unit/specs/components/DataCollector/validateUserEventOptions.spec.js @@ -0,0 +1,68 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import validateUserEventOptions from "../../../../../src/components/DataCollector/validateUserEventOptions.js"; + +describe("DataCollector::validateUserEventOptions", () => { + it("throws error for invalid options", () => { + [ + { + xdm: { + eventType: "test", + identityMap: "123", + }, + }, + { + xdm: { + eventType: "test", + identityMap: { + namespace1: { + id: "123", + primary: true, + }, + }, + }, + }, + { + xdm: { + eventType: "test", + identityMap: { + namespace1: {}, + }, + }, + }, + { + xdm: { + eventType: "test", + identityMap: { + namespace1: [ + { + id: "123", + primary: true, + authenticatedState: "loggedIn", + }, + ], + }, + }, + }, + { + decisionScopes: ["item1", "item1"], + }, + ].forEach((options) => { + expect(() => { + validateUserEventOptions({ + options, + }); + }).toThrowError(); + }); + }); +}); diff --git a/vtest/unit/specs/components/EventMerge/createComponent.spec.js b/vtest/unit/specs/components/EventMerge/createComponent.spec.js new file mode 100644 index 000000000..b87b00c89 --- /dev/null +++ b/vtest/unit/specs/components/EventMerge/createComponent.spec.js @@ -0,0 +1,31 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import createComponent from "../../../../../src/components/EventMerge/createComponent.js"; + +describe("EventMerge:createComponent", () => { + it("creates a component", () => { + const createEventMergeId = () => undefined; + expect( + createComponent({ + createEventMergeId, + }), + ).toEqual({ + commands: { + createEventMergeId: { + run: createEventMergeId, + }, + }, + }); + }); +}); diff --git a/vtest/unit/specs/components/EventMerge/createEventMergeId.spec.js b/vtest/unit/specs/components/EventMerge/createEventMergeId.spec.js new file mode 100644 index 000000000..9e95287f2 --- /dev/null +++ b/vtest/unit/specs/components/EventMerge/createEventMergeId.spec.js @@ -0,0 +1,24 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import createEventMergeId from "../../../../../src/components/EventMerge/createEventMergeId.js"; +import uuidV4Regex from "../../../constants/uuidV4Regex.js"; + +describe("EventMerge:createEventMergeId", () => { + it("returns a UUID v4-compliant Id", () => { + expect(uuidV4Regex.test(createEventMergeId().eventMergeId)).toBe(true); + }); + it("doesn't return any other fields in the response", () => { + expect(Object.keys(createEventMergeId())).toEqual(["eventMergeId"]); + }); +}); diff --git a/vtest/unit/specs/components/Identity/addEcidToPayload.spec.js b/vtest/unit/specs/components/Identity/addEcidToPayload.spec.js new file mode 100644 index 000000000..aebab901c --- /dev/null +++ b/vtest/unit/specs/components/Identity/addEcidToPayload.spec.js @@ -0,0 +1,26 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import addEcidToPayload from "../../../../../src/components/Identity/addEcidToPayload.js"; + +describe("Identity:addEcidToPayload", () => { + it("adds ECID to payload", () => { + const payload = { + addIdentity: vi.fn(), + }; + addEcidToPayload(payload, "user@adobe"); + expect(payload.addIdentity).toHaveBeenCalledWith("ECID", { + id: "user@adobe", + }); + }); +}); diff --git a/vtest/unit/specs/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.spec.js b/vtest/unit/specs/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.spec.js new file mode 100644 index 000000000..95df29eb0 --- /dev/null +++ b/vtest/unit/specs/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.spec.js @@ -0,0 +1,61 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import appendIdentityToUrlOptionsValidator from "../../../../../../src/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.js"; + +describe("Identity::appendIdentityToUrlOptionsValidator", () => { + [ + undefined, + "myurl", + {}, + { + url: "", + }, + { + url: "hello", + other: "goodbye", + }, + ].forEach((param) => { + it(`should throw an error when ${JSON.stringify(param)} is passed`, () => { + expect(() => { + appendIdentityToUrlOptionsValidator(param); + }).toThrowError(); + }); + }); + it("should accept a url", () => { + expect( + appendIdentityToUrlOptionsValidator({ + url: "http://google.com", + }), + ).toEqual({ + url: "http://google.com", + }); + }); + it("should accept override configuration", () => { + expect(() => { + appendIdentityToUrlOptionsValidator({ + url: "http://google.com", + edgeConfigOverrides: { + identity: { + idSyncContainerId: "123", + }, + }, + }); + }).not.toThrowError(); + expect(() => { + appendIdentityToUrlOptionsValidator({ + url: "http://google.com", + edgeConfigOverrides: {}, + }); + }).not.toThrowError(); + }); +}); diff --git a/vtest/unit/specs/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.spec.js b/vtest/unit/specs/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.spec.js new file mode 100644 index 000000000..9e6a2879d --- /dev/null +++ b/vtest/unit/specs/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.spec.js @@ -0,0 +1,46 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import injectAppendIdentityToUrl from "../../../../../../src/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.js"; + +describe("appendIdentityToUrl", () => { + const date = new Date(1234); + const orgId = "myorg@adobe"; + const appendIdentityToUrl = injectAppendIdentityToUrl({ + dateProvider: () => date, + orgId, + }); + const ecid = "1234"; + const qsp = "adobe_mc=TS%3D1%7CMCMID%3D1234%7CMCORGID%3Dmyorg%2540adobe"; + const test = (original, expected) => { + it(`appends to "${original}"`, () => { + expect(appendIdentityToUrl(ecid, original)).toBe(expected); + }); + }; + test("", `?${qsp}`); + test("/", `/?${qsp}`); + test("?", `?${qsp}`); + test("?a=b", `?a=b&${qsp}`); + test("#test", `?${qsp}#test`); + test("/example.html", `/example.html?${qsp}`); + test("https://adobe.com", `https://adobe.com?${qsp}`); + test("https://adobe.com?", `https://adobe.com?${qsp}`); + test("https://adobe.com?a=1", `https://adobe.com?a=1&${qsp}`); + test("https://adobe.com#myhash", `https://adobe.com?${qsp}#myhash`); + test("https://adobe.com?#myhash", `https://adobe.com?${qsp}#myhash`); + test("https://adobe.com?a=1#myhash", `https://adobe.com?a=1&${qsp}#myhash`); + test( + "https://adobe.com#myweird?hash", + `https://adobe.com?${qsp}#myweird?hash`, + ); +}); diff --git a/vtest/unit/specs/components/Identity/configValidators.spec.js b/vtest/unit/specs/components/Identity/configValidators.spec.js new file mode 100644 index 000000000..25d44005c --- /dev/null +++ b/vtest/unit/specs/components/Identity/configValidators.spec.js @@ -0,0 +1,42 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import configValidators from "../../../../../src/components/Identity/configValidators.js"; +import testConfigValidators from "../../../helpers/testConfigValidators.js"; + +describe("Identity config validators", () => { + testConfigValidators({ + configValidators, + validConfigurations: [ + {}, + { + thirdPartyCookiesEnabled: false, + }, + { + idMigrationEnabled: true, + }, + ], + invalidConfigurations: [ + { + thirdPartyCookiesEnabled: 42, + }, + { + idMigrationEnabled: () => {}, + }, + ], + defaultValues: { + thirdPartyCookiesEnabled: true, + idMigrationEnabled: true, + }, + }); +}); diff --git a/vtest/unit/specs/components/Identity/createComponent.spec.js b/vtest/unit/specs/components/Identity/createComponent.spec.js new file mode 100644 index 000000000..76bbf1df0 --- /dev/null +++ b/vtest/unit/specs/components/Identity/createComponent.spec.js @@ -0,0 +1,503 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createComponent from "../../../../../src/components/Identity/createComponent.js"; +import { defer } from "../../../../../src/utils/index.js"; +import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; + +describe("Identity::createComponent", () => { + let addEcidQueryToPayload; + let addQueryStringIdentityToPayload; + let ensureSingleIdentity; + let setLegacyEcid; + let handleResponseForIdSyncs; + let getNamespacesFromResponse; + let getIdentity; + let consent; + let appendIdentityToUrl; + let logger; + let getIdentityOptionsValidator; + let awaitConsentDeferred; + let withConsentDeferred; + let getIdentityDeferred; + let response; + let component; + beforeEach(() => { + ensureSingleIdentity = vi.fn(); + addEcidQueryToPayload = vi.fn(); + addQueryStringIdentityToPayload = vi.fn(); + setLegacyEcid = vi.fn(); + handleResponseForIdSyncs = vi.fn(); + getNamespacesFromResponse = vi.fn(); + getIdentityDeferred = defer(); + awaitConsentDeferred = defer(); + withConsentDeferred = defer(); + consent = { + awaitConsent: vi.fn().mockReturnValue(awaitConsentDeferred.promise), + withConsent: vi.fn().mockReturnValue(withConsentDeferred.promise), + }; + appendIdentityToUrl = vi.fn(); + logger = { + warn: vi.fn(), + }; + getIdentity = vi.fn().mockReturnValue(getIdentityDeferred.promise); + getIdentityOptionsValidator = (options) => options; + component = createComponent({ + ensureSingleIdentity, + addEcidQueryToPayload, + addQueryStringIdentityToPayload, + setLegacyEcid, + handleResponseForIdSyncs, + getNamespacesFromResponse, + getIdentity, + consent, + appendIdentityToUrl, + logger, + getIdentityOptionsValidator, + }); + response = { + getEdge: vi.fn(), + }; + }); + it("adds ECID query to event", () => { + const payload = { + type: "payload", + }; + const request = { + getPayload() { + return payload; + }, + }; + const onResponse = vi.fn(); + component.lifecycle.onBeforeRequest({ + request, + onResponse, + }); + expect(addEcidQueryToPayload).toHaveBeenCalledWith(payload); + }); + it("adds the query string identity to the payload", () => { + const payload = { + type: "payload", + }; + const request = { + getPayload() { + return payload; + }, + }; + component.lifecycle.onBeforeRequest({ + request, + }); + expect(addQueryStringIdentityToPayload).toHaveBeenNthCalledWith(1, payload); + }); + it("ensures request has identity", () => { + const payload = { + type: "payload", + }; + const request = { + getPayload() { + return payload; + }, + }; + const onResponse = vi.fn(); + const onRequestFailure = vi.fn(); + const ensureSingleIdentityPromise = Promise.resolve(); + ensureSingleIdentity.mockReturnValue(ensureSingleIdentityPromise); + const result = component.lifecycle.onBeforeRequest({ + request, + onResponse, + onRequestFailure, + }); + expect(ensureSingleIdentity).toHaveBeenCalledWith({ + request, + onResponse, + onRequestFailure, + }); + expect(result).toBe(ensureSingleIdentityPromise); + }); + it("does not create legacy identity cookie if response does not contain ECID", () => { + const idSyncsPromise = Promise.resolve(); + handleResponseForIdSyncs.mockReturnValue(idSyncsPromise); + component.lifecycle.onResponse({ + response, + }); + expect(getNamespacesFromResponse).toHaveBeenCalledWith(response); + expect(setLegacyEcid).not.toHaveBeenCalled(); + }); + it("creates legacy identity cookie if response contains ECID", () => { + const idSyncsPromise = Promise.resolve(); + handleResponseForIdSyncs.mockReturnValue(idSyncsPromise); + getNamespacesFromResponse.mockReturnValue({ + ECID: "user@adobe", + }); + component.lifecycle.onResponse({ + response, + }); + expect(getNamespacesFromResponse).toHaveBeenCalledWith(response); + expect(setLegacyEcid).toHaveBeenCalledWith("user@adobe"); + component.lifecycle.onResponse({ + response, + }); + expect(getNamespacesFromResponse).toHaveBeenCalledTimes(2); + expect(setLegacyEcid).toHaveBeenCalledTimes(1); + }); + it("handles ID syncs", () => { + const idSyncsPromise = Promise.resolve(); + handleResponseForIdSyncs.mockReturnValue(idSyncsPromise); + const result = component.lifecycle.onResponse({ + response, + }); + expect(handleResponseForIdSyncs).toHaveBeenCalledWith(response); + return expect(result).resolves.toBe(undefined); + }); + it("getIdentity command should make a request when ecid is not available", () => { + const idSyncsPromise = Promise.resolve(); + handleResponseForIdSyncs.mockReturnValue(idSyncsPromise); + const onResolved = vi.fn(); + const onResolved2 = vi.fn(); + response.getEdge.mockReturnValue({ + regionId: 42, + }); + component.commands.getIdentity + .run({ + namespaces: ["ECID"], + }) + .then(onResolved); + return flushPromiseChains() + .then(() => { + expect(getIdentity).not.toHaveBeenCalled(); + expect(onResolved).not.toHaveBeenCalled(); + awaitConsentDeferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(getIdentity).toHaveBeenCalled(); + // ECID and CORE are requested regardless of the namespaces passed in. + getNamespacesFromResponse.mockReturnValue({ + ECID: "user@adobe", + CORE: "mycoreid", + }); + component.lifecycle.onResponse({ + response, + }); + getIdentityDeferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(onResolved).toHaveBeenCalledWith({ + identity: { + ECID: "user@adobe", + }, + edge: { + regionId: 42, + }, + }); + getNamespacesFromResponse.mockReturnValue({ + CORE: "mycoreid", + }); + component.commands.getIdentity + .run({ + namespaces: ["CORE"], + }) + .then(onResolved2); + return flushPromiseChains(); + }) + .then(() => { + expect(getIdentity).toHaveBeenCalledTimes(1); + return flushPromiseChains(); + }) + .then(() => { + expect(onResolved2).toHaveBeenCalledWith({ + identity: { + CORE: "mycoreid", + }, + edge: { + regionId: 42, + }, + }); + }); + }); + it("getIdentity command should not make a request when ecid is available", () => { + const idSyncsPromise = Promise.resolve(); + handleResponseForIdSyncs.mockReturnValue(idSyncsPromise); + getNamespacesFromResponse.mockReturnValue({ + ECID: "user@adobe", + }); + response.getEdge.mockReturnValue({ + regionId: 7, + }); + component.lifecycle.onResponse({ + response, + }); + const onResolved = vi.fn(); + component.commands.getIdentity + .run({ + namespaces: ["ECID"], + }) + .then(onResolved); + return flushPromiseChains() + .then(() => { + expect(getIdentity).not.toHaveBeenCalled(); + expect(onResolved).not.toHaveBeenCalled(); + awaitConsentDeferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(getIdentity).not.toHaveBeenCalled(); + expect(onResolved).toHaveBeenCalledWith({ + identity: { + ECID: "user@adobe", + }, + edge: { + regionId: 7, + }, + }); + }); + }); + it("getIdentity command should not make a request when CORE is available", () => { + const idSyncsPromise = Promise.resolve(); + handleResponseForIdSyncs.mockReturnValue(idSyncsPromise); + getNamespacesFromResponse.mockReturnValue({ + ECID: "user@adobe", + CORE: "mycoreid", + }); + response.getEdge.mockReturnValue({ + regionId: 7, + }); + component.lifecycle.onResponse({ + response, + }); + const onResolved = vi.fn(); + component.commands.getIdentity + .run({ + namespaces: ["CORE"], + }) + .then(onResolved); + return flushPromiseChains() + .then(() => { + expect(getIdentity).not.toHaveBeenCalled(); + expect(onResolved).not.toHaveBeenCalled(); + awaitConsentDeferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(getIdentity).not.toHaveBeenCalled(); + expect(onResolved).toHaveBeenCalledWith({ + identity: { + CORE: "mycoreid", + }, + edge: { + regionId: 7, + }, + }); + }); + }); + it("getIdentity command is called with configuration overrides, when provided", () => { + const idSyncsPromise = Promise.resolve(); + handleResponseForIdSyncs.mockReturnValue(idSyncsPromise); + const onResolved = vi.fn(); + response.getEdge.mockReturnValue({ + regionId: 42, + }); + const getIdentityOptions = { + namespaces: ["ECID"], + edgeConfigOverrides: { + com_adobe_identity: { + idSyncContainerId: "123", + }, + }, + }; + component.commands.getIdentity.run(getIdentityOptions).then(onResolved); + return flushPromiseChains() + .then(() => { + expect(getIdentity).not.toHaveBeenCalled(); + expect(onResolved).not.toHaveBeenCalled(); + awaitConsentDeferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(getIdentity).toHaveBeenCalledWith(getIdentityOptions); + getNamespacesFromResponse.mockReturnValue({ + ECID: "user@adobe", + }); + component.lifecycle.onResponse({ + response, + }); + getIdentityDeferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(onResolved).toHaveBeenCalledWith({ + identity: { + ECID: "user@adobe", + }, + edge: { + regionId: 42, + }, + }); + }); + }); + it("appendIdentityToUrl should return the unmodified url when consent is not given.", () => { + const commandPromise = component.commands.appendIdentityToUrl.run({ + url: "myurl", + }); + withConsentDeferred.reject(new Error("My consent error.")); + return expect(commandPromise) + .resolves.toStrictEqual({ + url: "myurl", + }) + .then(() => { + expect(logger.warn).toHaveBeenCalledWith( + "Unable to append identity to url. My consent error.", + ); + expect(getIdentity).not.toHaveBeenCalled(); + expect(appendIdentityToUrl).not.toHaveBeenCalled(); + }); + }); + it("appendIdentityToUrl should return the unmodified url when getIdentity returns an error.", () => { + const commandPromise = component.commands.appendIdentityToUrl.run({ + url: "myurl", + }); + withConsentDeferred.resolve(); + getIdentityDeferred.reject(new Error("My getIdentity error.")); + return expect(commandPromise) + .resolves.toStrictEqual({ + url: "myurl", + }) + .then(() => { + expect(logger.warn).toHaveBeenNthCalledWith( + 1, + "Unable to append identity to url. My getIdentity error.", + ); + expect(appendIdentityToUrl).not.toHaveBeenCalled(); + }); + }); + it("appendIdentityToUrl should getIdentity when there isn't one.", () => { + const idSyncsPromise = Promise.resolve(); + handleResponseForIdSyncs.mockReturnValue(idSyncsPromise); + appendIdentityToUrl.mockReturnValue("modifiedUrl"); + const onResolved = vi.fn(); + response.getEdge.mockReturnValue({ + regionId: 42, + }); + component.commands.appendIdentityToUrl + .run({ + namespaces: ["ECID"], + url: "myurl", + }) + .then(onResolved); + return flushPromiseChains() + .then(() => { + expect(getIdentity).not.toHaveBeenCalled(); + expect(onResolved).not.toHaveBeenCalled(); + withConsentDeferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(getIdentity).toHaveBeenCalledWith( + expect.objectContaining({ + namespaces: ["ECID"], + }), + ); + getNamespacesFromResponse.mockReturnValue({ + ECID: "user@adobe", + }); + component.lifecycle.onResponse({ + response, + }); + getIdentityDeferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(logger.warn).not.toHaveBeenCalled(); + expect(appendIdentityToUrl).toHaveBeenNthCalledWith( + 1, + "user@adobe", + "myurl", + ); + }); + }); + it("appendIdentityToUrl should append the identity to a url when there is already an identity", () => { + // set the ECID + const idSyncsPromise = Promise.resolve(); + handleResponseForIdSyncs.mockReturnValue(idSyncsPromise); + getNamespacesFromResponse.mockReturnValue({ + ECID: "user@adobe", + }); + response.getEdge.mockReturnValue({ + regionId: 7, + }); + component.lifecycle.onResponse({ + response, + }); + appendIdentityToUrl.mockReturnValue("modifiedUrl"); + const commandPromise = component.commands.appendIdentityToUrl.run({ + url: "myurl", + }); + withConsentDeferred.resolve(); + return expect(commandPromise).resolves.toStrictEqual({ + url: "modifiedUrl", + }); + }); + it("appendIdentityToUrl should call getIdentity with configuration overrides, if provided", () => { + const idSyncsPromise = Promise.resolve(); + handleResponseForIdSyncs.mockReturnValue(idSyncsPromise); + appendIdentityToUrl.mockReturnValue("modifiedUrl"); + const onResolved = vi.fn(); + response.getEdge.mockReturnValue({ + regionId: 42, + }); + const edgeConfigOverrides = { + com_adobe_identity: { + idSyncContainerId: "123", + }, + }; + component.commands.appendIdentityToUrl + .run({ + namespaces: ["ECID"], + url: "myurl", + edgeConfigOverrides, + }) + .then(onResolved); + return flushPromiseChains() + .then(() => { + expect(getIdentity).not.toHaveBeenCalled(); + expect(onResolved).not.toHaveBeenCalled(); + withConsentDeferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(getIdentity).toHaveBeenCalledWith( + expect.objectContaining({ + namespaces: ["ECID"], + edgeConfigOverrides, + }), + ); + getNamespacesFromResponse.mockReturnValue({ + ECID: "user@adobe", + }); + component.lifecycle.onResponse({ + response, + }); + getIdentityDeferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(logger.warn).not.toHaveBeenCalled(); + expect(appendIdentityToUrl).toHaveBeenNthCalledWith( + 1, + "user@adobe", + "myurl", + ); + }); + }); +}); diff --git a/vtest/unit/specs/components/Identity/createLegacyIdentity.spec.js b/vtest/unit/specs/components/Identity/createLegacyIdentity.spec.js new file mode 100644 index 000000000..1c3487573 --- /dev/null +++ b/vtest/unit/specs/components/Identity/createLegacyIdentity.spec.js @@ -0,0 +1,137 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createLegacyIdentity from "../../../../../src/components/Identity/createLegacyIdentity.js"; + +describe("Identity::createLegacyIdentity", () => { + let idMigrationEnabled; + let orgId; + let getEcidFromVisitor; + let apexDomain; + let isPageSsl; + let cookieJar; + let legacyIdentity; + beforeEach(() => { + idMigrationEnabled = true; + orgId = "TEST_ORG"; + getEcidFromVisitor = vi.fn().mockReturnValue(Promise.resolve()); + apexDomain = "mydomain"; + isPageSsl = true; + cookieJar = { + get: vi.fn(), + set: vi.fn(), + }; + legacyIdentity = undefined; + }); + const build = () => { + legacyIdentity = createLegacyIdentity({ + config: { + idMigrationEnabled, + orgId, + }, + getEcidFromVisitor, + apexDomain, + isPageSsl, + cookieJar, + }); + }; + describe("getEcid", () => { + it("should return a promise resolved with undefined if ID migration disabled", async () => { + idMigrationEnabled = false; + build(); + const result = await legacyIdentity.getEcid(); + expect(result).toBeUndefined(); + expect(cookieJar.get).not.toHaveBeenCalled(); + }); + it("should return promise resolved with undefined if no AMCV cookie or s_ecid cookie is present", async () => { + build(); + const result = await legacyIdentity.getEcid(); + expect(result).toBeUndefined(); + }); + [ + "random|string|MCMID|1234|random|random", + "MCMID|1234|random|random", + "random|random|MCMID|1234", + "MCMID|1234", + ].forEach((cookieValue) => { + it(`should return promise resolved with ECID if AMCV cookie is ${cookieValue}`, async () => { + cookieJar.get.mockImplementation((cookieName) => + cookieName === "AMCV_TEST_ORG" ? cookieValue : undefined, + ); + build(); + expect(await legacyIdentity.getEcid()).toEqual("1234"); + }); + it(`should return promise resolved with ECID if s_ecid cookie is ${cookieValue}`, async () => { + cookieJar.get.mockImplementation((cookieName) => + cookieName === "s_ecid" ? cookieValue : undefined, + ); + build(); + expect(await legacyIdentity.getEcid()).toEqual("1234"); + }); + }); + it("should return promise resolved with undefined cookie does not contain MCMID", async () => { + const cookieValue = "version|0.0.4"; + cookieJar.get.mockReturnValue(cookieValue); + build(); + expect(await legacyIdentity.getEcid()).toBeUndefined(); + }); + it("should request ECID from visitor ID Service if legacy ECID cookies are missing", async () => { + getEcidFromVisitor.mockReturnValue(Promise.resolve("visitor_ecid")); + build(); + expect(await legacyIdentity.getEcid()).toEqual("visitor_ecid"); + }); + }); + describe("setEcid", () => { + it("should not write AMCV cookie if ID migration disabled", () => { + idMigrationEnabled = false; + build(); + legacyIdentity.setEcid("1234"); + expect(cookieJar.set).not.toHaveBeenCalled(); + }); + it("writes a secure AMCV cookie", () => { + build(); + legacyIdentity.setEcid("1234"); + expect(cookieJar.set).toHaveBeenNthCalledWith( + 1, + "AMCV_TEST_ORG", + "MCMID|1234", + { + domain: "mydomain", + expires: 390, + sameSite: "none", + secure: true, + }, + ); + }); + it("writes an insecure AMCV cookie", () => { + isPageSsl = false; + build(); + legacyIdentity.setEcid("1234"); + expect(cookieJar.set).toHaveBeenNthCalledWith( + 1, + "AMCV_TEST_ORG", + "MCMID|1234", + { + domain: "mydomain", + expires: 390, + }, + ); + }); + it("should not write AMCV cookie if already present", () => { + cookieJar.get.mockReturnValue("MCMID|1234|otherstuff"); + build(); + legacyIdentity.setEcid("1234"); + expect(cookieJar.set).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Identity/getIdentity/createGetIdentity.spec.js b/vtest/unit/specs/components/Identity/getIdentity/createGetIdentity.spec.js new file mode 100644 index 000000000..e34ceabe8 --- /dev/null +++ b/vtest/unit/specs/components/Identity/getIdentity/createGetIdentity.spec.js @@ -0,0 +1,185 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createGetIdentity from "../../../../../../src/components/Identity/getIdentity/createGetIdentity.js"; + +describe("Identity::createGetIdentity", () => { + let sendEdgeNetworkRequest; + let createIdentityRequestPayload; + let createIdentityRequest; + let requestPayload; + let request; + beforeEach(() => { + sendEdgeNetworkRequest = vi.fn(); + requestPayload = { + mergeConfigOverride: vi.fn(), + type: vi.fn().mockReturnValue("payload"), + }; + createIdentityRequestPayload = vi.fn().mockReturnValue(requestPayload); + request = { + getPayload() { + return requestPayload; + }, + }; + createIdentityRequest = vi.fn().mockReturnValue(request); + }); + it("should return a function which calls sendEdgeNetworkRequest", () => { + const getIdentity = createGetIdentity({ + sendEdgeNetworkRequest, + createIdentityRequestPayload, + createIdentityRequest, + }); + getIdentity(); + expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ + request, + }); + }); + it("each getIdentity call should create new payloads and requests", () => { + const payload1 = { + mergeConfigOverride: vi.fn(), + type: vi.fn().mockReturnValue("payload1"), + }; + const payload2 = { + mergeConfigOverride: vi.fn(), + type: vi.fn().mockReturnValue("payload2"), + }; + const request1 = { + type: "request1", + }; + const request2 = { + type: "request2", + }; + createIdentityRequestPayload + .mockReturnValueOnce(payload1) + .mockReturnValueOnce(payload2); + createIdentityRequest + .mockReturnValueOnce(request1) + .mockReturnValueOnce(request2); + const getIdentity = createGetIdentity({ + sendEdgeNetworkRequest, + createIdentityRequestPayload, + createIdentityRequest, + }); + getIdentity({ + namespaces: ["namespace1", "namespace2"], + }); + expect(createIdentityRequestPayload).toHaveBeenCalledWith([ + "namespace1", + "namespace2", + ]); + expect(createIdentityRequest).toHaveBeenCalledWith({ + payload: payload1, + }); + expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ + request: request1, + }); + getIdentity(); + expect(createIdentityRequest).toHaveBeenCalledWith({ + payload: payload2, + }); + expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ + request: request2, + }); + }); + it("send override configuration, when provided", () => { + const request1 = { + type: "request1", + }; + createIdentityRequestPayload.mockReturnValueOnce(requestPayload); + createIdentityRequest.mockReturnValueOnce(request1); + const getIdentity = createGetIdentity({ + sendEdgeNetworkRequest, + createIdentityRequestPayload, + createIdentityRequest, + }); + const configuration = { + com_adobe_identity: { + idSyncContainerId: "123", + }, + }; + getIdentity({ + namespaces: ["namespace1"], + edgeConfigOverrides: configuration, + }); + expect(createIdentityRequestPayload).toHaveBeenCalledWith(["namespace1"]); + expect(createIdentityRequest).toHaveBeenCalledWith({ + payload: requestPayload, + }); + expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ + request: request1, + }); + expect(requestPayload.mergeConfigOverride).toHaveBeenCalledWith({ + com_adobe_identity: { + idSyncContainerId: configuration.com_adobe_identity.idSyncContainerId, + }, + }); + }); + it("send global override configuration, when provided", () => { + const request1 = { + type: "request1", + }; + createIdentityRequestPayload.mockReturnValueOnce(requestPayload); + createIdentityRequest.mockReturnValueOnce(request1); + const configuration = { + com_adobe_identity: { + idSyncContainerId: "123", + }, + }; + const getIdentity = createGetIdentity({ + sendEdgeNetworkRequest, + createIdentityRequestPayload, + createIdentityRequest, + globalConfigOverrides: configuration, + }); + getIdentity({ + namespaces: ["namespace1"], + }); + expect(createIdentityRequestPayload).toHaveBeenCalledWith(["namespace1"]); + expect(createIdentityRequest).toHaveBeenCalledWith({ + payload: requestPayload, + }); + expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ + request: request1, + }); + expect(requestPayload.mergeConfigOverride).toHaveBeenCalledWith({ + com_adobe_identity: { + idSyncContainerId: configuration.com_adobe_identity.idSyncContainerId, + }, + }); + }); + it("send edge config id override, when provided", () => { + const request1 = { + type: "request1", + }; + createIdentityRequestPayload.mockReturnValueOnce(requestPayload); + createIdentityRequest.mockReturnValueOnce(request1); + const getIdentity = createGetIdentity({ + sendEdgeNetworkRequest, + createIdentityRequestPayload, + createIdentityRequest, + }); + getIdentity({ + namespaces: ["namespace1"], + edgeConfigOverrides: { + datastreamId: "123", + }, + }); + expect(createIdentityRequestPayload).toHaveBeenCalledWith(["namespace1"]); + expect(createIdentityRequest).toHaveBeenCalledWith({ + payload: requestPayload, + datastreamIdOverride: "123", + }); + expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ + request: request1, + }); + }); +}); diff --git a/vtest/unit/specs/components/Identity/getIdentity/createGetIdentityOptionsValidator.spec.js b/vtest/unit/specs/components/Identity/getIdentity/createGetIdentityOptionsValidator.spec.js new file mode 100644 index 000000000..c63695c6b --- /dev/null +++ b/vtest/unit/specs/components/Identity/getIdentity/createGetIdentityOptionsValidator.spec.js @@ -0,0 +1,114 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import createGetIdentityOptionsValidator from "../../../../../../src/components/Identity/getIdentity/createGetIdentityOptionsValidator.js"; + +describe("Identity::getIdentityOptionsValidator", () => { + const thirdPartyValidator = createGetIdentityOptionsValidator({ + thirdPartyCookiesEnabled: true, + }); + const firstPartyValidator = createGetIdentityOptionsValidator({ + thirdPartyCookiesEnabled: false, + }); + it("should throw an error when invalid options are passed", () => { + expect(() => { + thirdPartyValidator({ + key: ["item1", "item2"], + }); + }).toThrow(new Error("'key': Unknown field.")); + expect(() => { + thirdPartyValidator({ + key1: ["item1", "item2"], + key2: ["item1", "item2"], + }); + }).toThrow(new Error("'key1': Unknown field.\n'key2': Unknown field.")); + expect(() => { + thirdPartyValidator({ + namespaces: [], + }); + }).toThrow( + new Error("'namespaces': Expected a non-empty array, but got []."), + ); + expect(() => { + thirdPartyValidator({ + namespaces: ["ECID", "ECID"], + }); + }).toThrow( + new Error( + `'namespaces': Expected array values to be unique, but got ["ECID","ECID"].`, + ), + ); + expect(() => { + thirdPartyValidator({ + namespaces: ["ACD"], + }); + }).toThrow( + new Error( + `'namespaces[0]': Expected one of these values: ["ECID","CORE"], but got "ACD".`, + ), + ); + }); + it("should return valid options when no options are passed", () => { + expect(() => { + thirdPartyValidator(); + }).not.toThrow(); + const validatedIdentityOptions = thirdPartyValidator(); + expect(validatedIdentityOptions).toEqual({ + namespaces: ["ECID"], + }); + }); + it("should not throw when supported namespace options are passed", () => { + const ECID = "ECID"; + expect(() => { + thirdPartyValidator({ + namespaces: [ECID], + }); + }).not.toThrow(); + }); + it("should return valid options when configuration is passed", () => { + expect(() => { + thirdPartyValidator({ + edgeConfigOverrides: { + identity: { + idSyncContainerId: "123", + }, + }, + }); + }).not.toThrow(); + }); + it("should return valid options when an empty configuration is passed", () => { + expect(() => { + thirdPartyValidator({ + edgeConfigOverrides: {}, + }); + }).not.toThrow(); + }); + it("should throw an error when CORE is passed with third party cookies disabled", () => { + expect(() => { + firstPartyValidator({ + namespaces: ["CORE"], + }); + }).toThrow( + new Error( + `namespaces: The CORE namespace cannot be requested when third-party cookies are disabled.`, + ), + ); + }); + it("should not throw when CORE is passed with third party cookies enabled", () => { + expect(() => { + thirdPartyValidator({ + namespaces: ["CORE"], + }); + }).not.toThrow(); + }); +}); diff --git a/vtest/unit/specs/components/Identity/getIdentity/createIdentityRequest.spec.js b/vtest/unit/specs/components/Identity/getIdentity/createIdentityRequest.spec.js new file mode 100644 index 000000000..689dd0e98 --- /dev/null +++ b/vtest/unit/specs/components/Identity/getIdentity/createIdentityRequest.spec.js @@ -0,0 +1,42 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import describeRequest from "../../../../helpers/describeRequest.js"; +import createIdentityRequest from "../../../../../../src/components/Identity/getIdentity/createIdentityRequest.js"; + +describe("createIdentityRequest", () => { + describeRequest(createIdentityRequest); + it("provides the appropriate action", () => { + const payload = {}; + const request = createIdentityRequest({ + payload, + }); + expect(request.getAction()).toBe("identity/acquire"); + }); + it("never uses sendBeacon", () => { + const payload = {}; + const request = createIdentityRequest({ + payload, + }); + expect(request.getUseSendBeacon()).toBe(false); + }); + it("passes the datastreamIdOverride to the request", () => { + const payload = {}; + const datastreamIdOverride = "my-edge-config-id-override"; + const request = createIdentityRequest({ + payload, + datastreamIdOverride, + }); + expect(request.getDatastreamIdOverride()).toBe(datastreamIdOverride); + }); +}); diff --git a/vtest/unit/specs/components/Identity/getIdentity/createIdentityRequestPayload.spec.js b/vtest/unit/specs/components/Identity/getIdentity/createIdentityRequestPayload.spec.js new file mode 100644 index 000000000..ea336adcd --- /dev/null +++ b/vtest/unit/specs/components/Identity/getIdentity/createIdentityRequestPayload.spec.js @@ -0,0 +1,49 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import createIdentityPayload from "../../../../../../src/components/Identity/getIdentity/createIdentityRequestPayload.js"; +import describeRequestPayload from "../../../../helpers/describeRequestPayload.js"; + +describe("createIdentityRequestPayload", () => { + describeRequestPayload(() => { + return createIdentityPayload(["NS1", "NS2", "NS3"]); + }); + it("adds identities", () => { + const payload = createIdentityPayload(["NS1", "NS2", "NS3"]); + payload.addIdentity("IDNS", { + id: "ABC123", + }); + payload.addIdentity("IDNS", { + id: "DEF456", + }); + expect(payload.toJSON()).toEqual({ + xdm: { + identityMap: { + IDNS: [ + { + id: "ABC123", + }, + { + id: "DEF456", + }, + ], + }, + }, + query: { + identity: { + fetch: ["NS1", "NS2", "NS3"], + }, + }, + }); + }); +}); diff --git a/vtest/unit/specs/components/Identity/getNamespacesFromResponse.spec.js b/vtest/unit/specs/components/Identity/getNamespacesFromResponse.spec.js new file mode 100644 index 000000000..c279c0c3f --- /dev/null +++ b/vtest/unit/specs/components/Identity/getNamespacesFromResponse.spec.js @@ -0,0 +1,56 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import getNamespacesFromResponse from "../../../../../src/components/Identity/getNamespacesFromResponse.js"; + +describe("Identity::getEcidFromResponse", () => { + it("does not return ECID if ECID does not exist in response", () => { + const response = { + getPayloadsByType: vi.fn().mockReturnValue([ + { + namespace: { + code: "other", + }, + id: "user123", + }, + ]), + }; + expect(getNamespacesFromResponse(response)).toEqual({ + other: "user123", + }); + expect(response.getPayloadsByType).toHaveBeenCalledWith("identity:result"); + }); + it("returns ECID if ECID exists in response", () => { + const response = { + getPayloadsByType: vi.fn().mockReturnValue([ + { + namespace: { + code: "other", + }, + id: "user123", + }, + { + namespace: { + code: "ECID", + }, + id: "user@adobe", + }, + ]), + }; + expect(getNamespacesFromResponse(response)).toEqual({ + other: "user123", + ECID: "user@adobe", + }); + expect(response.getPayloadsByType).toHaveBeenCalledWith("identity:result"); + }); +}); diff --git a/vtest/unit/specs/components/Identity/injectAddEcidQueryToPayload.spec.js b/vtest/unit/specs/components/Identity/injectAddEcidQueryToPayload.spec.js new file mode 100644 index 000000000..211e5f3d4 --- /dev/null +++ b/vtest/unit/specs/components/Identity/injectAddEcidQueryToPayload.spec.js @@ -0,0 +1,62 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import injectAddEcidQueryToPayload from "../../../../../src/components/Identity/injectAddEcidQueryToPayload.js"; + +describe("Identity::addEcidQueryToPayload", () => { + it("adds an ECID & CORE query to the event when third party cookies are enabled on Chrome", () => { + const addEcidQueryToPayload = injectAddEcidQueryToPayload({ + thirdPartyCookiesEnabled: true, + areThirdPartyCookiesSupportedByDefault: () => true, + }); + const payload = { + mergeQuery: vi.fn(), + }; + addEcidQueryToPayload(payload); + expect(payload.mergeQuery).toHaveBeenCalledWith({ + identity: { + fetch: ["ECID", "CORE"], + }, + }); + }); + it("adds only ECID query to the event when third party cookies are enabled on Safari", () => { + const addEcidQueryToPayload = injectAddEcidQueryToPayload({ + thirdPartyCookiesEnabled: true, + areThirdPartyCookiesSupportedByDefault: () => false, + }); + const payload = { + mergeQuery: vi.fn(), + }; + addEcidQueryToPayload(payload); + expect(payload.mergeQuery).toHaveBeenCalledWith({ + identity: { + fetch: ["ECID"], + }, + }); + }); + it("adds an ECID query to the event when third party cookies are disabled on Chrome", () => { + const addEcidQueryToPayload = injectAddEcidQueryToPayload({ + thirdPartyCookiesEnabled: false, + areThirdPartyCookiesSupportedByDefault: () => true, + }); + const payload = { + mergeQuery: vi.fn(), + }; + addEcidQueryToPayload(payload); + expect(payload.mergeQuery).toHaveBeenCalledWith({ + identity: { + fetch: ["ECID"], + }, + }); + }); +}); diff --git a/vtest/unit/specs/components/Identity/injectAddLegacyEcidToPayload.spec.js b/vtest/unit/specs/components/Identity/injectAddLegacyEcidToPayload.spec.js new file mode 100644 index 000000000..9faf0cfed --- /dev/null +++ b/vtest/unit/specs/components/Identity/injectAddLegacyEcidToPayload.spec.js @@ -0,0 +1,51 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectAddLegacyEcidToPayload from "../../../../../src/components/Identity/injectAddLegacyEcidToPayload.js"; + +describe("Identity::injectAddLegacyEcidToPayload", () => { + let getLegacyEcid; + let addEcidToPayload; + let payload; + let addLegacyEcidToPayload; + beforeEach(() => { + getLegacyEcid = vi.fn().mockReturnValue(Promise.resolve("legacy@adobe")); + addEcidToPayload = vi.fn(); + addLegacyEcidToPayload = injectAddLegacyEcidToPayload({ + getLegacyEcid, + addEcidToPayload, + }); + payload = { + hasIdentity: vi.fn(), + }; + }); + it("does not add legacy ECID to payload if legacy ECID does not exist", () => { + payload.hasIdentity.mockReturnValue(false); + getLegacyEcid.mockReturnValue(Promise.resolve()); + return addLegacyEcidToPayload(payload).then(() => { + expect(addEcidToPayload).not.toHaveBeenCalled(); + }); + }); + it("adds legacy ECID to payload if legacy ECID exists", () => { + payload.hasIdentity.mockReturnValue(false); + return addLegacyEcidToPayload(payload).then(() => { + expect(addEcidToPayload).toHaveBeenCalledWith(payload, "legacy@adobe"); + }); + }); + it("does not add legacy ECID to payload if the payload already has an ecid", async () => { + payload.hasIdentity.mockReturnValue(true); + const response = await addLegacyEcidToPayload(payload); + expect(getLegacyEcid).not.toHaveBeenCalled(); + expect(response).toBeUndefined(); + }); +}); diff --git a/vtest/unit/specs/components/Identity/injectAddQueryStringIdentityToPayload.spec.js b/vtest/unit/specs/components/Identity/injectAddQueryStringIdentityToPayload.spec.js new file mode 100644 index 000000000..d3cc35bd4 --- /dev/null +++ b/vtest/unit/specs/components/Identity/injectAddQueryStringIdentityToPayload.spec.js @@ -0,0 +1,177 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectAddQueryStringIdentityToPayload from "../../../../../src/components/Identity/injectAddQueryStringIdentityToPayload.js"; +import createDataCollectionRequestPayload from "../../../../../src/utils/request/createDataCollectionRequestPayload.js"; +import createIdentityRequestPayload from "../../../../../src/components/Identity/getIdentity/createIdentityRequestPayload.js"; +import createConsentRequestPayload from "../../../../../src/components/Consent/createConsentRequestPayload.js"; + +describe("Identity::injectAddQueryStringIdentityToPayload", () => { + let locationSearch; + let dateProvider; + let orgId; + let logger; + let date; + let payload; + beforeEach(() => { + dateProvider = () => date; + locationSearch = + "?foo=bar&adobe_mc=TS%3D1641432103%7CMCMID%3D77094828402023918047117570965393734545%7CMCORGID%3DFAF554945B90342F0A495E2C%40AdobeOrg&a=b"; + date = new Date(1641432103 * 1000); + orgId = "FAF554945B90342F0A495E2C@AdobeOrg"; + logger = { + info: vi.fn(), + warn: vi.fn(), + }; + }); + const run = () => { + injectAddQueryStringIdentityToPayload({ + locationSearch, + dateProvider, + orgId, + logger, + })(payload); + }; + [ + [ + "DataCollection", + createDataCollectionRequestPayload, + (p) => p.xdm.identityMap, + ], + ["Identity", createIdentityRequestPayload, (p) => p.xdm.identityMap], + ["Consent", createConsentRequestPayload, (p) => p.identityMap], + ].forEach(([type, createPayload, getIdentityMap]) => { + describe(`with ${type} payload`, () => { + beforeEach(() => { + payload = createPayload(); + }); + it("adds the identity", () => { + run(); + expect(getIdentityMap(payload.toJSON())).toEqual({ + ECID: [ + { + id: "77094828402023918047117570965393734545", + }, + ], + }); + }); + it("doesn't overwrite an existing identity in the identityMap", () => { + payload.addIdentity("ECID", { + id: "1234", + }); + run(); + expect(getIdentityMap(payload.toJSON())).toEqual({ + ECID: [ + { + id: "1234", + }, + ], + }); + }); + }); + }); + describe("with mock payload", () => { + beforeEach(() => { + payload = { + addIdentity: vi.fn(), + hasIdentity: vi.fn(), + }; + payload.hasIdentity.mockReturnValue(false); + }); + it("doesn't do anything when there is no query string", () => { + locationSearch = ""; + run(); + expect(payload.addIdentity).not.toHaveBeenCalled(); + }); + it("doesn't do anything when there is no TS parameter", () => { + locationSearch = `?adobe_mc=${encodeURIComponent("MCMID=myid|MCORG=myorg")}`; + run(); + expect(payload.addIdentity).not.toHaveBeenCalled(); + }); + it("doesn't do anything when there is no MCMID parameter", () => { + locationSearch = `?adobe_mc=${encodeURIComponent("TS=1000|MCORG=myorg")}`; + run(); + expect(payload.addIdentity).not.toHaveBeenCalled(); + }); + it("doesn't do anything when there is no MCORG parameter", () => { + locationSearch = `?adobe_mc=${encodeURIComponent("TS=1000|MCMID=myid")}`; + run(); + expect(payload.addIdentity).not.toHaveBeenCalled(); + }); + it("doesn't do anything with an expired link", () => { + date = new Date((1641432103 + 301) * 1000); + run(); + expect(payload.addIdentity).not.toHaveBeenCalled(); + }); + it("adds the identity for an exactly 5 minute old link", () => { + date = new Date((1641432103 + 300) * 1000); + run(); + expect(payload.addIdentity).toHaveBeenCalled(); + }); + it("doesn't do anything when the orgs don't match", () => { + orgId = "myotherorg"; + run(); + expect(payload.addIdentity).not.toHaveBeenCalled(); + }); + [ + "adobe_mc=", + "adobe_mc=a", + "adobe_mc=a%3Db", + "adobe_mc=%7C%7C", + `adobe_mc=${encodeURIComponent("TS=foo|MCMID=12345|MCORGID=FAF554945B90342F0A495E2C@AdobeOrg")}`, + `adobe_mc=${encodeURIComponent("TS=1641432103|MCMID=|MCORGID=FAF554945B90342F0A495E2C@AdobeOrg")}`, + `adobe_mc=${encodeURIComponent("TS|MCMID")}`, + ].forEach((value) => { + it(`handles garbage parameter value: ${value}`, () => { + locationSearch = `?${value}`; + run(); + expect(payload.addIdentity).not.toHaveBeenCalled(); + expect(logger.info).toHaveBeenNthCalledWith( + 1, + expect.stringMatching(/invalid/), + ); + }); + }); + it("reads an identity from visitor", () => { + locationSearch = + "?adobe_mc=MCMID%3D06387190804794960331430905673364101813%7CMCORGID%3D5BFE274A5F6980A50A495C08%2540AdobeOrg%7CTS%3D1653516560"; + orgId = "5BFE274A5F6980A50A495C08@AdobeOrg"; + date = new Date(1653516560 * 1000); + run(); + expect(payload.addIdentity).toHaveBeenNthCalledWith(1, "ECID", { + id: "06387190804794960331430905673364101813", + }); + }); + it("handles multiple copies of the adobe_mc param", () => { + locationSearch = + "?adobe_mc=MCMID%3Dfirst%7CMCORGID%3Dabc%7CTS%3D1653516560&adobe_mc=MCMID%3Dsecond%7CMCORGID%3Dabc%7CTS%3D1653516560"; + orgId = "abc"; + date = new Date(1653516560 * 1000); + run(); + expect(payload.addIdentity).toHaveBeenNthCalledWith(1, "ECID", { + id: "second", + }); + expect(logger.warn).toHaveBeenCalled(); + }); + it("handles multiple copies of the adobe_mc param with empty param", () => { + locationSearch = + "?adobe_mc=MCMID%3Dfirst%7CMCORGID%3Dabc%7CTS%3D1653516560&adobe_mc="; + orgId = "abc"; + date = new Date(1653516560 * 1000); + run(); + expect(payload.addIdentity).not.toHaveBeenCalled(); + expect(logger.warn).toHaveBeenCalled(); + expect(logger.info).toHaveBeenCalled(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Identity/injectAwaitIdentityCookie.spec.js b/vtest/unit/specs/components/Identity/injectAwaitIdentityCookie.spec.js new file mode 100644 index 000000000..3e4b58ef2 --- /dev/null +++ b/vtest/unit/specs/components/Identity/injectAwaitIdentityCookie.spec.js @@ -0,0 +1,87 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectAwaitIdentityCookie from "../../../../../src/components/Identity/injectAwaitIdentityCookie.js"; + +describe("Identity::injectAwaitIdentityCookie", () => { + let identityCookieExists; + let awaitIdentityCookie; + let runOnResponseCallbacks; + let runOnRequestFailureCallbacks; + let onResponse; + let onRequestFailure; + let logger; + beforeEach(() => { + identityCookieExists = true; + const onResponseCallbacks = []; + runOnResponseCallbacks = () => { + onResponseCallbacks.forEach((callback) => { + callback(); + }); + }; + onResponse = (callback) => onResponseCallbacks.push(callback); + const onRequestFailureCallbacks = []; + runOnRequestFailureCallbacks = () => { + onRequestFailureCallbacks.forEach((callback) => { + callback(); + }); + }; + onRequestFailure = (callback) => onRequestFailureCallbacks.push(callback); + logger = { + warn: vi.fn(), + }; + awaitIdentityCookie = injectAwaitIdentityCookie({ + orgId: "org@adobe", + doesIdentityCookieExist: () => identityCookieExists, + logger, + }); + }); + it("resolves promise if identity cookie exists after response", () => { + const promise = awaitIdentityCookie({ + onResponse, + onRequestFailure, + }); + runOnResponseCallbacks(); + expect(logger.warn).not.toHaveBeenCalled(); + return promise; + }); + it("rejects promise if identity cookie does not exist after response and logs warning", () => { + identityCookieExists = false; + const promise = awaitIdentityCookie({ + onResponse, + onRequestFailure, + }); + runOnResponseCallbacks(); + expect(logger.warn).toHaveBeenCalled(); + return expect(promise).rejects.toThrowError(/Identity cookie not found/i); + }); + it("resolves promise if identity cookie exists after request failure", () => { + const promise = awaitIdentityCookie({ + onResponse, + onRequestFailure, + }); + runOnRequestFailureCallbacks(); + expect(logger.warn).not.toHaveBeenCalled(); + return promise; + }); + it("rejects promise if identity cookie does not exist after request failure", () => { + identityCookieExists = false; + const promise = awaitIdentityCookie({ + onResponse, + onRequestFailure, + }); + runOnRequestFailureCallbacks(); + expect(logger.warn).not.toHaveBeenCalled(); + return expect(promise).rejects.toThrowError(/Identity cookie not found/i); + }); +}); diff --git a/vtest/unit/specs/components/Identity/injectEnsureSingleIdentity.spec.js b/vtest/unit/specs/components/Identity/injectEnsureSingleIdentity.spec.js new file mode 100644 index 000000000..dfa986a89 --- /dev/null +++ b/vtest/unit/specs/components/Identity/injectEnsureSingleIdentity.spec.js @@ -0,0 +1,212 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectEnsureSingleIdentity from "../../../../../src/components/Identity/injectEnsureSingleIdentity.js"; +import { defer } from "../../../../../src/utils/index.js"; +import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; + +describe("Identity::injectEnsureSingleIdentity", () => { + let doesIdentityCookieExist; + let setDomainForInitialIdentityPayload; + let addLegacyEcidToPayload; + let awaitIdentityCookie; + let logger; + let ensureSingleIdentity; + let sentIndex; + let receivedIndex; + let requests; + let requestSentStatusByIndex; + let awaitIdentityDeferreds; + let onResponse; + let onRequestFailure; + let doesIdentityCookieExistBoolean; + beforeEach(() => { + logger = { + info: vi.fn(), + }; + sentIndex = 0; + receivedIndex = 0; + requests = []; + requestSentStatusByIndex = []; + awaitIdentityDeferreds = []; + doesIdentityCookieExistBoolean = false; + setDomainForInitialIdentityPayload = (request) => { + request.setUseIdThirdPartyDomain(); + }; + addLegacyEcidToPayload = (payload) => { + payload.addIdentity("ECID", { + id: "ABC123", + }); + return Promise.resolve(); + }; + awaitIdentityCookie = () => { + const deferred = defer(); + awaitIdentityDeferreds.push(deferred); + return deferred.promise; + }; + doesIdentityCookieExist = () => doesIdentityCookieExistBoolean; + }); + const setup = () => { + ensureSingleIdentity = injectEnsureSingleIdentity({ + doesIdentityCookieExist, + setDomainForInitialIdentityPayload, + addLegacyEcidToPayload, + awaitIdentityCookie, + logger, + }); + }; + const sendRequest = () => { + const requestPayload = { + addIdentity: vi.fn(), + }; + const request = { + getPayload: vi.fn().mockReturnValue(requestPayload), + setIsIdentityEstablished: vi.fn().mockReturnValue(undefined), + setUseIdThirdPartyDomain: vi.fn().mockReturnValue(undefined), + }; + requests.push(request); + onResponse = vi.fn(); + onRequestFailure = vi.fn(); + const i = sentIndex; + requestSentStatusByIndex.push(false); + ensureSingleIdentity({ + request, + onResponse, + onRequestFailure, + }).then(() => { + requestSentStatusByIndex[i] = true; + }); + sentIndex += 1; + }; + const simulateResponseWithIdentity = () => { + doesIdentityCookieExist = true; + awaitIdentityDeferreds[receivedIndex].resolve(); + receivedIndex += 1; + }; + const simulateResponseWithoutIdentity = () => { + awaitIdentityDeferreds[receivedIndex].reject(); + receivedIndex += 1; + }; + it("allows first request to proceed and pauses subsequent requests until identity cookie exists", () => { + setup(); + return Promise.resolve() + .then(() => { + sendRequest(); + sendRequest(); + sendRequest(); + return flushPromiseChains(); + }) + .then(() => { + expect(requestSentStatusByIndex).toEqual([true, false, false]); + simulateResponseWithIdentity(); + return flushPromiseChains(); + }) + .then(() => { + expect(requestSentStatusByIndex).toEqual([true, true, true]); + expect(requests[0].setUseIdThirdPartyDomain).toHaveBeenCalled(); + expect(requests[1].setUseIdThirdPartyDomain).not.toHaveBeenCalled(); + expect(requests[2].setUseIdThirdPartyDomain).not.toHaveBeenCalled(); + expect(requests[0].getPayload().addIdentity).toHaveBeenCalled(); + expect(requests[1].getPayload().addIdentity).not.toHaveBeenCalled(); + expect(requests[2].getPayload().addIdentity).not.toHaveBeenCalled(); + sendRequest(); + return flushPromiseChains(); + }) + .then(() => { + expect(requests[3].setUseIdThirdPartyDomain).not.toHaveBeenCalled(); + expect(requests[3].getPayload().addIdentity).not.toHaveBeenCalled(); + expect(requestSentStatusByIndex[3]).toEqual(true); + }); + }); + it("allows the second request to be called if the first doesn't set the cookie, but still holds up the third", () => { + setup(); + return Promise.resolve() + .then(() => { + sendRequest(); + sendRequest(); + sendRequest(); + sendRequest(); + return flushPromiseChains(); + }) + .then(() => { + expect(requestSentStatusByIndex).toEqual([true, false, false, false]); + simulateResponseWithoutIdentity(); + return flushPromiseChains(); + }) + .then(() => { + expect(requestSentStatusByIndex).toEqual([true, true, false, false]); + simulateResponseWithIdentity(); + return flushPromiseChains(); + }) + .then(() => { + expect(requestSentStatusByIndex).toEqual([true, true, true, true]); + expect(requests[0].setUseIdThirdPartyDomain).toHaveBeenCalled(); + expect(requests[1].setUseIdThirdPartyDomain).toHaveBeenCalled(); + expect(requests[2].setUseIdThirdPartyDomain).not.toHaveBeenCalled(); + expect(requests[3].setUseIdThirdPartyDomain).not.toHaveBeenCalled(); + expect(requests[0].getPayload().addIdentity).toHaveBeenCalled(); + expect(requests[1].getPayload().addIdentity).toHaveBeenCalled(); + expect(requests[2].getPayload().addIdentity).not.toHaveBeenCalled(); + expect(requests[3].getPayload().addIdentity).not.toHaveBeenCalled(); + }); + }); + it("logs messages", () => { + setup(); + return Promise.resolve() + .then(() => { + sendRequest(); + return flushPromiseChains(); + }) + .then(() => { + expect(logger.info).not.toHaveBeenCalled(); + sendRequest(); + return flushPromiseChains(); + }) + .then(() => { + expect(logger.info).toHaveBeenCalledWith( + "Delaying request while retrieving ECID from server.", + ); + simulateResponseWithIdentity(); + return flushPromiseChains(); + }) + .then(() => { + expect(logger.info).toHaveBeenCalledWith( + "Resuming previously delayed request.", + ); + }); + }); + it("sends a request without third-party domain or legacy ECID if we have an identity cookie", () => { + doesIdentityCookieExistBoolean = true; + setup(); + return Promise.resolve() + .then(() => { + sendRequest(); + return flushPromiseChains(); + }) + .then(() => { + expect(requestSentStatusByIndex).toEqual([true]); + expect(requests[0].setUseIdThirdPartyDomain).not.toHaveBeenCalled(); + expect(requests[0].getPayload().addIdentity).not.toHaveBeenCalled(); + }); + }); + it("calls awaitIdentityCookie with the correct parameters", () => { + awaitIdentityCookie = vi.fn(); + awaitIdentityCookie.mockReturnValue(Promise.resolve()); + setup(); + sendRequest(); + expect(awaitIdentityCookie).toHaveBeenCalledWith({ + onResponse, + onRequestFailure, + }); + }); +}); diff --git a/vtest/unit/specs/components/Identity/injectHandleResponseForIdSyncs.spec.js b/vtest/unit/specs/components/Identity/injectHandleResponseForIdSyncs.spec.js new file mode 100644 index 000000000..f0cfa9dd5 --- /dev/null +++ b/vtest/unit/specs/components/Identity/injectHandleResponseForIdSyncs.spec.js @@ -0,0 +1,35 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import injectHandleResponseForIdSyncs from "../../../../../src/components/Identity/injectHandleResponseForIdSyncs.js"; + +describe("Identity::injectHandleResponseForIdSyncs", () => { + it("processes ID syncs", () => { + const processIdSyncsPromise = Promise.resolve(); + const processIdSyncs = vi.fn().mockReturnValue(processIdSyncsPromise); + const idSyncPayloads = [ + { + type: "idSync", + }, + ]; + const response = { + getPayloadsByType: vi.fn().mockReturnValue(idSyncPayloads), + }; + const handleResponseForIdSyncs = injectHandleResponseForIdSyncs({ + processIdSyncs, + }); + const result = handleResponseForIdSyncs(response); + expect(processIdSyncs).toHaveBeenCalledWith(idSyncPayloads); + expect(result).toBe(processIdSyncsPromise); + }); +}); diff --git a/vtest/unit/specs/components/Identity/injectProcessIdSyncs.spec.js b/vtest/unit/specs/components/Identity/injectProcessIdSyncs.spec.js new file mode 100644 index 000000000..64f2db146 --- /dev/null +++ b/vtest/unit/specs/components/Identity/injectProcessIdSyncs.spec.js @@ -0,0 +1,83 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectProcessIdSyncs from "../../../../../src/components/Identity/injectProcessIdSyncs.js"; + +describe("Identity::injectProcessIdSyncs", () => { + let fireReferrerHideableImage; + let logger; + let processIdSyncs; + beforeEach(() => { + fireReferrerHideableImage = vi.fn().mockReturnValue(Promise.resolve()); + logger = { + info: vi.fn(), + error: vi.fn(), + }; + processIdSyncs = injectProcessIdSyncs({ + fireReferrerHideableImage, + logger, + }); + }); + it("handles no ID syncs", () => { + return processIdSyncs([]).then(() => { + expect(fireReferrerHideableImage).not.toHaveBeenCalled(); + }); + }); + it("calls fireReferrerHideableImage for all ID syncs of type URL, and logs results", () => { + fireReferrerHideableImage.mockImplementation(({ url }) => { + return url === "http://test.zyx" ? Promise.resolve() : Promise.reject(); + }); + const identities = [ + { + type: "url", + id: 2097728, + spec: { + url: "http://test.abc", + hideReferrer: true, + }, + }, + { + type: "cookie", + spec: { + name: "testCookieIdSync", + value: "id\u003ds2", + domain: "", + ttl: 30, + }, + }, + { + type: "url", + id: 2097729, + spec: { + url: "http://test.zyx", + hideReferrer: false, + }, + }, + ]; + return processIdSyncs(identities).then(() => { + expect(fireReferrerHideableImage).toHaveBeenCalledWith({ + url: "http://test.abc", + hideReferrer: true, + }); + expect(fireReferrerHideableImage).toHaveBeenCalledWith({ + url: "http://test.zyx", + hideReferrer: false, + }); + expect(logger.info).toHaveBeenCalledWith( + "ID sync succeeded: http://test.zyx", + ); + expect(logger.error).toHaveBeenCalledWith( + "ID sync failed: http://test.abc", + ); + }); + }); +}); diff --git a/vtest/unit/specs/components/Identity/injectSetDomainForInitialIdentityPayload.spec.js b/vtest/unit/specs/components/Identity/injectSetDomainForInitialIdentityPayload.spec.js new file mode 100644 index 000000000..ce5d71324 --- /dev/null +++ b/vtest/unit/specs/components/Identity/injectSetDomainForInitialIdentityPayload.spec.js @@ -0,0 +1,57 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectSetDomainForInitialIdentityPayload from "../../../../../src/components/Identity/injectSetDomainForInitialIdentityPayload.js"; + +describe("Identity::injectSetDomainForInitialIdentityPayload", () => { + let request; + let thirdPartyCookiesEnabled; + let areThirdPartyCookiesSupportedByDefault; + let setDomainForInitialIdentityPayload; + const build = () => { + setDomainForInitialIdentityPayload = + injectSetDomainForInitialIdentityPayload({ + thirdPartyCookiesEnabled, + areThirdPartyCookiesSupportedByDefault, + }); + }; + beforeEach(() => { + request = { + setUseIdThirdPartyDomain: vi.fn(), + }; + areThirdPartyCookiesSupportedByDefault = vi.fn(); + }); + it("does not use third-party domain if third-party cookies are disabled", () => { + thirdPartyCookiesEnabled = false; + areThirdPartyCookiesSupportedByDefault.mockReturnValue(true); + build(); + setDomainForInitialIdentityPayload(request); + expect(request.setUseIdThirdPartyDomain).not.toHaveBeenCalled(); + }); + it("does not use third-party domain if third-party cookies are not supported by the browser by default", () => { + thirdPartyCookiesEnabled = true; + areThirdPartyCookiesSupportedByDefault.mockReturnValue(false); + build(); + setDomainForInitialIdentityPayload(request); + expect(areThirdPartyCookiesSupportedByDefault).toHaveBeenCalledWith(); + expect(request.setUseIdThirdPartyDomain).not.toHaveBeenCalled(); + }); + it("uses third-party domain if third-party cookies are enabled and supported by the browser by default", () => { + thirdPartyCookiesEnabled = true; + areThirdPartyCookiesSupportedByDefault.mockReturnValue(true); + build(); + setDomainForInitialIdentityPayload(request); + expect(areThirdPartyCookiesSupportedByDefault).toHaveBeenCalledWith(); + expect(request.setUseIdThirdPartyDomain).toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Identity/visitorService/awaitVisitorOptIn.spec.js b/vtest/unit/specs/components/Identity/visitorService/awaitVisitorOptIn.spec.js new file mode 100644 index 000000000..6507d621e --- /dev/null +++ b/vtest/unit/specs/components/Identity/visitorService/awaitVisitorOptIn.spec.js @@ -0,0 +1,79 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, afterAll, describe, it, expect } from "vitest"; +import awaitVisitorOptIn from "../../../../../../src/components/Identity/visitorService/awaitVisitorOptIn.js"; + +const logger = { + info: vi.fn(), +}; +describe("awaitVisitorOptIn", () => { + beforeEach(() => { + window.adobe = undefined; + }); + afterAll(() => { + window.adobe = undefined; + }); + describe("No legacy opt in object is present", () => { + it("should return promise resolved with undefined", () => { + return expect( + awaitVisitorOptIn({ + logger, + }), + ).resolves.toBe(undefined); + }); + }); + describe("Legacy opt in object is present and gives approval", () => { + it("should return promise resolved with undefined", () => { + window.adobe = { + optIn: { + fetchPermissions(callback) { + setTimeout(callback, 0); + }, + isApproved() { + return true; + }, + Categories: { + ECID: "ecid", + }, + }, + }; + return expect( + awaitVisitorOptIn({ + logger, + }), + ).resolves.toBe(undefined); + }); + }); + describe("Legacy opt in object is present and gives denial", () => { + it('should return promise rejected with new Error("Legacy opt-in was declined.")', () => { + window.adobe = { + optIn: { + fetchPermissions(callback) { + setTimeout(callback, 0); + }, + isApproved() { + return false; + }, + Categories: { + ECID: "ecid", + }, + }, + }; + return expect( + awaitVisitorOptIn({ + logger, + }), + ).rejects.toThrowError("Legacy opt-in was declined."); + }); + }); +}); diff --git a/vtest/unit/specs/components/Identity/visitorService/getVisitor.spec.js b/vtest/unit/specs/components/Identity/visitorService/getVisitor.spec.js new file mode 100644 index 000000000..09f03b6f1 --- /dev/null +++ b/vtest/unit/specs/components/Identity/visitorService/getVisitor.spec.js @@ -0,0 +1,33 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import getVisitor from "../../../../../../src/components/Identity/visitorService/getVisitor.js"; + +describe("getVisitor", () => { + let mockWindow; + beforeEach(() => { + mockWindow = {}; + }); + it("Returns Visitor function if Visitor is available and valid", () => { + mockWindow.Visitor = vi.fn(); + mockWindow.Visitor.getInstance = vi.fn(); + expect(getVisitor(mockWindow)).toEqual(mockWindow.Visitor); + }); + it("Returns false if Visitor is available but does not support getInstance", () => { + mockWindow.Visitor = vi.fn(); + expect(getVisitor(mockWindow)).toBe(false); + }); + it("Returns false if Visitor is not available", () => { + expect(getVisitor(mockWindow)).toBe(false); + }); +}); diff --git a/vtest/unit/specs/components/Identity/visitorService/injectGetEcidFromVisitor.spec.js b/vtest/unit/specs/components/Identity/visitorService/injectGetEcidFromVisitor.spec.js new file mode 100644 index 000000000..daa613a55 --- /dev/null +++ b/vtest/unit/specs/components/Identity/visitorService/injectGetEcidFromVisitor.spec.js @@ -0,0 +1,86 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, afterAll, describe, it, expect } from "vitest"; +import injectGetEcidFromVisitor from "../../../../../../src/components/Identity/visitorService/injectGetEcidFromVisitor.js"; + +const logger = { + info: vi.fn(), +}; +const Visitor = () => {}; +Visitor.getInstance = () => { + return { + getMarketingCloudVisitorID(cb) { + setTimeout(() => { + cb("ecid123"); + }, 0); + }, + }; +}; +const orgId = "456org"; +describe("getEcidFromVisitor", () => { + beforeEach(() => { + window.Visitor = undefined; + }); + afterAll(() => { + window.Visitor = undefined; + }); + describe("Visitor does not exist", () => { + it("should return promise resolved with undefined", () => { + const getEcidFromVisitor = injectGetEcidFromVisitor({ + logger, + orgId, + }); + return expect(getEcidFromVisitor()).resolves.toBe(undefined); + }); + }); + describe("Visitor exists; awaitVisitorOptIn resolves the promise", () => { + it("should return promise resolved with ecid123", () => { + window.Visitor = Visitor; + const awaitVisitorOptIn = () => { + return Promise.resolve(); + }; + const getEcidFromVisitor = injectGetEcidFromVisitor({ + logger, + orgId, + awaitVisitorOptIn, + }); + return expect(getEcidFromVisitor()).resolves.toBe("ecid123"); + }); + }); + describe("Visitor exists; awaitVisitorOptIn rejects the promise", () => { + it("should return promise resolved with undefined", () => { + window.Visitor = Visitor; + const awaitVisitorOptIn = () => { + return Promise.reject(); + }; + const getEcidFromVisitor = injectGetEcidFromVisitor({ + logger, + orgId, + awaitVisitorOptIn, + }); + return expect(getEcidFromVisitor()).resolves.toBe(undefined); + }); + }); + it("should find Visitor if it was defined after Web SDK initialization.", () => { + const awaitVisitorOptIn = () => { + return Promise.resolve(); + }; + const getEcidFromVisitor = injectGetEcidFromVisitor({ + logger, + orgId, + awaitVisitorOptIn, + }); + window.Visitor = Visitor; + return expect(getEcidFromVisitor()).resolves.toBe("ecid123"); + }); +}); diff --git a/vtest/unit/specs/components/LibraryInfo/index.spec.js b/vtest/unit/specs/components/LibraryInfo/index.spec.js new file mode 100644 index 000000000..5cba3f75e --- /dev/null +++ b/vtest/unit/specs/components/LibraryInfo/index.spec.js @@ -0,0 +1,40 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, describe, it, expect } from "vitest"; +import createLibraryInfo from "../../../../../src/components/LibraryInfo/index.js"; + +describe("LibraryInfo", () => { + let toolsMock; + beforeEach(() => { + toolsMock = { + config: { + foo: "bar", + }, + componentRegistry: { + getCommandNames: () => ["bar"], + getComponentNames: () => ["ComponentA", "ComponentB"], + }, + }; + }); + it("returns library, command, and config information", () => { + expect(createLibraryInfo(toolsMock).commands.getLibraryInfo.run()).toEqual({ + libraryInfo: { + version: `__VERSION__`, + configs: { + foo: "bar", + }, + commands: ["bar", "configure", "setDebug"], + components: ["ComponentA", "ComponentB"], + }, + }); + }); +}); diff --git a/vtest/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js b/vtest/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js new file mode 100644 index 000000000..d91683a55 --- /dev/null +++ b/vtest/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js @@ -0,0 +1,322 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createGetInstance from "../../../../../src/components/MediaAnalyticsBridge/createGetInstance.js"; + +describe("createGetInstance", () => { + const logger = { + warn: vi.fn(), + }; + let trackMediaSession; + let trackMediaEvent; + let uuid; + beforeEach(() => { + trackMediaSession = vi.fn(); + trackMediaEvent = vi.fn(); + uuid = vi.fn().mockReturnValue("1234-5678-9101-1121"); + }); + it("should return an object", () => { + const result = createGetInstance({ + logger, + trackMediaSession, + trackMediaEvent, + uuid, + }); + expect(typeof result).toBe("object"); + expect(typeof result.trackSessionStart).toBe("function"); + expect(typeof result.trackPlay).toBe("function"); + expect(typeof result.trackComplete).toBe("function"); + expect(typeof result.trackPause).toBe("function"); + expect(typeof result.trackError).toBe("function"); + expect(typeof result.trackEvent).toBe("function"); + expect(typeof result.trackSessionEnd).toBe("function"); + expect(typeof result.updatePlayhead).toBe("function"); + expect(typeof result.updateQoEObject).toBe("function"); + expect(typeof result.destroy).toBe("function"); + }); + it("when play is called", () => { + const result = createGetInstance({ + logger, + trackMediaSession, + trackMediaEvent, + uuid, + }); + result.trackSessionStart({ + sessionDetails: {}, + }); + result.trackPlay(); + expect(trackMediaEvent).toHaveBeenCalledWith({ + playerId: "1234-5678-9101-1121", + xdm: { + eventType: "media.play", + mediaCollection: {}, + }, + }); + }); + it("when pause is called", () => { + const result = createGetInstance({ + logger, + trackMediaSession, + trackMediaEvent, + uuid, + }); + result.trackSessionStart({ + sessionDetails: {}, + }); + result.trackPause(); + expect(trackMediaEvent).toHaveBeenCalledWith({ + playerId: "1234-5678-9101-1121", + xdm: { + eventType: "media.pauseStart", + mediaCollection: {}, + }, + }); + }); + it("when sessionStart is called", () => { + const result = createGetInstance({ + logger, + trackMediaSession, + trackMediaEvent, + uuid, + }); + const sessionDetails = { + name: "test", + friendlyName: "test1", + length: "test2", + streamType: "vod", + contentType: "video/mp4", + }; + const meta = { + isUserLoggedIn: "false", + tvStation: "Sample TV station", + programmer: "Sample programmer", + assetID: "/uri-reference", + "a.media.episode": "episode1", + }; + result.trackSessionStart( + { + sessionDetails, + }, + meta, + ); + expect(trackMediaSession).toHaveBeenCalledWith({ + playerId: "1234-5678-9101-1121", + getPlayerDetails: expect.any(Function), + xdm: { + eventType: "media.sessionStart", + mediaCollection: { + sessionDetails: { + name: "test", + friendlyName: "test1", + length: "test2", + streamType: "vod", + contentType: "video/mp4", + episode: "episode1", + }, + customMetadata: [ + { + name: "isUserLoggedIn", + value: "false", + }, + { + name: "tvStation", + value: "Sample TV station", + }, + { + name: "programmer", + value: "Sample programmer", + }, + { + name: "assetID", + value: "/uri-reference", + }, + ], + }, + }, + }); + }); + it("when trackError is called", () => { + const result = createGetInstance({ + logger, + trackMediaSession, + trackMediaEvent, + uuid, + }); + result.trackSessionStart({ + sessionDetails: {}, + }); + result.trackError("error"); + expect(trackMediaEvent).toHaveBeenCalledWith({ + playerId: "1234-5678-9101-1121", + xdm: { + eventType: "media.error", + mediaCollection: { + errorDetails: { + name: "error", + source: "player", + }, + }, + }, + }); + expect(logger.warn).toHaveBeenCalled(); + }); + it("when trackComplete is called", () => { + const result = createGetInstance({ + logger, + trackMediaSession, + trackMediaEvent, + uuid, + }); + result.trackSessionStart({ + sessionDetails: {}, + }); + result.trackComplete(); + expect(trackMediaEvent).toHaveBeenCalledWith({ + playerId: "1234-5678-9101-1121", + xdm: { + eventType: "media.sessionComplete", + mediaCollection: {}, + }, + }); + }); + it("when trackSessionEnd is called", () => { + const result = createGetInstance({ + logger, + trackMediaSession, + trackMediaEvent, + uuid, + }); + result.trackSessionStart({ + sessionDetails: {}, + }); + result.trackSessionEnd(); + expect(trackMediaEvent).toHaveBeenCalledWith({ + playerId: "1234-5678-9101-1121", + xdm: { + eventType: "media.sessionEnd", + mediaCollection: {}, + }, + }); + }); + it("when state update is called", () => { + const result = createGetInstance({ + logger, + trackMediaSession, + trackMediaEvent, + uuid, + }); + const state = { + name: "muted", + }; + result.trackSessionStart({ + sessionDetails: {}, + }); + result.trackEvent("stateStart", state); + expect(trackMediaEvent).toHaveBeenCalledWith({ + playerId: "1234-5678-9101-1121", + xdm: { + eventType: "media.statesUpdate", + mediaCollection: { + statesStart: [ + { + name: "muted", + }, + ], + }, + }, + }); + }); + it("when state update is called", () => { + const result = createGetInstance({ + logger, + trackMediaSession, + trackMediaEvent, + uuid, + }); + const state = { + name: "muted", + }; + result.trackSessionStart({ + sessionDetails: {}, + }); + result.trackEvent("stateEnd", state); + expect(trackMediaEvent).toHaveBeenCalledWith({ + playerId: "1234-5678-9101-1121", + xdm: { + eventType: "media.statesUpdate", + mediaCollection: { + statesEnd: [ + { + name: "muted", + }, + ], + }, + }, + }); + }); + it("when track adds is called add get's converted correctly", () => { + const result = createGetInstance({ + logger, + trackMediaSession, + trackMediaEvent, + uuid, + }); + const advertisingDetails = { + friendlyName: "test", + name: "trst1", + podPosition: 2, + length: 100, + }; + const adContextData = { + affiliate: "Sample affiliate 2", + campaign: "Sample ad campaign 2", + "a.media.ad.advertiser": "Sample Advertiser 2", + "a.media.ad.campaign": "csmpaign2", + }; + result.trackSessionStart({ + sessionDetails: {}, + }); + result.trackEvent( + "adStart", + { + advertisingDetails, + }, + adContextData, + ); + expect(trackMediaEvent).toHaveBeenCalledWith({ + playerId: "1234-5678-9101-1121", + xdm: { + eventType: "media.adStart", + mediaCollection: { + advertisingDetails: { + friendlyName: "test", + name: "trst1", + podPosition: 2, + length: 100, + advertiser: "Sample Advertiser 2", + campaignID: "csmpaign2", + }, + customMetadata: [ + { + name: "affiliate", + value: "Sample affiliate 2", + }, + { + name: "campaign", + value: "Sample ad campaign 2", + }, + ], + }, + }, + }); + }); +}); diff --git a/vtest/unit/specs/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.spec.js b/vtest/unit/specs/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.spec.js new file mode 100644 index 000000000..7defbba0b --- /dev/null +++ b/vtest/unit/specs/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.spec.js @@ -0,0 +1,110 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createMediaAnalyticsBridgeComponent from "../../../../../src/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.js"; + +describe("MediaAnalyticsBridge::createMediaAnalyticsBridgeComponent", () => { + const config = { + streamingMedia: { + channel: "testChannel", + playerName: "testPlayerName", + appVersion: "testAppVersion", + }, + }; + let logger; + let mediaAnalyticsBridgeComponent; + let trackMediaEvent; + let mediaResponseHandler; + let trackMediaSession; + let createMediaHelper; + let createGetInstance; + const build = (configs) => { + mediaAnalyticsBridgeComponent = createMediaAnalyticsBridgeComponent({ + config: configs, + logger, + trackMediaEvent, + mediaResponseHandler, + trackMediaSession, + createMediaHelper, + createGetInstance, + }); + }; + beforeEach(() => { + logger = { + info: vi.fn(), + }; + mediaResponseHandler = vi.fn(); + trackMediaEvent = vi.fn(); + trackMediaSession = vi.fn(); + createMediaHelper = vi.fn(); + createGetInstance = vi.fn(); + build(config); + }); + it("should reject promise when called with invalid config", async () => { + build({}); + const getMediaAnalyticsTracker = + mediaAnalyticsBridgeComponent.commands.getMediaAnalyticsTracker; + return expect(getMediaAnalyticsTracker.run()).rejects.toThrowError(); + }); + it("should call createGetInstance when getInstance Media API is called", async () => { + build(config); + const { getMediaAnalyticsTracker } = mediaAnalyticsBridgeComponent.commands; + const mediaApi = await getMediaAnalyticsTracker.run(); + mediaApi.getInstance(); + expect(createGetInstance).toHaveBeenCalled(); + }); + it("should call onBeforeMediaEvent when onBeforeEvent is called with legacy flag", async () => { + build(config); + const getPlayerDetails = () => {}; + const { onBeforeEvent } = mediaAnalyticsBridgeComponent.lifecycle; + const mediaOptions = { + legacy: true, + playerId: "testPlayerId", + getPlayerDetails, + }; + const onResponseHandler = (onResponse) => { + onResponse({ + response: {}, + }); + }; + onBeforeEvent({ + mediaOptions, + onResponse: onResponseHandler, + }); + expect(mediaResponseHandler).toHaveBeenCalledWith({ + getPlayerDetails, + playerId: "testPlayerId", + response: {}, + }); + }); + it("should not call onBeforeMediaEvent when onBeforeEvent is called without legacy flag", async () => { + build(config); + const getPlayerDetails = () => {}; + const { onBeforeEvent } = mediaAnalyticsBridgeComponent.lifecycle; + const mediaOptions = { + legacy: false, + playerId: "testPlayerId", + getPlayerDetails, + }; + const onResponseHandler = (onResponse) => { + onResponse({ + response: {}, + }); + }; + onBeforeEvent({ + mediaOptions, + onResponse: onResponseHandler, + }); + expect(mediaResponseHandler).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/MediaAnalyticsBridge/createMediaHelper.spec.js b/vtest/unit/specs/components/MediaAnalyticsBridge/createMediaHelper.spec.js new file mode 100644 index 000000000..79ba7268a --- /dev/null +++ b/vtest/unit/specs/components/MediaAnalyticsBridge/createMediaHelper.spec.js @@ -0,0 +1,213 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createMediaHelper from "../../../../../src/components/MediaAnalyticsBridge/createMediaHelper.js"; + +describe("createMediaHelper", () => { + let logger; + let mediaHelper; + beforeEach(() => { + logger = { + warn: vi.fn(), + }; + mediaHelper = createMediaHelper({ + logger, + }); + }); + describe("createMediaObject", () => { + it("should return a valid media object when called with valid arguments", () => { + const friendlyName = "testFriendlyName"; + const name = "testName"; + const length = 120; + const contentType = "video/mp4"; + const streamType = "VOD"; + const expectedResult = { + sessionDetails: { + friendlyName, + name, + length, + contentType, + streamType, + }, + }; + const result = mediaHelper.createMediaObject( + friendlyName, + name, + length, + contentType, + streamType, + ); + expect(result).toEqual(expectedResult); + }); + it("should log a warning and return an empty object when validation fails", () => { + const friendlyName = ""; + const name = ""; + const length = "invalid"; + const contentType = ""; + const streamType = ""; + const expectedResult = {}; + const result = mediaHelper.createMediaObject( + friendlyName, + name, + length, + contentType, + streamType, + ); + expect(result).toEqual(expectedResult); + expect(logger.warn).toHaveBeenCalled(); + }); + }); + describe("createAdBreakObject", () => { + it("should return a valid ad break object when called with valid arguments", () => { + const name = "testAdBreak"; + const position = 1; + const startTime = 120; + const expectedResult = { + advertisingPodDetails: { + friendlyName: name, + offset: position, + index: startTime, + }, + }; + const result = mediaHelper.createAdBreakObject(name, position, startTime); + expect(result).toEqual(expectedResult); + }); + it("should log a warning and return an empty object when validation fails", () => { + const name = ""; + const position = "invalid"; + const startTime = "invalid"; + const expectedResult = {}; + const result = mediaHelper.createAdBreakObject(name, position, startTime); + expect(result).toEqual(expectedResult); + expect(logger.warn).toHaveBeenCalled(); + }); + }); + describe("createAdObject", () => { + it("should return a valid ad object when called with valid arguments", () => { + const name = "testAd"; + const id = "testId"; + const position = 1; + const length = 30; + const expectedResult = { + advertisingDetails: { + friendlyName: name, + name: id, + podPosition: position, + length, + }, + }; + const result = mediaHelper.createAdObject(name, id, position, length); + expect(result).toEqual(expectedResult); + }); + it("should log a warning and return an empty object when validation fails", () => { + const name = ""; + const id = ""; + const position = "invalid"; + const length = "invalid"; + const expectedResult = {}; + const result = mediaHelper.createAdObject(name, id, position, length); + expect(result).toEqual(expectedResult); + expect(logger.warn).toHaveBeenCalled(); + }); + }); + describe("createChapterObject", () => { + it("should return a valid chapter object when called with valid arguments", () => { + const name = "testChapter"; + const position = 1; + const length = 30; + const startTime = 120; + const expectedResult = { + chapterDetails: { + friendlyName: name, + offset: position, + length, + index: startTime, + }, + }; + const result = mediaHelper.createChapterObject( + name, + position, + length, + startTime, + ); + expect(result).toEqual(expectedResult); + }); + it("should log a warning and return an empty object when validation fails", () => { + const name = ""; + const position = "invalid"; + const length = "invalid"; + const startTime = "invalid"; + const expectedResult = {}; + const result = mediaHelper.createChapterObject( + name, + position, + length, + startTime, + ); + expect(result).toEqual(expectedResult); + expect(logger.warn).toHaveBeenCalled(); + }); + }); + describe("createStateObject", () => { + it("should return a valid state object when called with valid arguments", () => { + const stateName = "testState"; + const expectedResult = { + name: stateName, + }; + const result = mediaHelper.createStateObject(stateName); + expect(result).toEqual(expectedResult); + }); + it("should log a warning and return an empty object when validation fails", () => { + const stateName = "invalid state name"; + const expectedResult = {}; + const result = mediaHelper.createStateObject(stateName); + expect(result).toEqual(expectedResult); + expect(logger.warn).toHaveBeenCalled(); + }); + }); + describe("createQoEObject", () => { + it("should return a valid QOE object when called with valid arguments", () => { + const bitrate = 5000; + const droppedFrames = 10; + const framesPerSecond = 30; + const timeToStart = 2; + const expectedResult = { + bitrate, + droppedFrames, + framesPerSecond, + timeToStart, + }; + const result = mediaHelper.createQoEObject( + bitrate, + droppedFrames, + framesPerSecond, + timeToStart, + ); + expect(result).toEqual(expectedResult); + }); + it("should log a warning and return an empty object when validation fails", () => { + const bitrate = "invalid"; + const droppedFrames = "invalid"; + const fps = "invalid"; + const startupTime = "invalid"; + const expectedResult = {}; + const result = mediaHelper.createQoEObject( + bitrate, + droppedFrames, + fps, + startupTime, + ); + expect(result).toEqual(expectedResult); + expect(logger.warn).toHaveBeenCalled(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createActionsProvider.spec.js b/vtest/unit/specs/components/Personalization/createActionsProvider.spec.js new file mode 100644 index 000000000..73d89e71f --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createActionsProvider.spec.js @@ -0,0 +1,96 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createActionsProvider from "../../../../../src/components/Personalization/createActionsProvider.js"; + +describe("createActionsProvider", () => { + let actionsProvider; + let logger; + beforeEach(() => { + logger = { + warn: vi.fn(), + error: vi.fn(), + info: vi.fn(), + }; + logger.enabled = true; + actionsProvider = createActionsProvider({ + modules: { + something: { + eat: () => Promise.resolve("yum"), + sleep: () => Promise.resolve(), + exercise: () => Promise.resolve(), + }, + }, + preprocessors: { + something: [(action) => action], + superfluous: [ + (action) => action, + (action) => action, + (action) => action, + ], + }, + logger, + }); + }); + it("executes appropriate action", () => { + const actionDetails = { + schema: "something", + type: "eat", + itWorked: true, + }; + actionsProvider.executeAction(actionDetails).then((result) => { + expect(result).toEqual("yum"); + expect(logger.info).toHaveBeenNthCalledWith( + 1, + expect.stringContaining( + `Action ${JSON.stringify(actionDetails)} executed.`, + ), + ); + }); + }); + it("throws error if missing schema", () => { + const actionDetails = { + schema: "hidden-valley", + type: "truckee", + itWorked: true, + }; + actionsProvider.executeAction(actionDetails).catch((error) => { + expect(error.message).toEqual( + `Action "truckee" not found for schema "hidden-valley"`, + ); + expect(logger.warn).toHaveBeenNthCalledWith( + 1, + expect.stringContaining( + `Failed to execute action ${JSON.stringify(actionDetails)}.`, + ), + ); + }); + }); + it("throws error if missing action", () => { + const actionDetails = { + schema: "something", + type: "truckee", + itWorked: true, + }; + actionsProvider.executeAction(actionDetails).catch((error) => { + expect(error.message).toEqual( + `Action "truckee" not found for schema "something"`, + ); + expect(logger.warn).toHaveBeenNthCalledWith( + 1, + expect.stringContaining( + `Failed to execute action ${JSON.stringify(actionDetails)}.`, + ), + ); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createApplyPropositions.spec.js b/vtest/unit/specs/components/Personalization/createApplyPropositions.spec.js new file mode 100644 index 000000000..306754f79 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createApplyPropositions.spec.js @@ -0,0 +1,287 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import { + MIXED_PROPOSITIONS, + PAGE_WIDE_SCOPE_DECISIONS, +} from "./responsesMock/eventResponses.js"; +import createApplyPropositions from "../../../../../src/components/Personalization/createApplyPropositions.js"; +import clone from "../../../../../src/utils/clone.js"; +import injectCreateProposition from "../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; +import createMockProposition from "../../../helpers/createMockProposition.js"; +import { DOM_ACTION_COLLECT_INTERACTIONS } from "../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; +import { + JSON_CONTENT_ITEM, + DOM_ACTION, +} from "../../../../../src/constants/schema.js"; + +const METADATA = { + home: { + selector: "#home-item1", + actionType: "setHtml", + }, +}; +describe("Personalization::createApplyPropositions", () => { + let processPropositions; + let createProposition; + let renderedPropositions; + let viewCache; + let applyPropositions; + let render; + beforeEach(() => { + processPropositions = vi.fn(); + processPropositions.mockImplementation((propositions) => { + const returnedPropositions = propositions.map((proposition) => ({ + ...proposition.toJSON(), + renderAttempted: true, + })); + return { + returnedPropositions, + render, + }; + }); + render = vi.fn(); + render.mockImplementation(() => Promise.resolve("notifications")); + createProposition = injectCreateProposition({ + preprocess: (data) => data, + isPageWideSurface: () => false, + }); + renderedPropositions = { + concat: vi.fn(), + }; + viewCache = { + getView: vi.fn(), + }; + viewCache.getView.mockReturnValue(Promise.resolve([])); + applyPropositions = createApplyPropositions({ + processPropositions, + createProposition, + renderedPropositions, + viewCache, + }); + }); + it("it should return an empty propositions promise if propositions is empty array", async () => { + const result = await applyPropositions({ + propositions: [], + }); + expect(result).toEqual({ + propositions: [], + }); + expect(processPropositions).toHaveBeenNthCalledWith(1, []); + }); + it("it should apply user-provided dom-action/default-content schema propositions", async () => { + const expectedExecuteDecisionsPropositions = clone( + PAGE_WIDE_SCOPE_DECISIONS, + ).map((proposition) => { + proposition.items = proposition.items.slice(0, 2); + return proposition; + }); + const result = await applyPropositions({ + propositions: PAGE_WIDE_SCOPE_DECISIONS, + }); + expect(processPropositions).toHaveBeenCalledTimes(1); + const expectedScopes = expectedExecuteDecisionsPropositions.map( + (proposition) => proposition.scope, + ); + result.propositions.forEach((proposition) => { + expect(proposition.renderAttempted).toBe(true); + expect(expectedScopes).toContain(proposition.scope); + expect(proposition.items).toEqual( + expect.arrayContaining([expect.any(Object)]), + ); + expect(proposition.items.length).toEqual(3); + }); + }); + it("it should merge metadata with propositions that have html-content-item schema", async () => { + const { propositions } = await applyPropositions({ + propositions: MIXED_PROPOSITIONS, + metadata: METADATA, + }); + expect(propositions.length).toEqual(4); + propositions.forEach((proposition) => { + expect(proposition.items.length).toEqual(1); + if (proposition.items[0].id === "442358") { + expect(proposition.items[0].data.selector).toEqual("#root"); + expect(proposition.items[0].data.type).toEqual("click"); + } else if (proposition.items[0].id === "442359") { + expect(proposition.scope).toEqual("home"); + expect(proposition.items[0].data.selector).toEqual("#home-item1"); + expect(proposition.items[0].data.type).toEqual("setHtml"); + } + }); + expect(processPropositions).toHaveBeenCalledTimes(1); + }); + it("it should drop items with html-content-item schema when there is no metadata", async () => { + const propositions = [ + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", + scope: "home", + items: [ + { + id: "442359", + schema: "https://ns.adobe.com/personalization/html-content-item", + data: { + content: "

Some custom content for the home page

", + format: "text/html", + id: "1202448", + }, + }, + { + id: "442358", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + format: "application/vnd.adobe.target.dom-action", + selector: "#root", + }, + }, + ], + }, + ]; + const result = await applyPropositions({ + propositions, + }); + expect(result.propositions.length).toEqual(1); + expect(result.propositions[0].items.length).toEqual(1); + expect(result.propositions[0].items[0].id).toEqual("442358"); + expect(result.propositions[0].renderAttempted).toBe(true); + }); + it("it should return renderAttempted = true on resulting propositions", async () => { + const result = await applyPropositions({ + propositions: MIXED_PROPOSITIONS, + }); + expect(result.propositions.length).toEqual(3); + result.propositions.forEach((proposition) => { + expect(proposition.renderAttempted).toBe(true); + }); + }); + it("it should ignore propositions with __view__ scope that have already been rendered", async () => { + const propositions = JSON.parse(JSON.stringify(MIXED_PROPOSITIONS)); + propositions[4].renderAttempted = true; + const result = await applyPropositions({ + propositions, + }); + expect(result.propositions.length).toEqual(2); + result.propositions.forEach((proposition) => { + expect(proposition.renderAttempted).toBe(true); + if (proposition.scope === "__view__") { + expect(proposition.items[0].id).not.toEqual("442358"); + } else { + expect(proposition.scope).toEqual("home"); + } + }); + }); + it("it should ignore items with unsupported schemas", async () => { + const expectedItemIds = ["442358", "442359"]; + const { propositions } = await applyPropositions({ + propositions: MIXED_PROPOSITIONS, + }); + expect(propositions.length).toEqual(3); + propositions.forEach((proposition) => { + expect(proposition.items.length).toEqual(1); + proposition.items.forEach((item) => { + expect(expectedItemIds.indexOf(item.id) > -1); + }); + }); + }); + it("it should not mutate original propositions", async () => { + const originalPropositions = clone(MIXED_PROPOSITIONS); + const result = await applyPropositions({ + propositions: originalPropositions, + metadata: METADATA, + }); + let numReturnedPropositions = 0; + expect(originalPropositions).toEqual(MIXED_PROPOSITIONS); + result.propositions.forEach((proposition) => { + const [original] = originalPropositions.filter( + (originalProposition) => originalProposition.id === proposition.id, + ); + if (original) { + numReturnedPropositions += 1; + expect(proposition).not.toBe(original); + } + }); + expect(numReturnedPropositions).toEqual(4); + }); + it("concats viewName propositions", async () => { + viewCache.getView.mockReturnValue( + Promise.resolve([ + createProposition({ + id: "myViewNameProp1", + items: [{}], + }), + ]), + ); + const result = await applyPropositions({ + viewName: "myViewName", + }); + expect(result).toEqual({ + propositions: [ + { + id: "myViewNameProp1", + items: [{}], + renderAttempted: true, + }, + ], + }); + }); + it("handles track actions for json-content-item", async () => { + const testElementId = "superfluous123"; + const proposition = createMockProposition({ + id: "abc", + schema: JSON_CONTENT_ITEM, + data: { + isGood: true, + }, + }); + const expectedProposition = { + id: "id", + scope: "scope", + scopeDetails: { + decisionProvider: "AJO", + }, + items: [ + { + id: "abc", + schema: DOM_ACTION, + data: { + isGood: true, + selector: "#superfluous123", + type: DOM_ACTION_COLLECT_INTERACTIONS, + }, + }, + ], + }; + const result = await applyPropositions({ + propositions: [proposition.toJSON()], + metadata: { + scope: { + selector: `#${testElementId}`, + actionType: DOM_ACTION_COLLECT_INTERACTIONS, + }, + }, + }); + expect(result).toEqual({ + propositions: [ + { + ...expectedProposition, + renderAttempted: true, + }, + ], + }); + expect(processPropositions).toHaveBeenCalledTimes(1); + expect(processPropositions.mock.calls[0][0][0].toJSON()).toEqual( + expectedProposition, + ); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createClickStorage.spec.js b/vtest/unit/specs/components/Personalization/createClickStorage.spec.js new file mode 100644 index 000000000..7dcddcef4 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createClickStorage.spec.js @@ -0,0 +1,117 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, describe, it, expect } from "vitest"; +import createClickStorage from "../../../../../src/components/Personalization/createClickStorage.js"; + +describe("Personalization::createClickStorage", () => { + let clickStorage; + const FIRST_CLICK = { + selector: "div:123:h2", + meta: { + id: "AT:123", + scope: "__view__", + scopeDetails: { + test: "blah1", + }, + trackingLabel: "mylabel", + scopeType: "myscopetype", + }, + }; + const SECOND_CLICK = { + selector: "div:123:h2", + meta: { + id: "AT:123", + scope: "consent", + scopeDetails: { + test: "blah3", + }, + }, + }; + const THIRD_CLICK = { + selector: "div:123:h2", + meta: { + id: "AT:234", + scope: "consent", + scopeDetails: { + test: "blah4", + }, + }, + }; + const FORTH_CLICK = { + selector: "div:123:h1", + meta: { + id: "AT:123", + scope: "consent", + scopeDetails: { + test: "blah5", + }, + }, + }; + + /* this is how the clickStorage map should look like + const expectedClicksInStorage = { + "div:123:h1": { + "AT:123": { + scope: "consent", + scopeDetails: { + blah: "blah" + } + } + }, + "div:123:h2": { + "AT:123": { + scope: "consent", + scopeDetails: { + blah: "blah" + }, + }, + "AT:234": { + scope: "consent", + scopeDetails: { + blah: "blah" + } + } + } + }; */ + beforeEach(() => { + clickStorage = createClickStorage(); + }); + it("returns empty array if empty storage", () => { + expect(clickStorage.getClickSelectors()).toEqual([]); + }); + it("returns empty object when no metadata for this selector", () => { + expect(clickStorage.getClickMetas("123")).toEqual({}); + }); + it("stores clicks as a map in the click storage and returns the selectors and metadata", () => { + clickStorage.storeClickMeta(FIRST_CLICK); + clickStorage.storeClickMeta(SECOND_CLICK); + clickStorage.storeClickMeta(THIRD_CLICK); + clickStorage.storeClickMeta(FORTH_CLICK); + expect(clickStorage.getClickSelectors().length).toEqual(2); + expect(clickStorage.getClickMetas("div:123:h2").length).toEqual(2); + }); + it("getClickMetas returns the id, scopeDetails, scope, trackingLabel, and scopeType", () => { + clickStorage.storeClickMeta(FIRST_CLICK); + const meta = clickStorage.getClickMetas("div:123:h2"); + expect(clickStorage.getClickSelectors().length).toEqual(1); + expect(meta.length).toEqual(1); + expect(meta[0]).toEqual({ + id: "AT:123", + scope: "__view__", + scopeDetails: { + test: "blah1", + }, + trackingLabel: "mylabel", + scopeType: "myscopetype", + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createComponent.spec.js b/vtest/unit/specs/components/Personalization/createComponent.spec.js new file mode 100644 index 000000000..e1e4c25b8 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createComponent.spec.js @@ -0,0 +1,185 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createComponent from "../../../../../src/components/Personalization/createComponent.js"; + +describe("Personalization", () => { + let logger; + let fetchDataHandler; + let onClickHandler; + let viewChangeHandler; + let showContainers; + let isAuthoringModeEnabled; + let viewCache; + let mergeQuery; + let event; + let personalizationComponent; + let setTargetMigration; + let mergeDecisionsMeta; + let renderedPropositions; + let cacheUpdate; + const build = () => { + personalizationComponent = createComponent({ + logger, + fetchDataHandler, + viewChangeHandler, + onClickHandler, + isAuthoringModeEnabled, + mergeQuery, + viewCache, + showContainers, + setTargetMigration, + mergeDecisionsMeta, + renderedPropositions, + }); + }; + beforeEach(() => { + event = { + mergeQuery: vi.fn(), + getViewName: vi.fn(), + }; + event.getViewName.mockReturnValue({}); + logger = { + info: vi.fn(), + warn: vi.fn(), + }; + isAuthoringModeEnabled = vi.fn().mockReturnValue(false); + fetchDataHandler = vi.fn(); + viewChangeHandler = vi.fn(); + onClickHandler = vi.fn(); + showContainers = vi.fn(); + mergeQuery = vi.fn(); + viewCache = { + isInitialized: vi.fn(), + createCacheUpdate: vi.fn(), + }; + cacheUpdate = { + update: vi.fn(), + cancel: vi.fn(), + }; + viewCache.createCacheUpdate.mockReturnValue(cacheUpdate); + setTargetMigration = vi.fn(); + mergeDecisionsMeta = vi.fn(); + renderedPropositions = { + clear: vi.fn(), + }; + build(); + }); + describe("onBeforeEvent", () => { + it("shouldn't do anything since authoringMode is enabled", () => { + isAuthoringModeEnabled.mockReturnValue(true); + const renderDecisions = true; + const personalization = { + decisionScopes: ["foo"], + }; + personalizationComponent.lifecycle.onBeforeEvent({ + event, + renderDecisions, + personalization, + }); + expect(logger.warn).toHaveBeenCalledWith( + "Rendering is disabled for authoring mode.", + ); + expect(isAuthoringModeEnabled).toHaveBeenCalled(); + expect(mergeQuery).toHaveBeenCalledWith(event, { + enabled: false, + }); + expect(fetchDataHandler).not.toHaveBeenCalled(); + expect(viewChangeHandler).not.toHaveBeenCalled(); + expect(onClickHandler).not.toHaveBeenCalled(); + expect(showContainers).not.toHaveBeenCalled(); + expect(viewCache.createCacheUpdate).not.toHaveBeenCalled(); + }); + it("should trigger pageLoad if there are decisionScopes", () => { + const renderDecisions = false; + const personalization = { + decisionScopes: ["alloy1"], + }; + personalizationComponent.lifecycle.onBeforeEvent({ + event, + renderDecisions, + personalization, + }); + expect(isAuthoringModeEnabled).toHaveBeenCalled(); + expect(fetchDataHandler).toHaveBeenCalled(); + expect(viewChangeHandler).not.toHaveBeenCalled(); + expect(mergeQuery).not.toHaveBeenCalled(); + expect(onClickHandler).not.toHaveBeenCalled(); + expect(viewCache.createCacheUpdate).toHaveBeenCalled(); + }); + it("should trigger pageLoad if cache is not initialized", () => { + const renderDecisions = false; + const personalization = { + decisionScopes: [], + }; + viewCache.isInitialized.mockReturnValue(false); + personalizationComponent.lifecycle.onBeforeEvent({ + event, + renderDecisions, + personalization, + }); + expect(isAuthoringModeEnabled).toHaveBeenCalled(); + expect(fetchDataHandler).toHaveBeenCalled(); + expect(viewChangeHandler).not.toHaveBeenCalled(); + expect(mergeQuery).not.toHaveBeenCalled(); + expect(onClickHandler).not.toHaveBeenCalled(); + expect(viewCache.createCacheUpdate).toHaveBeenCalled(); + }); + it("should trigger viewHandler if cache is initialized and viewName is provided", () => { + const renderDecisions = false; + const personalization = { + decisionScopes: [], + }; + viewCache.isInitialized.mockReturnValue(true); + event.getViewName.mockReturnValue("cart"); + personalizationComponent.lifecycle.onBeforeEvent({ + event, + renderDecisions, + personalization, + }); + expect(isAuthoringModeEnabled).toHaveBeenCalled(); + expect(fetchDataHandler).not.toHaveBeenCalled(); + expect(viewChangeHandler).toHaveBeenCalled(); + expect(mergeQuery).not.toHaveBeenCalled(); + expect(onClickHandler).not.toHaveBeenCalled(); + expect(viewCache.createCacheUpdate).not.toHaveBeenCalled(); + }); + it("should trigger onClickHandler at onClick", () => { + personalizationComponent.lifecycle.onClick({ + event, + }); + expect(onClickHandler).toHaveBeenCalled(); + }); + it("should call showContainers() when a request fails", () => { + viewCache.isInitialized.mockReturnValue(true); + const onRequestFailure = vi.fn().mockImplementation((func) => func()); + personalizationComponent.lifecycle.onBeforeEvent({ + event, + onRequestFailure, + }); + expect(onRequestFailure).toHaveBeenCalled(); + expect(showContainers).toHaveBeenCalled(); + }); + }); + describe("onBeforeRequest", () => { + it("should always call setTargetMigration during onBeforeRequest", () => { + const request = { + getPayload: vi.fn(), + }; + personalizationComponent.lifecycle.onBeforeRequest({ + request, + }); + expect(setTargetMigration).toHaveBeenNthCalledWith(1, request); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createFetchDataHandler.spec.js b/vtest/unit/specs/components/Personalization/createFetchDataHandler.spec.js new file mode 100644 index 000000000..bc3063040 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createFetchDataHandler.spec.js @@ -0,0 +1,249 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createFetchDataHandler from "../../../../../src/components/Personalization/createFetchDataHandler.js"; +import injectCreateProposition from "../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; +import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; +import defer from "../../../../../src/utils/defer.js"; +import createNotificationHandler from "../../../../../src/components/Personalization/createNotificationHandler.js"; + +describe("Personalization::createFetchDataHandler", () => { + let prehidingStyle; + let showContainers; + let hideContainers; + let mergeQuery; + let collect; + let processPropositions; + let createProposition; + let renderedPropositions; + let notificationHandler; + let consent; + let logger; + let cacheUpdate; + let personalizationDetails; + let event; + let onResponse; + let response; + beforeEach(() => { + logger = { + logOnContentRendering: vi.fn(), + }; + prehidingStyle = "myprehidingstyle"; + showContainers = vi.fn(); + hideContainers = vi.fn(); + mergeQuery = vi.fn(); + collect = vi.fn(); + processPropositions = vi.fn(); + createProposition = injectCreateProposition({ + preprocess: (data) => data, + isPageWideSurface: () => false, + }); + renderedPropositions = { + concat: vi.fn(), + }; + notificationHandler = createNotificationHandler( + collect, + renderedPropositions, + ); + consent = { + current: vi.fn(), + }; + consent.current.mockReturnValue({ + state: "in", + wasSet: false, + }); + cacheUpdate = { + update: vi.fn(), + }; + personalizationDetails = { + isRenderDecisions: vi.fn(), + createQueryDetails: vi.fn(), + getViewName: vi.fn(), + isSendDisplayEvent: vi.fn(), + }; + personalizationDetails.createQueryDetails.mockReturnValue("myquerydetails"); + personalizationDetails.isSendDisplayEvent.mockReturnValue(true); + event = "myevent"; + onResponse = vi.fn(); + response = { + getPayloadsByType: vi.fn(), + }; + }); + const run = () => { + const fetchDataHandler = createFetchDataHandler({ + prehidingStyle, + showContainers, + hideContainers, + mergeQuery, + processPropositions, + createProposition, + notificationHandler, + consent, + logger, + }); + fetchDataHandler({ + cacheUpdate, + personalizationDetails, + event, + onResponse, + }); + }; + const returnResponse = () => { + expect(onResponse).toHaveBeenCalledTimes(1); + const callback = onResponse.mock.calls[0][0]; + return callback({ + response, + }); + }; + it("should hide containers if renderDecisions is true", () => { + personalizationDetails.isRenderDecisions.mockReturnValue(true); + run(); + expect(hideContainers).toHaveBeenCalled(); + }); + it("shouldn't hide containers if renderDecisions is false", () => { + personalizationDetails.isRenderDecisions.mockReturnValue(false); + run(); + expect(hideContainers).not.toHaveBeenCalled(); + }); + it("shouldn't hide containers if we have out consent cookie", () => { + consent.current.mockReturnValue({ + state: "out", + wasSet: true, + }); + personalizationDetails.isRenderDecisions.mockReturnValue(true); + run(); + expect(hideContainers).not.toHaveBeenCalled(); + }); + it("should trigger responseHandler at onResponse", () => { + personalizationDetails.isRenderDecisions.mockReturnValue(false); + run(); + response.getPayloadsByType.mockReturnValue([]); + cacheUpdate.update.mockReturnValue([]); + processPropositions.mockReturnValue({ + returnedPropositions: [], + returnedDecisions: [], + }); + const result = returnResponse(); + expect(result).toEqual({ + propositions: [], + decisions: [], + }); + }); + it("should render decisions", async () => { + personalizationDetails.isRenderDecisions.mockReturnValue(true); + personalizationDetails.getViewName.mockReturnValue("myviewname"); + processPropositions = () => { + return { + render: () => + Promise.resolve([ + { + id: "handle1", + }, + ]), + returnedPropositions: [ + { + id: "handle1", + items: ["item1"], + renderAttempted: true, + }, + ], + returnedDecisions: [], + }; + }; + run(); + response.getPayloadsByType.mockReturnValue([ + { + id: "handle1", + scopeDetails: { + characteristics: { + scopeType: "view", + }, + }, + }, + { + id: "handle2", + }, + ]); + cacheUpdate.update.mockReturnValue([ + createProposition({ + id: "handle1", + }), + ]); + const result = returnResponse(); + expect(result).toEqual({ + propositions: [ + { + id: "handle1", + items: ["item1"], + renderAttempted: true, + }, + ], + decisions: [], + }); + await flushPromiseChains(); + expect(showContainers).toHaveBeenCalled(); + expect(collect).toHaveBeenNthCalledWith(1, { + decisionsMeta: [ + { + id: "handle1", + }, + ], + viewName: "myviewname", + }); + }); + it("should show containers immediately", async () => { + personalizationDetails.isRenderDecisions.mockReturnValue(true); + const renderDeferred = defer(); + processPropositions = () => { + return { + render: () => renderDeferred.promise, + returnedPropositions: [ + { + id: "handle2", + scope: "__view__", + items: ["item1"], + renderAttempted: true, + }, + ], + returnedDecisions: [], + }; + }; + run(); + response.getPayloadsByType.mockReturnValue([ + { + id: "handle2", + scope: "__view__", + items: ["item1"], + }, + ]); + cacheUpdate.update.mockReturnValue([]); + expect(showContainers).not.toHaveBeenCalled(); + returnResponse(); + expect(showContainers).toHaveBeenCalled(); + expect(collect).not.toHaveBeenCalled(); + renderDeferred.resolve([ + { + id: "handle2", + }, + ]); + await flushPromiseChains(); + expect(collect).toHaveBeenNthCalledWith(1, { + decisionsMeta: [ + { + id: "handle2", + }, + ], + viewName: undefined, + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createGetPageLocation.spec.js b/vtest/unit/specs/components/Personalization/createGetPageLocation.spec.js new file mode 100644 index 000000000..648e58279 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createGetPageLocation.spec.js @@ -0,0 +1,31 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import createGetPageLocation from "../../../../../src/components/Personalization/createGetPageLocation.js"; + +describe("Personalization::createGetPageLocation", () => { + it("it should return page location object", () => { + const win = { + location: { + href: "https://alloy.test.com/test/page/1/", + host: "alloy.test.com", + pathname: "/test/page/1/", + }, + }; + const getPageLocation = createGetPageLocation({ + window: win, + }); + const location = getPageLocation(); + expect(location).toEqual(win.location); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createHandleConsentFlicker.spec.js b/vtest/unit/specs/components/Personalization/createHandleConsentFlicker.spec.js new file mode 100644 index 000000000..6be72607f --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createHandleConsentFlicker.spec.js @@ -0,0 +1,66 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createHandleConsentFlicker from "../../../../../src/components/Personalization/createHandleConsentFlicker.js"; +import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; + +describe("Personalization::createHandleConsentFlicker", () => { + let showContainers; + let consent; + let handleConsentFlicker; + beforeEach(() => { + showContainers = vi.fn(); + consent = { + current: vi.fn(), + awaitConsent: vi.fn(), + }; + handleConsentFlicker = createHandleConsentFlicker({ + showContainers, + consent, + }); + }); + it("shows containers when consent is out and was set", () => { + consent.current.mockReturnValue({ + state: "out", + wasSet: true, + }); + handleConsentFlicker(); + expect(showContainers).toHaveBeenCalled(); + return flushPromiseChains().then(() => { + expect(consent.awaitConsent).not.toHaveBeenCalled(); + }); + }); + it("shows containers after consent is rejected", () => { + consent.current.mockReturnValue({ + state: "out", + wasSet: false, + }); + consent.awaitConsent.mockReturnValue(Promise.reject()); + handleConsentFlicker(); + expect(consent.awaitConsent).toHaveBeenCalled(); + return flushPromiseChains().then(() => { + expect(showContainers).toHaveBeenCalled(); + }); + }); + it("does not show containers after consent is given", () => { + consent.current.mockReturnValue({ + state: "out", + wasSet: false, + }); + consent.awaitConsent.mockReturnValue(Promise.resolve()); + handleConsentFlicker(); + expect(consent.awaitConsent).toHaveBeenCalled(); + return flushPromiseChains().then(() => { + expect(showContainers).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createInteractionStorage.spec.js b/vtest/unit/specs/components/Personalization/createInteractionStorage.spec.js new file mode 100644 index 000000000..3105eced6 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createInteractionStorage.spec.js @@ -0,0 +1,170 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, describe, it, expect } from "vitest"; +import createInteractionStorage from "../../../../../src/components/Personalization/createInteractionStorage.js"; + +describe("Personalization::createInteractionStorage", () => { + let storeInteractionMeta; + let getInteractionMetas; + let PROPOSITIONS = []; + let interactIDs = {}; + beforeEach(() => { + ({ storeInteractionMeta, getInteractionMetas } = + createInteractionStorage()); + interactIDs = { + "div:123:h2": [1], + "div:123:h1": [2], + }; + PROPOSITIONS = [ + { + id: "AT:123", + scope: "__view__", + scopeDetails: { + test: "blah1", + characteristics: { + scopeType: "page", + }, + }, + items: [ + { + id: "0632668e-53a4-4f31-b092-45696e45829d", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + selector: "div:123:h2", + }, + characteristics: { + trackingLabel: "mylabel", + }, + }, + ], + }, + { + id: "AT:123", + scope: "consent", + scopeDetails: { + test: "blah3", + characteristics: { + scopeType: "view", + }, + }, + items: [ + { + id: "0632668e-53a4-4f31-b092-45696e45829d", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + selector: "div:123:h2", + }, + }, + ], + }, + { + id: "AT:234", + scope: "consent", + scopeDetails: { + test: "blah4", + characteristics: { + scopeType: "view", + }, + }, + items: [ + { + id: "0632668e-53a4-4f31-b092-45696e45829d", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + selector: "div:123:h2", + }, + }, + ], + }, + { + id: "AT:123", + scope: "consent", + scopeDetails: { + test: "blah5", + characteristics: { + scopeType: "view", + }, + }, + items: [ + { + id: "0632668e-53a4-4f31-b092-45696e45829d", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + selector: "div:123:h1", + }, + }, + ], + }, + ]; + }); + it("returns empty array when no metadata for this selector", () => { + expect(getInteractionMetas([1])).toEqual([]); + }); + it("stores clicks as a map in the click storage and returns the metadata", () => { + PROPOSITIONS.forEach((proposition) => { + const { id, scope, scopeDetails } = proposition; + proposition.items.forEach((item) => + storeInteractionMeta( + proposition.id, + item.id, + proposition.scopeDetails.characteristics.scopeType, + { + id, + scope, + scopeDetails, + }, + interactIDs[item.data.selector], + ), + ); + }); + expect(getInteractionMetas(interactIDs["div:123:h2"]).length).toEqual(2); + expect(getInteractionMetas(interactIDs["div:123:h1"]).length).toEqual(1); + }); + it("getInteractionMetas returns the id, scopeDetails, scope, trackingLabel, and scopeType", () => { + const proposition = PROPOSITIONS[0]; + proposition.items.forEach((item) => + storeInteractionMeta( + proposition.id, + item.id, + proposition.scopeDetails.characteristics.scopeType, + { + id: proposition.id, + scope: proposition.scope, + scopeDetails: proposition.scopeDetails, + }, + interactIDs[item.data.selector], + ), + ); + const meta = getInteractionMetas(interactIDs["div:123:h2"]); + expect(meta.length).toEqual(1); + expect(meta[0]).toEqual({ + id: "AT:123", + scope: "__view__", + scopeDetails: { + test: "blah1", + characteristics: { + scopeType: "page", + }, + }, + scopeType: "page", + items: [ + { + id: "0632668e-53a4-4f31-b092-45696e45829d", + }, + ], + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createNotificationHandler.spec.js b/vtest/unit/specs/components/Personalization/createNotificationHandler.spec.js new file mode 100644 index 000000000..106eb6e05 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createNotificationHandler.spec.js @@ -0,0 +1,58 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createNotificationHandler from "../../../../../src/components/Personalization/createNotificationHandler.js"; + +describe("Personalization::createNotificationHandler", () => { + let collect; + let renderedPropositions; + let notificationHandler; + const NOTIFICATIONS = [ + { + id: "abc", + scope: "web://localhost:3000/inAppMessages", + scopeDetails: { + activity: { + id: "abc#123", + }, + }, + }, + ]; + beforeEach(() => { + collect = vi.fn().mockReturnValue(Promise.resolve()); + renderedPropositions = { + concat: vi.fn(), + }; + notificationHandler = createNotificationHandler( + collect, + renderedPropositions, + ); + }); + it("emits a notification immediately", () => { + const handleNotifications = notificationHandler(true, true, "foo"); + handleNotifications(NOTIFICATIONS); + expect(collect).toHaveBeenNthCalledWith(1, { + decisionsMeta: NOTIFICATIONS, + viewName: "foo", + }); + }); + it("defers the notification", () => { + const handleNotifications = notificationHandler(true, false, undefined); + handleNotifications(NOTIFICATIONS); + expect(collect).not.toHaveBeenCalled(); + expect(renderedPropositions.concat).toHaveBeenCalledTimes(1); + }); + it("doesn't do anything if renderDecisions is false", () => { + notificationHandler(false, true, undefined); + expect(renderedPropositions.concat).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createOnClickHandler.spec.js b/vtest/unit/specs/components/Personalization/createOnClickHandler.spec.js new file mode 100644 index 000000000..a980ea79a --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createOnClickHandler.spec.js @@ -0,0 +1,519 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createOnClickHandler from "../../../../../src/components/Personalization/createOnClickHandler.js"; +import { mergeDecisionsMeta } from "../../../../../src/utils/event.js"; +import createEvent from "../../../../../src/core/createEvent.js"; +import { createNode } from "../../../../../src/utils/dom/index.js"; +import { + ALWAYS, + NEVER, +} from "../../../../../src/constants/propositionInteractionType.js"; +import { + ADOBE_JOURNEY_OPTIMIZER, + ADOBE_TARGET, +} from "../../../../../src/constants/decisionProvider.js"; + +describe("Personalization::createOnClickHandler", () => { + let collectInteractions; + let collectClicks; + let getInteractionMetas; + let getClickMetas; + let getClickSelectors; + let event; + const decisionsMeta = [ + { + id: 1, + scope: "foo", + }, + ]; + const decisionsMeta2 = [ + { + id: 2, + scope: "bar", + }, + ]; + let autoCollectPropositionInteractions; + beforeEach(() => { + collectInteractions = vi.fn(); + collectClicks = vi.fn(); + getInteractionMetas = vi.fn(); + getClickMetas = vi.fn(); + getClickSelectors = vi.fn(); + event = createEvent(); + vi.spyOn(event, "mergeXdm"); + autoCollectPropositionInteractions = { + [ADOBE_JOURNEY_OPTIMIZER]: ALWAYS, + [ADOBE_TARGET]: NEVER, + }; + }); + it("collects clicks", () => { + const selectors = ["foo", "foo2"]; + collectInteractions.mockReturnValue({ + decisionsMeta: [], + }); + collectClicks.mockReturnValue({ + decisionsMeta, + eventLabel: "", + }); + getClickSelectors.mockReturnValue(selectors); + const handleOnClick = createOnClickHandler({ + mergeDecisionsMeta, + collectInteractions, + collectClicks, + getInteractionMetas, + getClickMetas, + getClickSelectors, + autoCollectPropositionInteractions, + }); + const clickedElement = "foo"; + handleOnClick({ + event, + clickedElement, + }); + const expectedXdm = { + eventType: "decisioning.propositionInteract", + _experience: { + decisioning: { + propositions: [ + { + id: 1, + scope: "foo", + }, + ], + propositionEventType: { + interact: 1, + }, + }, + }, + }; + expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); + expect(collectClicks).toHaveBeenCalledWith( + clickedElement, + selectors, + getClickMetas, + ); + event.finalize(); + expect(event.toJSON()).toEqual({ + xdm: expectedXdm, + }); + }); + it("collects interactions", () => { + collectClicks.mockReturnValue({ + decisionsMeta: [], + }); + collectInteractions.mockReturnValue({ + decisionsMeta, + propositionActionLabel: "", + }); + const handleOnClick = createOnClickHandler({ + mergeDecisionsMeta, + collectInteractions, + collectClicks, + getInteractionMetas, + getClickMetas, + getClickSelectors, + autoCollectPropositionInteractions, + }); + const clickedElement = createNode("div", { + class: "clicked-element", + }); + handleOnClick({ + event, + clickedElement, + }); + const expectedXdm = { + eventType: "decisioning.propositionInteract", + _experience: { + decisioning: { + propositions: [ + { + id: 1, + scope: "foo", + }, + ], + propositionEventType: { + interact: 1, + }, + }, + }, + }; + expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); + expect(collectInteractions).toHaveBeenCalledWith( + clickedElement, + getInteractionMetas, + autoCollectPropositionInteractions, + ); + event.finalize(); + expect(event.toJSON()).toEqual({ + xdm: expectedXdm, + }); + }); + it("collects clicks with label", () => { + const selectors = ["foo", "foo2"]; + collectInteractions.mockReturnValue({ + decisionsMeta: [], + }); + collectClicks.mockReturnValue({ + decisionsMeta, + propositionActionLabel: "click-label", + }); + getClickSelectors.mockReturnValue(selectors); + const handleOnClick = createOnClickHandler({ + mergeDecisionsMeta, + collectInteractions, + collectClicks, + getInteractionMetas, + getClickMetas, + getClickSelectors, + autoCollectPropositionInteractions, + }); + const clickedElement = "foo"; + handleOnClick({ + event, + clickedElement, + }); + const expectedXdm = { + eventType: "decisioning.propositionInteract", + _experience: { + decisioning: { + propositions: [ + { + id: 1, + scope: "foo", + }, + ], + propositionEventType: { + interact: 1, + }, + propositionAction: { + label: "click-label", + }, + }, + }, + }; + expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); + expect(collectClicks).toHaveBeenCalledWith( + clickedElement, + selectors, + getClickMetas, + ); + event.finalize(); + expect(event.toJSON()).toEqual({ + xdm: expectedXdm, + }); + }); + it("collects interactions with label", () => { + collectClicks.mockReturnValue({ + decisionsMeta: [], + }); + collectInteractions.mockReturnValue({ + decisionsMeta, + propositionActionLabel: "click-label", + }); + const handleOnClick = createOnClickHandler({ + mergeDecisionsMeta, + collectInteractions, + collectClicks, + getInteractionMetas, + getClickMetas, + getClickSelectors, + autoCollectPropositionInteractions, + }); + const clickedElement = createNode("div", { + class: "clicked-element", + }); + handleOnClick({ + event, + clickedElement, + }); + const expectedXdm = { + eventType: "decisioning.propositionInteract", + _experience: { + decisioning: { + propositions: [ + { + id: 1, + scope: "foo", + }, + ], + propositionEventType: { + interact: 1, + }, + propositionAction: { + label: "click-label", + }, + }, + }, + }; + expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); + expect(collectInteractions).toHaveBeenCalledWith( + clickedElement, + getInteractionMetas, + autoCollectPropositionInteractions, + ); + event.finalize(); + expect(event.toJSON()).toEqual({ + xdm: expectedXdm, + }); + }); + + // TODO collect clicks with token ?? + + it("collects interactions with token", () => { + collectClicks.mockReturnValue({ + decisionsMeta: [], + }); + collectInteractions.mockReturnValue({ + decisionsMeta, + propositionActionToken: "click-token", + }); + const handleOnClick = createOnClickHandler({ + mergeDecisionsMeta, + collectInteractions, + collectClicks, + getInteractionMetas, + getClickMetas, + getClickSelectors, + autoCollectPropositionInteractions, + }); + const clickedElement = createNode("div", { + class: "clicked-element", + }); + handleOnClick({ + event, + clickedElement, + }); + const expectedXdm = { + eventType: "decisioning.propositionInteract", + _experience: { + decisioning: { + propositions: [ + { + id: 1, + scope: "foo", + }, + ], + propositionEventType: { + interact: 1, + }, + propositionAction: { + tokens: ["click-token"], + }, + }, + }, + }; + expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); + expect(collectInteractions).toHaveBeenCalledWith( + clickedElement, + getInteractionMetas, + autoCollectPropositionInteractions, + ); + event.finalize(); + expect(event.toJSON()).toEqual({ + xdm: expectedXdm, + }); + }); + it("shouldn't be called when clickStorage and interactionStorage are empty", () => { + collectInteractions.mockReturnValue({ + decisionsMeta: [], + }); + collectClicks.mockReturnValue({ + decisionsMeta: [], + }); + const handleOnClick = createOnClickHandler({ + mergeDecisionsMeta, + collectInteractions, + collectClicks, + getInteractionMetas, + getClickMetas, + getClickSelectors, + autoCollectPropositionInteractions, + }); + const clickedElement = createNode("div", { + class: "clicked-element", + }); + handleOnClick({ + event, + clickedElement, + }); + expect(event.mergeXdm).not.toHaveBeenCalled(); + }); + it("for interactions, adds a viewName to the response", () => { + collectClicks.mockReturnValue({ + decisionsMeta: [], + }); + collectInteractions.mockReturnValue({ + decisionsMeta, + viewName: "myview", + }); + const handleOnClick = createOnClickHandler({ + mergeDecisionsMeta, + collectInteractions, + collectClicks, + getInteractionMetas, + getClickMetas, + getClickSelectors, + autoCollectPropositionInteractions, + }); + const clickedElement = createNode("div", { + class: "clicked-element", + }); + handleOnClick({ + event, + clickedElement, + }); + const expectedXdm = { + eventType: "decisioning.propositionInteract", + web: { + webPageDetails: { + viewName: "myview", + }, + }, + _experience: { + decisioning: { + propositions: [ + { + id: 1, + scope: "foo", + }, + ], + propositionEventType: { + interact: 1, + }, + }, + }, + }; + expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); + expect(collectInteractions).toHaveBeenCalledWith( + clickedElement, + getInteractionMetas, + autoCollectPropositionInteractions, + ); + event.finalize(); + expect(event.toJSON()).toEqual({ + xdm: expectedXdm, + }); + }); + it("for clicks, adds a viewName to the response", () => { + const selectors = ["foo", "foo2"]; + collectInteractions.mockReturnValue({ + decisionsMeta: [], + }); + collectClicks.mockReturnValue({ + decisionsMeta, + viewName: "myview", + }); + getClickSelectors.mockReturnValue(selectors); + const handleOnClick = createOnClickHandler({ + mergeDecisionsMeta, + collectInteractions, + collectClicks, + getInteractionMetas, + getClickMetas, + getClickSelectors, + autoCollectPropositionInteractions, + }); + const clickedElement = "foo"; + handleOnClick({ + event, + clickedElement, + }); + const expectedXdm = { + eventType: "decisioning.propositionInteract", + web: { + webPageDetails: { + viewName: "myview", + }, + }, + _experience: { + decisioning: { + propositions: [ + { + id: 1, + scope: "foo", + }, + ], + propositionEventType: { + interact: 1, + }, + }, + }, + }; + expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); + expect(collectClicks).toHaveBeenCalledWith( + clickedElement, + selectors, + getClickMetas, + ); + event.finalize(); + expect(event.toJSON()).toEqual({ + xdm: expectedXdm, + }); + }); + it("gets metas for both click and interact", () => { + collectInteractions.mockReturnValue({ + decisionsMeta, + }); + collectClicks.mockReturnValue({ + decisionsMeta: decisionsMeta2, + }); + const handleOnClick = createOnClickHandler({ + mergeDecisionsMeta, + collectInteractions, + collectClicks, + getInteractionMetas, + getClickMetas, + getClickSelectors, + autoCollectPropositionInteractions, + }); + const clickedElement = createNode("div", { + class: "clicked-element", + }); + handleOnClick({ + event, + clickedElement, + }); + expect(collectInteractions).toHaveBeenCalled(); + expect(collectClicks).toHaveBeenCalled(); + const expectedXdm = { + eventType: "decisioning.propositionInteract", + _experience: { + decisioning: { + propositions: [ + { + id: 1, + scope: "foo", + }, + { + id: 2, + scope: "bar", + }, + ], + propositionEventType: { + interact: 1, + }, + }, + }, + }; + expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); + expect(collectInteractions).toHaveBeenCalledWith( + clickedElement, + getInteractionMetas, + autoCollectPropositionInteractions, + ); + event.finalize(); + expect(event.toJSON()).toEqual({ + xdm: expectedXdm, + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createOnDecisionHandler.spec.js b/vtest/unit/specs/components/Personalization/createOnDecisionHandler.spec.js new file mode 100644 index 000000000..808a8ca0d --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createOnDecisionHandler.spec.js @@ -0,0 +1,261 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createOnDecisionHandler from "../../../../../src/components/Personalization/createOnDecisionHandler.js"; +import { MESSAGE_CONTENT_CARD } from "../../../../../src/constants/schema.js"; +import injectCreateProposition from "../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; +import createNotificationHandler from "../../../../../src/components/Personalization/createNotificationHandler.js"; + +describe("Personalization::createOnDecisionHandler", () => { + const PROPOSITIONS = [ + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + qualifiedDate: 1683042673387, + displayedDate: 1683042673395, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + qualifiedDate: 1683042673387, + displayedDate: 1683042673395, + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + scope: "web://mywebsite.com/my-cards", + }, + { + id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", + items: [ + { + schema: MESSAGE_CONTENT_CARD, + data: { + expiryDate: 1712190456, + publishedDate: 1677752640000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/lumon.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "a handshake is available upon request.", + title: "Welcome to Lumon!", + }, + contentType: "application/json", + qualifiedDate: 1683042628064, + displayedDate: 1683042628070, + }, + id: "a48ca420-faea-467e-989a-5d179d9f562d", + }, + { + schema: MESSAGE_CONTENT_CARD, + data: { + expiryDate: 1712190456, + publishedDate: 1677839040000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/achievement.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Great job, you completed your profile.", + title: "Achievement Unlocked!", + }, + contentType: "application/json", + qualifiedDate: 1683042628064, + displayedDate: 1683042628070, + }, + id: "b7173290-588f-40c6-a05c-43ed5ec08b28", + }, + ], + scope: "web://mywebsite.com/my-cards", + }, + { + id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", + items: [ + { + schema: MESSAGE_CONTENT_CARD, + data: { + expiryDate: 1712190456, + publishedDate: 1678098240000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/twitter.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Posting on social media helps us spread the word.", + title: "Thanks for sharing!", + }, + contentType: "application/json", + qualifiedDate: 1683042658312, + displayedDate: 1683042658316, + }, + id: "cfcb1af7-7bc2-45b2-a86a-0aa93fe69ce7", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + eventToken: + "eyJtZXNzYWdlRXhlY3V0aW9uIjp7Im1lc3NhZ2VFeGVjdXRpb25JRCI6Ik5BIiwibWVzc2FnZUlEIjoiMDJjNzdlYTgtN2MwZS00ZDMzLTgwOTAtNGE1YmZkM2Q3NTAzIiwibWVzc2FnZVR5cGUiOiJtYXJrZXRpbmciLCJjYW1wYWlnbklEIjoiMzlhZThkNGItYjU1ZS00M2RjLWExNDMtNzdmNTAxOTViNDg3IiwiY2FtcGFpZ25WZXJzaW9uSUQiOiJiZDg1ZDllOC0yMDM3LTQyMmYtYjZkMi0zOTU3YzkwNTU5ZDMiLCJjYW1wYWlnbkFjdGlvbklEIjoiYjQ3ZmRlOGItNTdjMS00YmJlLWFlMjItNjRkNWI3ODJkMTgzIiwibWVzc2FnZVB1YmxpY2F0aW9uSUQiOiJhZTUyY2VkOC0yMDBjLTQ5N2UtODc4Ny1lZjljZmMxNzgyMTUifSwibWVzc2FnZVByb2ZpbGUiOnsiY2hhbm5lbCI6eyJfaWQiOiJodHRwczovL25zLmFkb2JlLmNvbS94ZG0vY2hhbm5lbHMvd2ViIiwiX3R5cGUiOiJodHRwczovL25zLmFkb2JlLmNvbS94ZG0vY2hhbm5lbC10eXBlcy93ZWIifSwibWVzc2FnZVByb2ZpbGVJRCI6ImY1Y2Q5OTk1LTZiNDQtNDIyMS05YWI3LTViNTMzOGQ1ZjE5MyJ9fQ==", + }, + strategies: [ + { + strategyID: "3VQe3oIqiYq2RAsYzmDTSf", + treatmentID: "yu7rkogezumca7i0i44v", + }, + ], + activity: { + id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", + }, + correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", + }, + }, + }, + { + id: "d1f7d411-a549-47bc-a4d8-c8e638b0a46b", + items: [ + { + schema: MESSAGE_CONTENT_CARD, + data: { + expiryDate: 1712190456, + publishedDate: 1678184640000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/gold-coin.jpg", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Now you're ready to earn!", + title: "Funds deposited!", + }, + contentType: "application/json", + qualifiedDate: 1683042653905, + displayedDate: 1683042653909, + }, + id: "0263e171-fa32-4c7a-9611-36b28137a81d", + }, + ], + scope: "web://mywebsite.com/my-cards", + }, + ]; + let render; + let collect; + let processPropositions; + let createProposition; + let onDecisionHandler; + let renderedPropositions; + let notificationHandler; + beforeEach(() => { + render = vi.fn().mockReturnValue( + Promise.resolve([ + { + id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", + hi: true, + }, + ]), + ); + collect = vi.fn().mockReturnValue(Promise.resolve()); + processPropositions = vi.fn().mockReturnValue({ + render, + returnedPropositions: PROPOSITIONS, + }); + createProposition = injectCreateProposition({ + preprocess: (data) => data, + isPageWideSurface: () => false, + }); + renderedPropositions = { + concat: vi.fn(), + }; + notificationHandler = createNotificationHandler( + collect, + renderedPropositions, + ); + onDecisionHandler = createOnDecisionHandler({ + processPropositions, + createProposition, + notificationHandler, + }); + }); + it("does not call render if renderDecisions=false", () => { + onDecisionHandler({ + viewName: "blippi", + renderDecisions: false, + propositions: PROPOSITIONS, + }); + expect(render).not.toHaveBeenCalled(); + }); + it("calls render if renderDecisions=true", async () => { + const mockEvent = { + getViewName: () => "blippi", + }; + const { propositions } = await onDecisionHandler({ + event: mockEvent, + personalization: {}, + renderDecisions: true, + propositions: PROPOSITIONS, + }); + expect(propositions).toEqual(PROPOSITIONS); + expect(render).toHaveBeenCalledTimes(1); + expect(collect).toHaveBeenNthCalledWith(1, { + decisionsMeta: [ + { + id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", + hi: true, + }, + ], + viewName: "blippi", + }); + expect(renderedPropositions.concat).not.toHaveBeenCalled(); + }); + it("defers sending display notification when sendDisplayEvent=false", async () => { + await onDecisionHandler({ + renderDecisions: true, + propositions: PROPOSITIONS, + event: { + getViewName: () => "blippi", + }, + personalization: { + sendDisplayEvent: false, + }, + }); + expect(collect).not.toHaveBeenCalled(); + expect(renderedPropositions.concat).toHaveBeenCalledTimes(1); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createPersonalizationDetails.spec.js b/vtest/unit/specs/components/Personalization/createPersonalizationDetails.spec.js new file mode 100644 index 000000000..21bbad23c --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createPersonalizationDetails.spec.js @@ -0,0 +1,521 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import PAGE_WIDE_SCOPE from "../../../../../src/constants/pageWideScope.js"; +import createPersonalizationDetails from "../../../../../src/components/Personalization/createPersonalizationDetails.js"; +import createGetPageLocation from "../../../../../src/components/Personalization/createGetPageLocation.js"; +import { + DEFAULT_CONTENT_ITEM, + DOM_ACTION, + HTML_CONTENT_ITEM, + MESSAGE_IN_APP, + JSON_CONTENT_ITEM, + REDIRECT_ITEM, + RULESET_ITEM, + MESSAGE_CONTENT_CARD, +} from "../../../../../src/constants/schema.js"; + +describe("Personalization::createPersonalizationDetails", () => { + const TEST_SURFACE = "web://alloy.test.com/test/page/1"; + const window = { + location: { + host: "alloy.test.com", + pathname: "/test/page/1/", + }, + }; + const getPageLocation = createGetPageLocation({ + window, + }); + let event; + let logger; + beforeEach(() => { + event = { + getViewName: vi.fn(), + }; + logger = { + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + }; + }); + + // s - has scopes or surfaces + // i - cache is initialized + // dp - defaultPersonalizationEnabled flag + // fetch - should fetch data + [ + { + s: false, + i: false, + dp: false, + fetch: false, + }, + { + s: true, + i: false, + dp: false, + fetch: true, + }, + { + s: false, + i: true, + dp: false, + fetch: false, + }, + { + s: true, + i: true, + dp: false, + fetch: true, + }, + { + s: false, + i: false, + dp: true, + fetch: true, + }, + { + s: true, + i: false, + dp: true, + fetch: true, + }, + { + s: false, + i: true, + dp: true, + fetch: true, + }, + { + s: true, + i: true, + dp: true, + fetch: true, + }, + { + s: false, + i: false, + fetch: true, + }, + { + s: true, + i: false, + fetch: true, + }, + { + s: false, + i: true, + fetch: false, + }, + { + s: true, + i: true, + fetch: true, + }, + ].forEach(({ s, i, dp, fetch }) => { + it(`should ${fetch ? "" : "not "}fetch data when ${s ? "" : "no "}scopes, the cache is ${i ? "" : "not "}initialized, and initializePersonalization is '${dp}'`, () => { + const personalizationDetails = createPersonalizationDetails({ + getPageLocation, + renderDecisions: true, + decisionScopes: [], + personalization: { + decisionScopes: s ? ["test"] : undefined, + defaultPersonalizationEnabled: dp, + }, + event, + isCacheInitialized: i, + logger, + }); + expect(personalizationDetails.shouldFetchData()).toEqual(fetch); + }); + }); + it("should fetch data when no cache, renderDecisions is true, no viewName and decisionScopes/surfaces (in non SPA world)", () => { + const decisionScopes = []; + const personalization = {}; + const renderDecisions = true; + event.getViewName.mockReturnValue(undefined); + const personalizationDetails = createPersonalizationDetails({ + getPageLocation, + renderDecisions, + decisionScopes, + personalization, + event, + isCacheInitialized: false, + logger, + }); + const expectedDecisionScopes = [PAGE_WIDE_SCOPE]; + const expectedQueryDetails = { + schemas: [ + DEFAULT_CONTENT_ITEM, + HTML_CONTENT_ITEM, + JSON_CONTENT_ITEM, + REDIRECT_ITEM, + RULESET_ITEM, + MESSAGE_IN_APP, + MESSAGE_CONTENT_CARD, + DOM_ACTION, + ], + decisionScopes: expectedDecisionScopes, + surfaces: [TEST_SURFACE], + }; + const queryDetails = personalizationDetails.createQueryDetails(); + expect(personalizationDetails.isRenderDecisions()).toEqual(true); + expect(personalizationDetails.hasScopes()).toEqual(false); + expect(personalizationDetails.hasSurfaces()).toEqual(false); + expect(queryDetails).toEqual(expectedQueryDetails); + expect(personalizationDetails.getViewName()).toEqual(undefined); + expect(personalizationDetails.shouldFetchData()).toEqual(true); + expect(personalizationDetails.hasViewName()).toEqual(false); + expect(personalizationDetails.shouldUseCachedData()).toEqual(false); + }); + it("should fetch data when no cache, renderDecisions is false, no viewName and decisionScopes/surfaces is empty", () => { + const decisionScopes = []; + const personalization = {}; + const renderDecisions = false; + event.getViewName.mockReturnValue(undefined); + const personalizationDetails = createPersonalizationDetails({ + getPageLocation, + renderDecisions, + decisionScopes, + personalization, + event, + isCacheInitialized: false, + logger, + }); + const expectedDecisionScopes = [PAGE_WIDE_SCOPE]; + const expectedQueryDetails = { + schemas: [ + DEFAULT_CONTENT_ITEM, + HTML_CONTENT_ITEM, + JSON_CONTENT_ITEM, + REDIRECT_ITEM, + RULESET_ITEM, + MESSAGE_IN_APP, + MESSAGE_CONTENT_CARD, + DOM_ACTION, + ], + decisionScopes: expectedDecisionScopes, + surfaces: [TEST_SURFACE], + }; + const queryDetails = personalizationDetails.createQueryDetails(); + expect(personalizationDetails.isRenderDecisions()).toEqual(false); + expect(personalizationDetails.hasScopes()).toEqual(false); + expect(personalizationDetails.hasSurfaces()).toEqual(false); + expect(queryDetails).toEqual(expectedQueryDetails); + expect(personalizationDetails.getViewName()).toEqual(undefined); + expect(personalizationDetails.shouldFetchData()).toEqual(true); + expect(personalizationDetails.hasViewName()).toEqual(false); + expect(personalizationDetails.shouldUseCachedData()).toEqual(false); + }); + it("should fetch data when no cache, renderDecisions is false, no viewName and decisionScopes is not empty", () => { + const decisionScopes = ["test1"]; + const personalization = {}; + const renderDecisions = false; + event.getViewName.mockReturnValue(undefined); + const personalizationDetails = createPersonalizationDetails({ + getPageLocation, + renderDecisions, + decisionScopes, + personalization, + event, + isCacheInitialized: false, + logger, + }); + const expectedDecisionScopes = ["test1", "__view__"]; + const expectedQueryDetails = { + schemas: [ + DEFAULT_CONTENT_ITEM, + HTML_CONTENT_ITEM, + JSON_CONTENT_ITEM, + REDIRECT_ITEM, + RULESET_ITEM, + MESSAGE_IN_APP, + MESSAGE_CONTENT_CARD, + DOM_ACTION, + ], + decisionScopes: expectedDecisionScopes, + surfaces: [TEST_SURFACE], + }; + const queryDetails = personalizationDetails.createQueryDetails(); + expect(personalizationDetails.isRenderDecisions()).toEqual(false); + expect(personalizationDetails.hasScopes()).toEqual(true); + expect(personalizationDetails.hasSurfaces()).toEqual(false); + expect(queryDetails).toEqual(expectedQueryDetails); + expect(personalizationDetails.getViewName()).toEqual(undefined); + expect(personalizationDetails.shouldFetchData()).toEqual(true); + expect(personalizationDetails.hasViewName()).toEqual(false); + expect(personalizationDetails.shouldUseCachedData()).toEqual(false); + }); + it("should fetch data when cache initialized, renderDecisions is false, no viewName, and decisionScopes is not empty", () => { + const decisionScopes = ["test1"]; + const personalization = {}; + const renderDecisions = false; + event.getViewName.mockReturnValue(undefined); + const personalizationDetails = createPersonalizationDetails({ + getPageLocation, + renderDecisions, + personalization, + decisionScopes, + event, + isCacheInitialized: true, + logger, + }); + const expectedDecisionScopes = ["test1"]; + const expectedQueryDetails = { + schemas: [ + DEFAULT_CONTENT_ITEM, + HTML_CONTENT_ITEM, + JSON_CONTENT_ITEM, + REDIRECT_ITEM, + RULESET_ITEM, + MESSAGE_IN_APP, + MESSAGE_CONTENT_CARD, + ], + decisionScopes: expectedDecisionScopes, + surfaces: [], + }; + const queryDetails = personalizationDetails.createQueryDetails(); + expect(personalizationDetails.isRenderDecisions()).toEqual(false); + expect(personalizationDetails.hasScopes()).toEqual(true); + expect(personalizationDetails.hasSurfaces()).toEqual(false); + expect(queryDetails).toEqual(expectedQueryDetails); + expect(personalizationDetails.getViewName()).toEqual(undefined); + expect(personalizationDetails.shouldFetchData()).toEqual(true); + expect(personalizationDetails.hasViewName()).toEqual(false); + expect(personalizationDetails.shouldUseCachedData()).toEqual(false); + }); + it("should fetch data when cache initialized, renderDecisions is false, no viewName, and surfaces is not empty", () => { + const decisionScopes = []; + const personalization = { + surfaces: ["web://test1.com"], + }; + const renderDecisions = false; + event.getViewName.mockReturnValue(undefined); + const personalizationDetails = createPersonalizationDetails({ + getPageLocation, + renderDecisions, + decisionScopes, + personalization, + event, + isCacheInitialized: true, + logger, + }); + const expectedDecisionScopes = []; + const expectedQueryDetails = { + schemas: [ + DEFAULT_CONTENT_ITEM, + HTML_CONTENT_ITEM, + JSON_CONTENT_ITEM, + REDIRECT_ITEM, + RULESET_ITEM, + MESSAGE_IN_APP, + MESSAGE_CONTENT_CARD, + ], + decisionScopes: expectedDecisionScopes, + surfaces: ["web://test1.com/"], + }; + const queryDetails = personalizationDetails.createQueryDetails(); + expect(personalizationDetails.isRenderDecisions()).toEqual(false); + expect(personalizationDetails.hasScopes()).toEqual(false); + expect(personalizationDetails.hasSurfaces()).toEqual(true); + expect(queryDetails).toEqual(expectedQueryDetails); + expect(personalizationDetails.getViewName()).toEqual(undefined); + expect(personalizationDetails.shouldFetchData()).toEqual(true); + expect(personalizationDetails.hasViewName()).toEqual(false); + expect(personalizationDetails.shouldUseCachedData()).toEqual(false); + }); + it("should fetch data when cache initialized, renderDecisions is true and decisionScopes is not empty and viewName exist", () => { + event.getViewName.mockReturnValue("cart"); + const decisionScopes = ["test1"]; + const personalization = { + decisionScopes: ["test2"], + surfaces: ["web://test1.com"], + }; + const renderDecisions = true; + const personalizationDetails = createPersonalizationDetails({ + getPageLocation, + renderDecisions, + decisionScopes, + personalization, + event, + isCacheInitialized: true, + logger, + }); + const expectedDecisionScopes = ["test1", "test2"]; + const expectedQueryDetails = { + schemas: [ + DEFAULT_CONTENT_ITEM, + HTML_CONTENT_ITEM, + JSON_CONTENT_ITEM, + REDIRECT_ITEM, + RULESET_ITEM, + MESSAGE_IN_APP, + MESSAGE_CONTENT_CARD, + ], + decisionScopes: expectedDecisionScopes, + surfaces: ["web://test1.com/"], + }; + const queryDetails = personalizationDetails.createQueryDetails(); + expect(personalizationDetails.isRenderDecisions()).toEqual(true); + expect(personalizationDetails.hasScopes()).toEqual(true); + expect(personalizationDetails.hasSurfaces()).toEqual(true); + expect(queryDetails).toEqual(expectedQueryDetails); + expect(personalizationDetails.getViewName()).toEqual("cart"); + expect(personalizationDetails.shouldFetchData()).toEqual(true); + expect(personalizationDetails.hasViewName()).toEqual(true); + }); + it("should do nothing when cache is initialized, renderDecisions true, no viewName and decisionScopes is empty", () => { + event.getViewName.mockReturnValue(undefined); + const decisionScopes = []; + const personalization = {}; + const renderDecisions = true; + const personalizationDetails = createPersonalizationDetails({ + getPageLocation, + renderDecisions, + decisionScopes, + personalization, + event, + isCacheInitialized: true, + logger, + }); + expect(personalizationDetails.isRenderDecisions()).toEqual(true); + expect(personalizationDetails.hasScopes()).toEqual(false); + expect(personalizationDetails.hasSurfaces()).toEqual(false); + expect(personalizationDetails.getViewName()).toEqual(undefined); + expect(personalizationDetails.shouldFetchData()).toEqual(false); + expect(personalizationDetails.hasViewName()).toEqual(false); + expect(personalizationDetails.shouldUseCachedData()).toEqual(false); + }); + it("should do nothing when cache is initialized, renderDecisions false, no viewName and decisionScopes is empty", () => { + event.getViewName.mockReturnValue(undefined); + const decisionScopes = []; + const personalization = {}; + const renderDecisions = false; + const personalizationDetails = createPersonalizationDetails({ + getPageLocation, + renderDecisions, + decisionScopes, + personalization, + event, + isCacheInitialized: true, + logger, + }); + expect(personalizationDetails.isRenderDecisions()).toEqual(false); + expect(personalizationDetails.hasScopes()).toEqual(false); + expect(personalizationDetails.hasSurfaces()).toEqual(false); + expect(personalizationDetails.getViewName()).toEqual(undefined); + expect(personalizationDetails.shouldFetchData()).toEqual(false); + expect(personalizationDetails.hasViewName()).toEqual(false); + expect(personalizationDetails.shouldUseCachedData()).toEqual(false); + }); + it("should use cache when cache initialized, renderDecisions is true and decisionScopes is empty and viewName exist", () => { + event.getViewName.mockReturnValue("cart"); + const decisionScopes = []; + const personalization = {}; + const renderDecisions = true; + const personalizationDetails = createPersonalizationDetails({ + getPageLocation, + renderDecisions, + personalization, + decisionScopes, + event, + isCacheInitialized: true, + logger, + }); + expect(personalizationDetails.isRenderDecisions()).toEqual(true); + expect(personalizationDetails.hasScopes()).toEqual(false); + expect(personalizationDetails.hasSurfaces()).toEqual(false); + expect(personalizationDetails.getViewName()).toEqual("cart"); + expect(personalizationDetails.hasViewName()).toEqual(true); + expect(personalizationDetails.shouldFetchData()).toEqual(false); + expect(personalizationDetails.shouldUseCachedData()).toEqual(true); + }); + it("should use cache when cache initialized, renderDecisions is false and decisionScopes is empty and viewName exist", () => { + event.getViewName.mockReturnValue("cart"); + const decisionScopes = []; + const personalization = {}; + const renderDecisions = false; + const personalizationDetails = createPersonalizationDetails({ + getPageLocation, + renderDecisions, + personalization, + decisionScopes, + event, + isCacheInitialized: true, + logger, + }); + expect(personalizationDetails.isRenderDecisions()).toEqual(false); + expect(personalizationDetails.hasScopes()).toEqual(false); + expect(personalizationDetails.hasSurfaces()).toEqual(false); + expect(personalizationDetails.getViewName()).toEqual("cart"); + expect(personalizationDetails.hasViewName()).toEqual(true); + expect(personalizationDetails.shouldFetchData()).toEqual(false); + expect(personalizationDetails.shouldUseCachedData()).toEqual(true); + }); + it("should fetch data when cache initialized, renderDecisions is true and decisionScopes has __view__ and viewName exist", () => { + event.getViewName.mockReturnValue("cart"); + const decisionScopes = ["__view__"]; + const personalization = { + surfaces: [TEST_SURFACE], + }; + const renderDecisions = true; + const personalizationDetails = createPersonalizationDetails({ + getPageLocation, + renderDecisions, + decisionScopes, + personalization, + event, + isCacheInitialized: true, + logger, + }); + const expectedDecisionScopes = ["__view__"]; + const expectedQueryDetails = { + schemas: [ + DEFAULT_CONTENT_ITEM, + HTML_CONTENT_ITEM, + JSON_CONTENT_ITEM, + REDIRECT_ITEM, + RULESET_ITEM, + MESSAGE_IN_APP, + MESSAGE_CONTENT_CARD, + DOM_ACTION, + ], + decisionScopes: expectedDecisionScopes, + surfaces: [TEST_SURFACE], + }; + const queryDetails = personalizationDetails.createQueryDetails(); + expect(personalizationDetails.isRenderDecisions()).toEqual(true); + expect(personalizationDetails.hasScopes()).toEqual(true); + expect(personalizationDetails.hasSurfaces()).toEqual(true); + expect(queryDetails).toEqual(expectedQueryDetails); + expect(personalizationDetails.getViewName()).toEqual("cart"); + expect(personalizationDetails.shouldFetchData()).toEqual(true); + expect(personalizationDetails.hasViewName()).toEqual(true); + }); + it("hasViewName should return false when viewName is empty", () => { + const decisionScopes = []; + const personalization = {}; + const renderDecisions = true; + event.getViewName.mockReturnValue(""); + const personalizationDetails = createPersonalizationDetails({ + getPageLocation, + renderDecisions, + decisionScopes, + personalization, + event, + isCacheInitialized: false, + logger, + }); + expect(personalizationDetails.isRenderDecisions()).toEqual(true); + expect(personalizationDetails.hasViewName()).toEqual(false); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createPreprocessors.spec.js b/vtest/unit/specs/components/Personalization/createPreprocessors.spec.js new file mode 100644 index 000000000..e7516b647 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createPreprocessors.spec.js @@ -0,0 +1,41 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { DOM_ACTION } from "../../../../../src/constants/schema.js"; +import createPreprocessors from "../../../../../src/components/Personalization/createPreprocessors.js"; + +describe("Personalization::createPreprocessors", () => { + it("has dom-action preprocessors", () => { + const preprocessors = createPreprocessors(); + expect(preprocessors).toEqual({ + [DOM_ACTION]: expect.any(Array), + }); + expect(preprocessors[DOM_ACTION].length).toEqual(2); + preprocessors[DOM_ACTION].forEach((preprocessor) => { + expect(preprocessor).toEqual(expect.any(Function)); + }); + }); + it("is structured correctly", () => { + const preprocessors = createPreprocessors(); + Object.keys(preprocessors).forEach((key) => { + expect(key.startsWith("https://ns.adobe.com/personalization/")).toBe( + true, + ); + }); + Object.values(preprocessors).forEach((list) => { + expect(list instanceof Array).toBe(true); + list.forEach((preprocessor) => { + expect(preprocessor).toEqual(expect.any(Function)); + }); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createSetTargetMigration.spec.js b/vtest/unit/specs/components/Personalization/createSetTargetMigration.spec.js new file mode 100644 index 000000000..1e47b7036 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createSetTargetMigration.spec.js @@ -0,0 +1,46 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createSetTargetMigration from "../../../../../src/components/Personalization/createSetTargetMigration.js"; + +describe("Personalization::createSetTargetMigration", () => { + let request; + let payload; + beforeEach(() => { + request = { + getPayload: vi.fn(), + }; + payload = { + mergeMeta: vi.fn(), + }; + request.getPayload.mockReturnValue(payload); + }); + it("adds to request meta if targetMigrationEnabled=true is configured", () => { + const setTargetMigration = createSetTargetMigration({ + targetMigrationEnabled: true, + }); + setTargetMigration(request); + expect(payload.mergeMeta).toHaveBeenNthCalledWith(1, { + target: { + migration: true, + }, + }); + }); + it("does not add to request meta if targetMigrationEnabled is not configured", () => { + const setTargetMigration = createSetTargetMigration({ + targetMigrationEnabled: false, + }); + setTargetMigration(request); + expect(payload.mergeMeta).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/createViewCacheManager.spec.js b/vtest/unit/specs/components/Personalization/createViewCacheManager.spec.js new file mode 100644 index 000000000..8f44706a7 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createViewCacheManager.spec.js @@ -0,0 +1,206 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, describe, it, expect } from "vitest"; +import { DEFAULT_CONTENT_ITEM } from "../../../../../src/constants/schema.js"; +import createViewCacheManager from "../../../../../src/components/Personalization/createViewCacheManager.js"; +import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; + +const propsToJSON = (props) => props.map((p) => p.toJSON()); +describe("Personalization::createViewCacheManager", () => { + const viewHandles = [ + { + id: "foo1", + scope: "home", + }, + { + id: "foo2", + scope: "home", + }, + { + id: "foo3", + scope: "cart", + }, + { + id: "foo4", + scope: "other", + }, + ]; + let createProposition; + let propositions; + beforeEach(() => { + createProposition = (viewHandle) => { + const { scope } = viewHandle; + return { + getScope() { + return scope; + }, + toJSON() { + return viewHandle; + }, + }; + }; + propositions = viewHandles.map(createProposition); + }); + it("stores and gets the decisions based on a viewName", async () => { + const viewCacheManager = createViewCacheManager({ + createProposition, + }); + const cacheUpdate = viewCacheManager.createCacheUpdate("home"); + const resultingHandles = cacheUpdate.update(propositions); + expect(resultingHandles).toEqual([propositions[0], propositions[1]]); + const homeViews = await viewCacheManager.getView("home"); + expect(homeViews).toEqual([propositions[0], propositions[1]]); + const cartViews = await viewCacheManager.getView("cart"); + expect(cartViews).toEqual([propositions[2]]); + const otherViews = await viewCacheManager.getView("other"); + expect(otherViews).toEqual([propositions[3]]); + }); + it("should be no views when decisions deferred is rejected", async () => { + const viewCacheManager = createViewCacheManager({ + createProposition, + }); + const cacheUpdate = viewCacheManager.createCacheUpdate("home"); + cacheUpdate.cancel(); + const homeViews = await viewCacheManager.getView("home"); + expect(homeViews.map((h) => h.toJSON())).toEqual([ + { + scope: "home", + scopeDetails: { + characteristics: { + scopeType: "view", + }, + }, + items: [ + { + schema: DEFAULT_CONTENT_ITEM, + }, + ], + }, + ]); + }); + it("should not be initialized when first created", () => { + const viewCacheManager = createViewCacheManager({ + createProposition, + }); + expect(viewCacheManager.isInitialized()).toBe(false); + }); + it("should be initialized when first cache update is created", () => { + const viewCacheManager = createViewCacheManager({ + createProposition, + }); + viewCacheManager.createCacheUpdate("home"); + expect(viewCacheManager.isInitialized()).toBe(true); + }); + it("allows you to store the views multiple times", async () => { + const viewCacheManager = createViewCacheManager({ + createProposition, + }); + const cacheUpdate1 = viewCacheManager.createCacheUpdate("cart"); + const cartProps = await cacheUpdate1.update(propositions); + expect(cartProps).toEqual([propositions[2]]); + const cacheUpdate2 = viewCacheManager.createCacheUpdate(); + const cartViewPromise = viewCacheManager.getView("cart"); + await flushPromiseChains(); + // await expectAsync(cartViewPromise).toBePending(); + + const cartPromiseIsPending = await Promise.race([ + cartViewPromise.then(() => false), + new Promise((resolve) => { + setTimeout(() => resolve(true), 0); + }), + ]); + expect(cartPromiseIsPending).toBe(true); + cacheUpdate2.update([ + createProposition({ + id: "foo4", + items: [], + scope: "about", + }), + ]); + expect(await cartViewPromise).toEqual([propositions[2]]); + expect(await viewCacheManager.getView("about").then(propsToJSON)).toEqual([ + { + id: "foo4", + items: [], + scope: "about", + }, + ]); + }); + it("is initialized after the first storeViews call", () => { + const viewCacheManager = createViewCacheManager({ + createProposition, + }); + viewCacheManager.createCacheUpdate(); + expect(viewCacheManager.isInitialized()).toBe(true); + }); + it("is initialized even after a failure", async () => { + const viewCacheManager = createViewCacheManager({ + createProposition, + }); + const update1 = viewCacheManager.createCacheUpdate(); + update1.cancel(); + await flushPromiseChains(); + expect(viewCacheManager.isInitialized()).toBe(true); + }); + it("reverts to old storage after a failure", async () => { + const viewCacheManager = createViewCacheManager({ + createProposition, + }); + const update1 = viewCacheManager.createCacheUpdate(); + update1.update(propositions); + const update2 = viewCacheManager.createCacheUpdate(); + update2.cancel(); + expect(await viewCacheManager.getView("home")).toEqual([ + propositions[0], + propositions[1], + ]); + }); + /* + it("applies the decisions in the order they were requested", async () => { + const viewCacheManager = createViewCacheManager({ createProposition }); + const update1 = viewCacheManager.createCacheUpdate(); + const viewPromise1 = viewCacheManager.getView(cartView).then(props => props.map(p => p.toJSON())); + const update2 = viewCacheManager.createCacheUpdate(); + const viewPromise2 = viewCacheManager.getView(cartView).then(props => props.map(p => p.toJSON())); + update2.update([ + createProposition({ + id: "foo4", + items: [], + scope: "cart" + }) + ]); + update1.update(viewHandles); + await expectAsync(viewPromise2).toBeResolvedTo([ + { + id: "foo4", + items: [], + scope: "cart" + } + ]); + await expectAsync(viewPromise1).toBeResolvedTo([ + { + id: "foo3", + items: [], + scope: "cart" + } + ]); + await expectAsync(viewCacheManager.getView(cartView)).toBeResolvedTo([ + { + id: "foo4", + items: [], + scope: "cart" + } + ]); + }); + */ +}); diff --git a/vtest/unit/specs/components/Personalization/createViewChangeHandler.spec.js b/vtest/unit/specs/components/Personalization/createViewChangeHandler.spec.js new file mode 100644 index 000000000..588d0cc67 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/createViewChangeHandler.spec.js @@ -0,0 +1,79 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createViewChangeHandler from "../../../../../src/components/Personalization/createViewChangeHandler.js"; +import { CART_VIEW_DECISIONS } from "./responsesMock/eventResponses.js"; +import injectCreateProposition from "../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; + +describe("Personalization::createViewChangeHandler", () => { + let processPropositions; + let viewCache; + let personalizationDetails; + let event; + let onResponse; + let logger; + let createProposition; + beforeEach(() => { + logger = { + logOnContentRendering: vi.fn(), + }; + processPropositions = vi.fn(); + viewCache = { + getView: vi.fn(), + }; + personalizationDetails = { + isRenderDecisions: vi.fn(), + getViewName: vi.fn(), + }; + event = "myevent"; + onResponse = vi.fn(); + createProposition = injectCreateProposition({ + preprocess: (data) => data, + isPageWideSurface: () => false, + }); + }); + const run = async () => { + const viewChangeHandler = createViewChangeHandler({ + logger, + processPropositions, + viewCache, + }); + const decisionsMeta = await viewChangeHandler({ + event, + personalizationDetails, + onResponse, + }); + const result = onResponse.mock.calls[0][0](); + return { + decisionsMeta, + result, + }; + }; + it("should trigger render if renderDecisions is true", async () => { + viewCache.getView.mockReturnValue( + Promise.resolve(CART_VIEW_DECISIONS.map((p) => createProposition(p))), + ); + personalizationDetails.isRenderDecisions.mockReturnValue(true); + personalizationDetails.getViewName.mockReturnValue("cart"); + processPropositions.mockReturnValue({ + render: () => Promise.resolve("decisionMeta"), + returnedPropositions: [], + returnedDecisions: CART_VIEW_DECISIONS, + }); + const { decisionsMeta, result } = await run(); + expect(logger.logOnContentRendering).toHaveBeenCalledTimes(1); + expect(processPropositions).toHaveBeenCalledTimes(1); + expect(decisionsMeta).toEqual("decisionMeta"); + expect(result.decisions).toEqual(CART_VIEW_DECISIONS); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/addNonceToInlineStyleElements.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/addNonceToInlineStyleElements.spec.js new file mode 100644 index 000000000..5ff331e5b --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/addNonceToInlineStyleElements.spec.js @@ -0,0 +1,45 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterEach, describe, it, expect } from "vitest"; +import addNonceToInlineStyleElements from "../../../../../../src/components/Personalization/dom-actions/addNonceToInlineStyleElements.js"; +import { testResetCachedNonce } from "../../../../../../src/components/Personalization/dom-actions/dom/getNonce.js"; +import { createFragment } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import { STYLE } from "../../../../../../src/constants/tagName.js"; +import { + selectNodes, + removeNode, + appendNode, + createNode, +} from "../../../../../../src/utils/dom/index.js"; + +describe("Personalization::dom-actions::addNonceToInlineStyleElements", () => { + afterEach(() => { + selectNodes("#fooById").forEach(removeNode); + }); + it("should add nonce to inline style elements if available", () => { + testResetCachedNonce(); + // Make sure a nonce is available to alloy + appendNode( + document.head, + createNode("script", { + id: "fooById", + nonce: "123", + }), + ); + const fragmentHtml = ""; + const fragment = createFragment(fragmentHtml); + addNonceToInlineStyleElements(fragment); + const styleNodes = selectNodes(STYLE, fragment); + expect(styleNodes[0].nonce).toEqual("123"); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js new file mode 100644 index 000000000..a0445fffa --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js @@ -0,0 +1,72 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, + selectNodes, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_APPEND_HTML } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::appendHtml", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("appendHtml"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_APPEND_HTML, + }); + }); + afterEach(() => { + cleanUpDomChanges("appendHtml"); + }); + it("should append personalized content", () => { + const modules = initDomActionsModules(); + const { appendHtml } = modules; + const element = createNode( + "ul", + { + id: "appendHtml", + }, + { + innerHTML: "
  • 1
  • ", + }, + ); + appendNode(document.body, element); + const settings = { + selector: "#appendHtml", + prehidingSelector: "#appendHtml", + content: `
  • 2
  • 3
  • `, + meta: { + a: 1, + }, + }; + return appendHtml(settings, decorateProposition).then(() => { + const result = selectNodes("ul#appendHtml li"); + expect(result.length).toEqual(3); + expect(result[0].innerHTML).toEqual("1"); + expect(result[1].innerHTML).toEqual("2"); + expect(result[2].innerHTML).toEqual("3"); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js new file mode 100644 index 000000000..99758665e --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js @@ -0,0 +1,162 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, afterEach, describe, it, expect } from "vitest"; +import { + createNode, + appendNode, + selectNodes, + removeNode, +} from "../../../../../../../src/utils/dom/index.js"; +import collectClicks from "../../../../../../../src/components/Personalization/dom-actions/clicks/collectClicks.js"; + +describe("Personalization::tracking::clicks", () => { + afterEach(() => { + selectNodes(".eq").forEach(removeNode); + }); + it("should collect clicks", () => { + const meta = [ + { + id: "AT:1234", + scope: "example_scope", + }, + ]; + const getClickMetas = vi.fn().mockReturnValue(meta); + const content = ` +
    +
    first
    + +
    second
    + +
    third
    +
    + `; + const node = createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, node); + const selectors = ["#abc:eq(0) > div.b:eq(0) > div.c"]; + const element = document.getElementById("one"); + const { decisionsMeta, propositionActionLabel } = collectClicks( + element, + selectors, + getClickMetas, + ); + expect(decisionsMeta).toEqual(meta); + expect(propositionActionLabel).toEqual(""); + }); + it("should collect and dedupe clicks with labels", () => { + const metaOuter = [ + { + id: "AT:outer-id-1", + scope: "outer-scope1", + }, + { + id: "AJO:inner-id-2", + scope: "inner-scope2", + trackingLabel: "outer-label-2", + }, + { + id: "AJO:outer-id-3", + scope: "outer-scope3", + trackingLabel: "outer-label-3", + }, + ]; + const metaInner = [ + { + id: "AT:inner-id-1", + scope: "inner-scope1", + }, + { + id: "AJO:inner-id-2", + scope: "inner-scope2", + trackingLabel: "inner-label-2", + }, + { + id: "AJO:inner-id-3", + scope: "inner-scope3", + trackingLabel: "inner-label-3", + }, + ]; + const getClickMetas = vi.fn((...args) => { + if (args[0] === "#abc:eq(0) > div.b:eq(0)") { + return metaOuter; + } + if (args[0] === "#abc:eq(0) > div.b:eq(0) > div.c") { + return metaInner; + } + + return undefined; + }); + + const content = ` +
    +
    first
    + +
    second
    + +
    third
    +
    + `; + const node = createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, node); + const selectors = [ + "#abc:eq(0) > div.b:eq(0)", + "#abc:eq(0) > div.b:eq(0) > div.c", + ]; + const element = document.getElementById("one"); + const { decisionsMeta, propositionActionLabel } = collectClicks( + element, + selectors, + getClickMetas, + ); + expect(decisionsMeta).toEqual([ + { + id: "AT:outer-id-1", + scope: "outer-scope1", + }, + { + id: "AJO:inner-id-2", + scope: "inner-scope2", + }, + { + id: "AJO:outer-id-3", + scope: "outer-scope3", + }, + { + id: "AT:inner-id-1", + scope: "inner-scope1", + }, + { + id: "AJO:inner-id-3", + scope: "inner-scope3", + }, + ]); + expect(propositionActionLabel).toEqual("inner-label-2"); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/clicks/collectInteractions.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/clicks/collectInteractions.spec.js new file mode 100644 index 000000000..7d9ac74f3 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/clicks/collectInteractions.spec.js @@ -0,0 +1,616 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import createInteractionStorage from "../../../../../../../src/components/Personalization/createInteractionStorage.js"; +import { + appendNode, + createNode, + removeNode, + selectNodes, +} from "../../../../../../../src/utils/dom/index.js"; +import collectInteractions from "../../../../../../../src/components/Personalization/dom-actions/clicks/collectInteractions.js"; +import { + ADOBE_JOURNEY_OPTIMIZER, + ADOBE_TARGET, +} from "../../../../../../../src/constants/decisionProvider.js"; +import { + ALWAYS, + DECORATED_ELEMENTS_ONLY, + NEVER, +} from "../../../../../../../src/constants/propositionInteractionType.js"; + +describe("Personalization::tracking::interactions", () => { + let storeInteractionMeta; + let getInteractionMetas; + let autoCollectPropositionInteractions; + let scopeDetails; + beforeEach(() => { + selectNodes(".eq").forEach(removeNode); + ({ storeInteractionMeta, getInteractionMetas } = + createInteractionStorage()); + autoCollectPropositionInteractions = { + [ADOBE_JOURNEY_OPTIMIZER]: ALWAYS, + [ADOBE_TARGET]: NEVER, + }; + scopeDetails = { + decisionProvider: ADOBE_JOURNEY_OPTIMIZER, + }; + }); + afterEach(() => { + selectNodes(".eq").forEach(removeNode); + }); + it("should collect interactions with interact-id, label and token", () => { + storeInteractionMeta( + "AT:1234", + "063", + "page", + { + id: "AT:1234", + scope: "example_scope", + scopeDetails, + }, + 99, + ); + const content = ` +
    +
    first
    +
    second
    +
    third
    +
    + `; + const node = createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, node); + [ + { + elementId: "one", + expectedLabel: "lbl-first", + expectedToken: "tok-111", + }, + { + elementId: "two", + expectedLabel: "lbl-second", + expectedToken: "tok-222", + }, + { + elementId: "three", + expectedLabel: "lbl-third", + expectedToken: "tok-333", + }, + ].forEach((definition) => { + const element = document.getElementById(definition.elementId); + const { decisionsMeta, propositionActionLabel, propositionActionToken } = + collectInteractions( + element, + getInteractionMetas, + autoCollectPropositionInteractions, + ); + expect(decisionsMeta).toEqual([ + { + id: "AT:1234", + scope: "example_scope", + scopeDetails, + items: [ + { + id: "063", + }, + ], + }, + ]); + expect(propositionActionLabel).toEqual(definition.expectedLabel); + expect(propositionActionToken).toEqual(definition.expectedToken); + }); + }); + it("should collect interactions for elements decorated with label when configured for 'decoratedElementsOnly'", () => { + autoCollectPropositionInteractions = { + [ADOBE_JOURNEY_OPTIMIZER]: DECORATED_ELEMENTS_ONLY, + [ADOBE_TARGET]: NEVER, + }; + storeInteractionMeta( + "AT:1234", + "063", + "page", + { + id: "AT:1234", + scope: "example_scope", + scopeDetails, + }, + 99, + ); + const content = ` +
    +
    first
    +
    second
    +
    third
    +
    + `; + const node = createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, node); + [ + { + elementId: "one", + expectedLabel: "lbl-first", + expectedToken: "tok-111", + }, + { + elementId: "two", + expectedLabel: "lbl-second", + expectedToken: "tok-222", + }, + { + elementId: "three", + expectedLabel: "lbl-third", + expectedToken: "tok-333", + }, + ].forEach((definition) => { + const element = document.getElementById(definition.elementId); + const { decisionsMeta, propositionActionLabel, propositionActionToken } = + collectInteractions( + element, + getInteractionMetas, + autoCollectPropositionInteractions, + ); + expect(decisionsMeta).toEqual([ + { + id: "AT:1234", + scope: "example_scope", + scopeDetails, + items: [ + { + id: "063", + }, + ], + }, + ]); + expect(propositionActionLabel).toEqual(definition.expectedLabel); + expect(propositionActionToken).toEqual(definition.expectedToken); + }); + }); + it("should NOT collect interactions for elements NOT decorated with label when configured for 'decoratedElementsOnly'", () => { + autoCollectPropositionInteractions = { + [ADOBE_JOURNEY_OPTIMIZER]: DECORATED_ELEMENTS_ONLY, + [ADOBE_TARGET]: NEVER, + }; + storeInteractionMeta( + "AT:1234", + "063", + "page", + { + id: "AT:1234", + scope: "example_scope", + scopeDetails, + }, + 99, + ); + const content = ` +
    +
    first
    +
    second
    +
    third
    +
    + `; + const node = createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, node); + [ + { + elementId: "one", + expectedLabel: undefined, + expectedToken: undefined, + }, + { + elementId: "two", + expectedLabel: undefined, + expectedToken: undefined, + }, + { + elementId: "three", + expectedLabel: undefined, + expectedToken: undefined, + }, + ].forEach((definition) => { + const element = document.getElementById(definition.elementId); + const { decisionsMeta, propositionActionLabel, propositionActionToken } = + collectInteractions( + element, + getInteractionMetas, + autoCollectPropositionInteractions, + ); + expect(decisionsMeta).toEqual([]); + expect(propositionActionLabel).toBeNull(); + expect(propositionActionToken).toBeNull(); + }); + }); + it("should find closest label and token", () => { + storeInteractionMeta( + "AT:1234", + "063", + "page", + { + id: "AT:1234", + scope: "example_scope", + scopeDetails, + }, + 99, + ); + const content = ` +
    +
    first
    +
    second
    +
    third
    +
    + `; + const node = createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, node); + [ + { + elementId: "one", + expectedLabel: "lbl-main", + expectedToken: "tok-main", + }, + { + elementId: "two", + expectedLabel: "lbl-main", + expectedToken: "tok-main", + }, + { + elementId: "three", + expectedLabel: "lbl-third", + expectedToken: "tok-333", + }, + ].forEach((definition) => { + const element = document.getElementById(definition.elementId); + const { decisionsMeta, propositionActionLabel, propositionActionToken } = + collectInteractions( + element, + getInteractionMetas, + autoCollectPropositionInteractions, + ); + expect(decisionsMeta).toEqual([ + { + id: "AT:1234", + scope: "example_scope", + scopeDetails, + items: [ + { + id: "063", + }, + ], + }, + ]); + expect(propositionActionLabel).toEqual(definition.expectedLabel); + expect(propositionActionToken).toEqual(definition.expectedToken); + }); + }); + it("should find closest label and token (nesting)", () => { + storeInteractionMeta( + "AT:1234", + "063", + "page", + { + id: "AT:1234", + scope: "example_scope", + scopeDetails, + }, + 99, + ); + const content = ` +
    +
    +
    +
    +
    +
    +
    +
    +
    + `; + const node = createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, node); + [ + { + elementId: "onegreatgrandchild", + expectedLabel: "lbl-onegrandchild", + expectedToken: "tok-onegreatgrandchild", + }, + { + elementId: "onegrandchild", + expectedLabel: "lbl-onegrandchild", + expectedToken: null, + }, + { + elementId: "onechild", + expectedLabel: null, + expectedToken: null, + }, + ].forEach((definition) => { + const element = document.getElementById(definition.elementId); + const { decisionsMeta, propositionActionLabel, propositionActionToken } = + collectInteractions( + element, + getInteractionMetas, + autoCollectPropositionInteractions, + ); + expect(decisionsMeta).toEqual([ + { + id: "AT:1234", + scope: "example_scope", + scopeDetails, + items: [ + { + id: "063", + }, + ], + }, + ]); + expect(propositionActionLabel).toEqual(definition.expectedLabel); + expect(propositionActionToken).toEqual(definition.expectedToken); + }); + }); + it("handles case where no interact-id exists", () => { + storeInteractionMeta( + "AT:1234", + "063", + "page", + { + id: "AT:1234", + scope: "example_scope", + scopeDetails, + }, + 99, + ); + const content = ` +
    +
    +
    +
    +
    +
    +
    +
    +
    + `; + const node = createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, node); + const element = document.getElementById("onegreatgrandchild"); + expect( + collectInteractions( + element, + getInteractionMetas, + autoCollectPropositionInteractions, + ), + ).toEqual({}); + }); + it("should collect and dedupe interactions with labels", () => { + // outer + storeInteractionMeta( + "1", + "p", + "page", + { + id: "AT:outer-id-1", + scope: "outer-scope1", + scopeDetails, + }, + 99, + ); + storeInteractionMeta( + "1", + "a", + "page", + { + id: "AT:outer-id-1", + scope: "outer-scope1", + scopeDetails, + }, + 99, + ); + storeInteractionMeta( + "2", + "b", + "page", + { + id: "AJO:inner-id-2", + scope: "inner-scope2", + scopeDetails, + trackingLabel: "outer-label-2", + }, + 99, + ); + storeInteractionMeta( + "3", + "c", + "page", + { + id: "AJO:outer-id-3", + scope: "outer-scope3", + scopeDetails, + trackingLabel: "outer-label-3", + }, + 99, + ); + + // inner + storeInteractionMeta( + "4", + "d", + "page", + { + id: "AT:inner-id-1", + scope: "inner-scope1", + scopeDetails, + }, + 11, + ); + storeInteractionMeta( + "2", + "b", + "page", + { + id: "AJO:inner-id-2", + scope: "inner-scope2", + scopeDetails, + trackingLabel: "inner-label-2", + }, + 11, + ); + storeInteractionMeta( + "6", + "f", + "page", + { + id: "AJO:inner-id-3", + scope: "inner-scope3", + scopeDetails, + trackingLabel: "inner-label-3", + }, + 11, + ); + const content = ` +
    +
    first
    +
    second
    +
    third
    +
    + `; + const node = createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, node); + const element = document.getElementById("one"); + const { decisionsMeta, propositionActionLabel, propositionActionToken } = + collectInteractions( + element, + getInteractionMetas, + autoCollectPropositionInteractions, + ); + expect(decisionsMeta).toEqual( + expect.arrayContaining([ + { + id: "AJO:inner-id-2", + scope: "inner-scope2", + scopeDetails, + items: [ + { + id: "b", + }, + ], + }, + { + id: "AT:inner-id-1", + scope: "inner-scope1", + scopeDetails, + items: [ + { + id: "d", + }, + ], + }, + { + id: "AJO:inner-id-3", + scope: "inner-scope3", + scopeDetails, + items: [ + { + id: "f", + }, + ], + }, + { + id: "AT:outer-id-1", + scope: "outer-scope1", + scopeDetails, + items: [ + { + id: "p", + }, + { + id: "a", + }, + ], + }, + { + id: "AJO:outer-id-3", + scope: "outer-scope3", + scopeDetails, + items: [ + { + id: "c", + }, + ], + }, + ]), + ); + expect(propositionActionLabel).toEqual("inner-label-2"); + expect(propositionActionToken).toEqual("inner-token-2"); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/collectInteractions.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/collectInteractions.spec.js new file mode 100644 index 000000000..6520c5895 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/collectInteractions.spec.js @@ -0,0 +1,51 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { INTERACT_ID_DATA_ATTRIBUTE } from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_COLLECT_INTERACTIONS } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::collectInteractions", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("something"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_COLLECT_INTERACTIONS, + }); + }); + afterEach(() => { + cleanUpDomChanges("something"); + }); + it("should decorate element", async () => { + const itemData = { + isCool: true, + selector: "#something", + }; + const modules = initDomActionsModules(); + const element = createNode("div", { + id: "something", + }); + appendNode(document.body, element); + await modules[DOM_ACTION_COLLECT_INTERACTIONS]( + itemData, + decorateProposition, + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/createPreprocess.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/createPreprocess.spec.js new file mode 100644 index 000000000..7a938265d --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/createPreprocess.spec.js @@ -0,0 +1,93 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createPreprocess from "../../../../../../src/components/Personalization/dom-actions/createPreprocess.js"; + +describe("Personalization::dom-actions::createPreprocess", () => { + let preprocessor1; + let preprocessor2; + let preprocess; + beforeEach(() => { + preprocessor1 = vi.fn(); + preprocessor2 = vi.fn(); + preprocess = createPreprocess([preprocessor1, preprocessor2]); + }); + it("handles an empty action", () => { + expect(preprocess({})).toEqual({}); + }); + it("passes the data through", () => { + preprocessor1.mockImplementation((data) => data); + preprocessor2.mockImplementation((data) => data); + expect( + preprocess({ + a: 1, + b: 2, + }), + ).toEqual({ + a: 1, + b: 2, + }); + }); + it("passes the data through when the preprocessor returns undefined", () => { + preprocessor1.mockImplementation(() => undefined); + preprocessor2.mockImplementation(() => undefined); + expect( + preprocess({ + a: 1, + b: 2, + }), + ).toEqual({ + a: 1, + b: 2, + }); + }); + it("updates the data", () => { + preprocessor1.mockImplementation(() => ({ + c: 3, + })); + preprocessor2.mockImplementation(() => ({ + d: 4, + })); + expect( + preprocess({ + a: 1, + b: 2, + }), + ).toEqual({ + a: 1, + b: 2, + c: 3, + d: 4, + }); + }); + it("updates the data2", () => { + preprocessor1.mockImplementation((data) => ({ + ...data, + c: 3, + })); + preprocessor2.mockImplementation((data) => ({ + ...data, + d: 4, + })); + expect( + preprocess({ + a: 1, + b: 2, + }), + ).toEqual({ + a: 1, + b: 2, + c: 3, + d: 4, + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js new file mode 100644 index 000000000..39ed4a22c --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js @@ -0,0 +1,41 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, describe, it, expect } from "vitest"; +import createRedirect from "../../../../../../src/components/Personalization/dom-actions/createRedirect.js"; + +describe("createRedirect", () => { + it("redirects", () => { + const window = { + location: { + replace: vi.fn(), + href: vi.fn(), + }, + }; + const redirect = createRedirect(window); + redirect("myurl"); + expect(window.location.replace).toHaveBeenCalledWith("myurl"); + expect(window.location.href).not.toHaveBeenCalled(); + }); + it("redirects using window.location.href when preserveHistory is true", () => { + const window = { + location: { + href: vi.fn(), + replace: vi.fn(), + }, + }; + const redirectUrl = "https://www.adobe.com"; + const redirect = createRedirect(window); + redirect(redirectUrl, true); + expect(window.location.href).toBe(redirectUrl); + expect(window.location.replace).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/customCode.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/customCode.spec.js new file mode 100644 index 000000000..09335e518 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/customCode.spec.js @@ -0,0 +1,78 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_CUSTOM_CODE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::customCode", () => { + let decorateProposition; + let customCode; + let element; + beforeEach(() => { + cleanUpDomChanges("customCode"); + delete window.someEvar123; + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_CUSTOM_CODE, + }); + const modules = initDomActionsModules(); + ({ customCode } = modules); + }); + afterEach(() => { + cleanUpDomChanges("customCode"); + delete window.someEvar123; + }); + it("should set content in container that has children", async () => { + element = createNode("div", { + id: "customCode", + class: "customCode", + }); + element.innerHTML = `
    `; + appendNode(document.body, element); + const settings = { + selector: ".customCode", + prehidingSelector: ".customCode", + content: "

    Hola!

    ", + meta: { + a: 1, + }, + }; + await customCode(settings, decorateProposition); + expect(element.innerHTML).toMatch( + /

    Hola!<\/p>

    <\/div>
    <\/div>/, + ); + }); + it("should set content in container that has NO children", async () => { + element = createNode("div", { + id: "customCode", + class: "customCode", + }); + appendNode(document.body, element); + const settings = { + selector: ".customCode", + prehidingSelector: ".customCode", + content: "

    Hola!

    Hello
    ", + meta: { + a: 1, + }, + }; + await customCode(settings, decorateProposition); + expect(element.innerHTML).toMatch( + /

    Hola!<\/p>

    Hello<\/div>/, + ); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/createFragment.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/createFragment.spec.js new file mode 100644 index 000000000..d2ecaccbe --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/createFragment.spec.js @@ -0,0 +1,21 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import createFragment from "../../../../../../../src/components/Personalization/dom-actions/dom/createFragment.js"; + +describe("Personalization::helper", () => { + it("createFragmentTest", () => { + const result = createFragment(`
    foo
    `); + expect(result.firstElementChild.id).toEqual("foo"); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getAttribute.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/getAttribute.spec.js new file mode 100644 index 000000000..8eefe945d --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/getAttribute.spec.js @@ -0,0 +1,30 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import getAttribute from "../../../../../../../src/components/Personalization/dom-actions/dom/getAttribute.js"; +import createFragment from "../../../../../../../src/components/Personalization/dom-actions/dom/createFragment.js"; + +describe("Personalization::helper::dom::getAttribute", () => { + it("returns element's attribute if exists", () => { + const element = createFragment(`
    foo
    `); + const name = "id"; + const result = getAttribute(element.firstElementChild, name); + expect(result).toEqual("foo"); + }); + it("returns null if element doesn't have this attribute", () => { + const element = createFragment(`
    foo
    `); + const name = "title"; + const result = getAttribute(element.firstElementChild, name); + expect(result).toBeNull(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getChildNodes.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/getChildNodes.spec.js new file mode 100644 index 000000000..222bf6f4e --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/getChildNodes.spec.js @@ -0,0 +1,35 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import getChildNodes from "../../../../../../../src/components/Personalization/dom-actions/dom/getChildNodes.js"; +import createFragment from "../../../../../../../src/components/Personalization/dom-actions/dom/createFragment.js"; + +describe("Personalization::helper::dom::getChildNodes", () => { + it("returns an array of child nodes", () => { + const element = createFragment( + `
    foo

    hello there

    `, + ); + const result = getChildNodes(element); + expect(result.length).toEqual(3); + expect(result[0].tagName).toEqual("DIV"); + expect(result[1].tagName).toEqual("H1"); + expect(result[2].id).toEqual("div2"); + }); + + it("returns undefined when there are no children", () => { + const element = createFragment(); + const result = getChildNodes(element); + expect(result.length).toEqual(1); + expect(result[0].tagName).toBeUndefined(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getChildren.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/getChildren.spec.js new file mode 100644 index 000000000..1db630a4d --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/getChildren.spec.js @@ -0,0 +1,32 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import getChildren from "../../../../../../../src/components/Personalization/dom-actions/dom/getChildren.js"; +import createFragment from "../../../../../../../src/components/Personalization/dom-actions/dom/createFragment.js"; + +describe("Personalization::helper::dom::getChildren", () => { + it("returns an array of children", () => { + const element = createFragment( + `
    foo

    hello there

    `, + ); + const result = getChildren(element); + expect(result.length).toEqual(2); + expect(result[0].tagName).toEqual("DIV"); + expect(result[1].tagName).toEqual("H1"); + }); + it("returns empty array if there are not children", () => { + const element = createFragment(); + const result = getChildren(element); + expect(result.length).toEqual(0); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getElementById.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/getElementById.spec.js new file mode 100644 index 000000000..5f7bf39ba --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/getElementById.spec.js @@ -0,0 +1,38 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterEach, describe, it, expect } from "vitest"; +import { + selectNodes, + removeNode, + appendNode, + createNode, +} from "../../../../../../../src/utils/dom/index.js"; +import { getElementById } from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; + +describe("Personalization::DOM::getElementById", () => { + afterEach(() => { + selectNodes("#fooById").forEach(removeNode); + }); + it("should return the node if exists", () => { + appendNode( + document.head, + createNode("style", { + id: "fooById", + }), + ); + expect(getElementById("fooById")).not.toBeNull(); + }); + it("should return array when nodes are NOT present", () => { + expect(getElementById("fooById")).toBeNull(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getFirstChild.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/getFirstChild.spec.js new file mode 100644 index 000000000..200b31d79 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/getFirstChild.spec.js @@ -0,0 +1,30 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import getFirstChild from "../../../../../../../src/components/Personalization/dom-actions/dom/getFirstChild.js"; +import createFragment from "../../../../../../../src/components/Personalization/dom-actions/dom/createFragment.js"; + +describe("Personalization::helper::dom::getFirstChild", () => { + it("returns the first child node of the element", () => { + const element = createFragment( + `

    hello there

    foo
    `, + ); + const result = getFirstChild(element); + expect(result.tagName).toEqual("H1"); + }); + it("returns null if there are no child elements", () => { + const element = createFragment(); + const result = getFirstChild(element); + expect(result).toBeNull(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getNextSibling.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/getNextSibling.spec.js new file mode 100644 index 000000000..38a933066 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/getNextSibling.spec.js @@ -0,0 +1,33 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import getNextSibling from "../../../../../../../src/components/Personalization/dom-actions/dom/getNextSibling.js"; +import createFragment from "../../../../../../../src/components/Personalization/dom-actions/dom/createFragment.js"; +import getFirstChild from "../../../../../../../src/components/Personalization/dom-actions/dom/getFirstChild.js"; + +describe("Personalization::helper::dom::getNextSibling", () => { + it("returns the element next sibling", () => { + const element = createFragment( + `
    foo

    hello there

    `, + ); + const firstElement = getFirstChild(element); + const nextSibling = getNextSibling(firstElement); + expect(nextSibling.tagName).toEqual("H1"); + }); + it("returns null if the element doesn't have a sibling node", () => { + const element = createFragment(`
    foo
    `); + const firstElement = getFirstChild(element); + const nextSibling = getNextSibling(firstElement); + expect(nextSibling).toBeNull(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getNonce.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/getNonce.spec.js new file mode 100644 index 000000000..0fb947a33 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/getNonce.spec.js @@ -0,0 +1,38 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterEach, describe, it, expect } from "vitest"; +import { testResetCachedNonce } from "../../../../../../../src/components/Personalization/dom-actions/dom/getNonce.js"; +import { + selectNodes, + removeNode, + appendNode, + createNode, +} from "../../../../../../../src/utils/dom/index.js"; +import { getNonce } from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; + +describe("Personalization::DOM::getNonce", () => { + afterEach(() => { + selectNodes("#fooById").forEach(removeNode); + }); + it("should return the nonce if defined", () => { + testResetCachedNonce(); + appendNode( + document.head, + createNode("script", { + id: "fooById", + nonce: "123", + }), + ); + expect(getNonce()).toEqual("123"); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getParent.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/getParent.spec.js new file mode 100644 index 000000000..61d15b720 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/getParent.spec.js @@ -0,0 +1,41 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterEach, describe, it, expect } from "vitest"; +import getParent from "../../../../../../../src/components/Personalization/dom-actions/dom/getParent.js"; +import { + selectNodes, + removeNode, + appendNode, + createNode, +} from "../../../../../../../src/utils/dom/index.js"; +import { getElementById } from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; + +describe("Personalization::DOM::getParent", () => { + afterEach(() => { + selectNodes("#parentId").forEach(removeNode); + selectNodes("#childId").forEach(removeNode); + }); + it("returns the parent node if exists", () => { + const parentNode = createNode("div", { + id: "parentId", + }); + const childNode = createNode("div", { + id: "childId", + }); + appendNode(parentNode, childNode); + appendNode(document.head, parentNode); + const result = getParent(getElementById("childId")); + expect(result.tagName).toEqual("DIV"); + expect(result.id).toEqual("parentId"); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/helperForEq.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/helperForEq.spec.js new file mode 100644 index 000000000..686eac8ef --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/helperForEq.spec.js @@ -0,0 +1,38 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import { + isNotEqSelector, + splitWithEq, +} from "../../../../../../../src/components/Personalization/dom-actions/dom/helperForEq.js"; + +describe("Personalization::DOM::helperForEq::isNotEqSelector", () => { + it("should match when no eq", () => { + const selector = "#id"; + expect(isNotEqSelector(selector)).toEqual(true); + }); + it("should not match when eq", () => { + const selector = "#id:eq(0)"; + expect(isNotEqSelector(selector)).toEqual(false); + }); +}); +describe("Personalization::DOM::helperForEq::splitWithEq", () => { + it("should split when no eq", () => { + const selector = "#id"; + expect(splitWithEq(selector)).toEqual(["#id"]); + }); + it("should split when eq", () => { + const selector = "#id:eq(0)"; + expect(splitWithEq(selector)).toEqual(["#id", "0"]); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/insertAfter.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/insertAfter.spec.js new file mode 100644 index 000000000..16dee8692 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/insertAfter.spec.js @@ -0,0 +1,44 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterEach, describe, it, expect } from "vitest"; +import insertAfter from "../../../../../../../src/components/Personalization/dom-actions/dom/insertAfter.js"; +import { + selectNodes, + removeNode, + appendNode, + createNode, +} from "../../../../../../../src/utils/dom/index.js"; +import { + getElementById, + getNextSibling, +} from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; + +describe("Personalization::DOM::insertAfter", () => { + afterEach(() => { + selectNodes("#style1").forEach(removeNode); + selectNodes("#style2").forEach(removeNode); + }); + it("inserts a node after an element", () => { + const element1 = createNode("style", { + id: "style1", + }); + const element2 = createNode("style", { + id: "style2", + }); + appendNode(document.head, element1); + insertAfter(element1, element2); + const node1 = getElementById("style1"); + const node2 = getNextSibling(node1); + expect(node2.id).toEqual("style2"); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js new file mode 100644 index 000000000..5ca8b07f5 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js @@ -0,0 +1,43 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, +} from "../../../../../../../src/utils/dom/index.js"; +import cleanUpDomChanges from "../../../../../helpers/cleanUpDomChanges.js"; +import isDomElement from "../../../../../../../src/components/Personalization/dom-actions/dom/isDomElement.js"; + +describe("Personalization::DOM::isDomElement", () => { + const testElementId = "superfluous123"; + beforeEach(() => { + const element = createNode("div", { + id: testElementId, + class: `test-element-${testElementId}`, + }); + element.innerHTML = "test element"; + appendNode(document.body, element); + }); + afterEach(() => { + cleanUpDomChanges(testElementId); + }); + it("validates dom element", () => { + expect(isDomElement(document.getElementById(testElementId))).toBe(true); + }); + it("validates not a dom element", () => { + expect(false); + expect(false); + expect(false); + expect(false); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js new file mode 100644 index 000000000..19ece12c8 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js @@ -0,0 +1,92 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterEach, describe, it, expect } from "vitest"; +import { + createNode, + appendNode, + selectNodes, + removeNode, +} from "../../../../../../../src/utils/dom/index.js"; +import matchesSelectorWithEq from "../../../../../../../src/components/Personalization/dom-actions/dom/matchesSelectorWithEq.js"; + +describe("Personalization::DOM::matchesSelectorWithEq", () => { + afterEach(() => { + selectNodes(".eq").forEach(removeNode); + }); + it("should match when no eq", () => { + const node = createNode("DIV", { + id: "noEq", + class: "eq", + }); + appendNode(document.body, node); + const selector = "#noEq"; + const element = document.getElementById("noEq"); + const result = matchesSelectorWithEq(selector, element); + expect(result).toEqual(true); + }); + it("should match when eq and just one element", () => { + const content = ` +
    +
    first
    + +
    second
    + +
    third
    +
    + `; + const node = createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, node); + const selector = "#abc:eq(0) > div.b:eq(0) > div.c:eq(0)"; + const element = document.getElementById("one"); + const result = matchesSelectorWithEq(selector, element); + expect(result).toEqual(true); + }); + it("should match when eq and multiple elements", () => { + const content = ` +
    +
    first
    + +
    second
    + +
    third
    +
    + `; + const node = createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, node); + const selector = "#abc:eq(0) > div.b:eq(0) > div.c"; + const one = document.getElementById("one"); + const two = document.getElementById("two"); + const three = document.getElementById("three"); + expect(matchesSelectorWithEq(selector, one)).toEqual(true); + expect(matchesSelectorWithEq(selector, two)).toEqual(true); + expect(matchesSelectorWithEq(selector, three)).toEqual(true); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/removeAttribute.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/removeAttribute.spec.js new file mode 100644 index 000000000..d39bd75a1 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/removeAttribute.spec.js @@ -0,0 +1,40 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterEach, describe, it, expect } from "vitest"; +import removeAttribute from "../../../../../../../src/components/Personalization/dom-actions/dom/removeAttribute.js"; +import { + createNode, + removeNode, + selectNodes, +} from "../../../../../../../src/utils/dom/index.js"; +import { + getAttribute, + setAttribute, +} from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; + +describe("Personalization::DOM::removeAttribute", () => { + afterEach(() => { + selectNodes("#fooId").forEach(removeNode); + }); + it("should remove the element's attribute", () => { + const element = createNode("div", { + id: "fooId", + }); + setAttribute(element, "data-foo", "dummyValue"); + const attr = getAttribute(element, "data-foo"); + expect(attr).toEqual("dummyValue"); + removeAttribute(element, "data-foo"); + const removedAttr = getAttribute(element, "data-foo"); + expect(removedAttr).toBeNull(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js new file mode 100644 index 000000000..48afcd9ac --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js @@ -0,0 +1,226 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterEach, describe, it, expect } from "vitest"; +import { + createNode, + appendNode, + selectNodes, + removeNode, +} from "../../../../../../../src/utils/dom/index.js"; +import { + escapeIdentifiersInSelector, + parseSelector, + selectNodesWithEq, +} from "../../../../../../../src/components/Personalization/dom-actions/dom/selectNodesWithEq.js"; + +describe("Personalization::DOM::escapeIdentifiersInSelector", () => { + it("should escape when digits only for ID selector", () => { + const result = escapeIdentifiersInSelector("#123 > #foo div.345"); + expect(result).toEqual("#\\31 23 > #foo div.\\33 45"); + expect(document.querySelector(result)).toEqual(null); + }); + it("should escape when digits only for class selector", () => { + const result = escapeIdentifiersInSelector(".123"); + expect(result).toEqual(".\\31 23"); + expect(document.querySelector(result)).toEqual(null); + }); + it("should escape when hyphen and digits ID selector", () => { + const result = escapeIdentifiersInSelector("#-123"); + expect(result).toEqual("#-\\31 23"); + expect(document.querySelector(result)).toEqual(null); + }); + it("should escape when hyphen and digits class selector", () => { + const result = escapeIdentifiersInSelector(".-123"); + expect(result).toEqual(".-\\31 23"); + expect(document.querySelector(result)).toEqual(null); + }); +}); +describe("Personalization::DOM::parseSelector", () => { + it("should parse selector when no eq", () => { + const result = parseSelector("#test"); + expect(result[0]).toEqual({ + sel: "#test", + }); + }); + it("should parse selector when eq", () => { + const result = parseSelector( + "HTML > BODY > DIV.wrapper:eq(0) > HEADER.header:eq(0) > DIV.pagehead:eq(0) > P:nth-of-type(1)", + ); + expect(result[0]).toEqual({ + sel: "HTML > BODY > DIV.wrapper", + eq: 0, + }); + expect(result[1]).toEqual({ + sel: " > HEADER.header", + eq: 0, + }); + expect(result[2]).toEqual({ + sel: " > DIV.pagehead", + eq: 0, + }); + expect(result[3]).toEqual({ + sel: " > P:nth-of-type(1)", + }); + }); +}); +describe("Personalization::DOM::selectNodesWithEq", () => { + afterEach(() => { + selectNodes(".eq").forEach(removeNode); + }); + it("should select when no eq", () => { + appendNode( + document.body, + createNode("DIV", { + id: "noEq", + class: "eq", + }), + ); + const result = selectNodesWithEq("#noEq"); + expect(result[0].tagName).toEqual("DIV"); + expect(result[0].id).toEqual("noEq"); + }); + it("should select when eq and just one element", () => { + const content = ` +
    +
    first
    + +
    second
    + +
    third
    +
    + `; + appendNode( + document.body, + createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ), + ); + const result = selectNodesWithEq("#abc:eq(0) > div.b:eq(0) > div.c:eq(0)"); + expect(result[0].tagName).toEqual("DIV"); + expect(result[0].textContent).toEqual("first"); + }); + it("should select when eq and multiple elements", () => { + const content = ` +
    +
    first
    + +
    second
    + +
    third
    +
    + `; + appendNode( + document.body, + createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ), + ); + const result = selectNodesWithEq("#abc:eq(0) > div.b:eq(0) > div.c"); + expect(result[0].tagName).toEqual("DIV"); + expect(result[0].textContent).toEqual("first"); + expect(result[1].tagName).toEqual("DIV"); + expect(result[1].textContent).toEqual("second"); + expect(result[2].tagName).toEqual("DIV"); + expect(result[2].textContent).toEqual("third"); + }); + + it("should select when eq and no elements", () => { + appendNode( + document.body, + createNode("DIV", { + id: "abc", + class: "eq", + }), + ); + const result = selectNodesWithEq("#abc:eq(0) > div.foo"); + expect(result.length).toEqual(0); + }); + + it("should select when eq and eq greater than number of nodes", () => { + appendNode( + document.body, + createNode("DIV", { + id: "abc", + class: "eq", + }), + ); + const result = selectNodesWithEq("#abc:eq(1)"); + expect(result.length).toEqual(0); + }); + + it("should show eq vs nth-of-child difference", () => { + const content = ` +
    +

    first

    +
    +
    +

    second

    +
    + `; + appendNode( + document.body, + createNode( + "DIV", + { + id: "abc", + class: "eq", + }, + { + innerHTML: content, + }, + ), + ); + + // NOTE: eq has zero based index, while nth-child index starts at 1 + const resultWithEq = selectNodesWithEq("#abc > div p:eq(0)"); + const resultWitNthChild = selectNodesWithEq("#abc > div :nth-child(1)"); + expect(resultWithEq.length).toEqual(1); + expect(resultWitNthChild.length).toEqual(2); + }); + + it("should show throw errors", () => { + appendNode( + document.body, + createNode("DIV", { + id: "abc", + class: "eq", + }), + ); + + const selectors = [ + "#abc:eq(bad)", + "#abc:eq(eq())", + "#abc:eq(0))", + "#abc.123", + " > ", + ]; + + selectors.forEach((selector) => { + expect(() => selectNodesWithEq(selector)).toThrow(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/setAttribute.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/setAttribute.spec.js new file mode 100644 index 000000000..9659540fd --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/setAttribute.spec.js @@ -0,0 +1,34 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterEach, describe, it, expect } from "vitest"; +import setAttribute from "../../../../../../../src/components/Personalization/dom-actions/dom/setAttribute.js"; +import { + createNode, + removeNode, + selectNodes, +} from "../../../../../../../src/utils/dom/index.js"; +import { getAttribute } from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; + +describe("Personalization::DOM::setAttribute", () => { + afterEach(() => { + selectNodes("#fooId").forEach(removeNode); + }); + it("should set the attribute for the element", () => { + const element = createNode("div", { + id: "fooId", + }); + setAttribute(element, "foo-data", "dummyValue"); + const attr = getAttribute(element, "foo-data"); + expect(attr).toEqual("dummyValue"); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/setStyle.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/setStyle.spec.js new file mode 100644 index 000000000..ab7b416e5 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/setStyle.spec.js @@ -0,0 +1,42 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterEach, describe, it, expect } from "vitest"; +import setStyle from "../../../../../../../src/components/Personalization/dom-actions/dom/setStyle.js"; +import { + selectNodes, + removeNode, + createNode, +} from "../../../../../../../src/utils/dom/index.js"; +import { getAttribute } from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; + +describe("Personalization::DOM::setStyle", () => { + afterEach(() => { + selectNodes("#fooDivId").forEach(removeNode); + }); + it("sets style with priority to the element", () => { + const element = createNode("div", { + id: "fooDivId", + }); + setStyle(element, "padding", "15px", "important"); + const style = getAttribute(element, "style"); + expect(style).toEqual("padding: 15px !important;"); + }); + it("sets style to the element, without priority", () => { + const element = createNode("div", { + id: "fooDivId", + }); + setStyle(element, "padding", "15px"); + const style = getAttribute(element, "style"); + expect(style).toEqual("padding: 15px;"); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/util.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/dom/util.spec.js new file mode 100644 index 000000000..84d7ca73b --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/dom/util.spec.js @@ -0,0 +1,27 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import { addPxIfMissing } from "../../../../../../../src/components/Personalization/dom-actions/dom/util.js"; + +describe("Personalization::DOM::util", () => { + it("appends 'px' string if missing", () => { + const value = "400"; + const result = addPxIfMissing(value); + expect(result).toEqual("400px"); + }); + it("does not append 'px' string if already present", () => { + const value = "400px"; + const result = addPxIfMissing(value); + expect(result).toEqual("400px"); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/images.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/images.spec.js new file mode 100644 index 000000000..62fc11ce8 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/images.spec.js @@ -0,0 +1,47 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + createFragment, + getChildNodes, +} from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import { + isImage, + loadImage, +} from "../../../../../../src/components/Personalization/dom-actions/images.js"; +import { IMG } from "../../../../../../src/constants/tagName.js"; +import { createNode } from "../../../../../../src/utils/dom/index.js"; + +describe("Personalization::helper::images", () => { + beforeEach(() => { + cleanUpDomChanges("fooImage"); + }); + afterEach(() => { + cleanUpDomChanges("fooImage"); + }); + it("should verify if it is an image", () => { + const fragmentHTML = ""; + const fragment = createFragment(fragmentHTML); + const imageNode = getChildNodes(fragment)[0]; + expect(isImage(fragment)).toBe(false); + expect(isImage(imageNode)).toBe(true); + }); + it("should create an image node", () => { + const result = loadImage("http://foo.com"); + const image = createNode(IMG, { + src: "http://foo.com", + }); + expect(result).toEqual(image); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js new file mode 100644 index 000000000..c7d813897 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js @@ -0,0 +1,65 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import initDomActionsModules, { + DOM_ACTION_APPEND_HTML, + DOM_ACTION_CUSTOM_CODE, + DOM_ACTION_INSERT_AFTER, + DOM_ACTION_INSERT_BEFORE, + DOM_ACTION_MOVE, + DOM_ACTION_PREPEND_HTML, + DOM_ACTION_REARRANGE, + DOM_ACTION_REMOVE, + DOM_ACTION_REPLACE_HTML, + DOM_ACTION_RESIZE, + DOM_ACTION_SET_ATTRIBUTE, + DOM_ACTION_SET_HTML, + DOM_ACTION_SET_IMAGE_SOURCE, + DOM_ACTION_SET_STYLE, + DOM_ACTION_SET_TEXT, + DOM_ACTION_COLLECT_INTERACTIONS, +} from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +const buildSet = () => { + const result = new Set(); + + // This is to make IE 11 happy + result.add(DOM_ACTION_SET_HTML); + result.add(DOM_ACTION_CUSTOM_CODE); + result.add(DOM_ACTION_SET_TEXT); + result.add(DOM_ACTION_SET_ATTRIBUTE); + result.add(DOM_ACTION_SET_IMAGE_SOURCE); + result.add(DOM_ACTION_SET_STYLE); + result.add(DOM_ACTION_MOVE); + result.add(DOM_ACTION_RESIZE); + result.add(DOM_ACTION_REARRANGE); + result.add(DOM_ACTION_REMOVE); + result.add(DOM_ACTION_INSERT_AFTER); + result.add(DOM_ACTION_INSERT_BEFORE); + result.add(DOM_ACTION_REPLACE_HTML); + result.add(DOM_ACTION_PREPEND_HTML); + result.add(DOM_ACTION_APPEND_HTML); + result.add(DOM_ACTION_COLLECT_INTERACTIONS); + return result; +}; +const STANDARD_MODULES = buildSet(); +describe("Personalization::turbine::initDomActionsModules", () => { + it("should have all the required modules", () => { + const result = initDomActionsModules(() => {}); + const keys = Object.keys(result); + expect(keys.length).toEqual(STANDARD_MODULES.size); + Object.keys(result).forEach((key) => { + expect(STANDARD_MODULES.has(key)).toEqual(true); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js new file mode 100644 index 000000000..725e3c8f1 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js @@ -0,0 +1,82 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, + selectNodes, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_INSERT_AFTER } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::insertAfter", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("insertAfter"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_INSERT_AFTER, + }); + }); + afterEach(() => { + cleanUpDomChanges("insertAfter"); + }); + it("should insert after personalized content", () => { + const modules = initDomActionsModules(); + const { insertAfter } = modules; + const child = createNode( + "div", + { + id: "a", + class: "ia", + }, + { + innerHTML: "AAA", + }, + ); + const element = createNode( + "div", + { + id: "insertAfter", + }, + {}, + [child], + ); + appendNode(document.body, element); + const settings = { + selector: "#a", + prehidingSelector: "#a", + content: `
    BBB
    CCC
    `, + meta: { + a: 1, + }, + }; + return insertAfter(settings, decorateProposition).then(() => { + const result = selectNodes("div#insertAfter .ia"); + expect(result[0].innerHTML).toEqual("AAA"); + expect(result[1].innerHTML).toEqual("BBB"); + expect(result[2].innerHTML).toEqual("CCC"); + expect(getAttribute(result[1], CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect( + getAttribute(result[1], INTERACT_ID_DATA_ATTRIBUTE), + ).not.toBeNull(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js new file mode 100644 index 000000000..2fbd245dd --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js @@ -0,0 +1,86 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, + selectNodes, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_INSERT_BEFORE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::insertBefore", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("insertBefore"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_INSERT_BEFORE, + }); + }); + afterEach(() => { + cleanUpDomChanges("insertBefore"); + }); + it("should insert before personalized content", () => { + const modules = initDomActionsModules(); + const { insertBefore } = modules; + const child = createNode( + "div", + { + id: "a", + class: "ib", + }, + { + innerHTML: "AAA", + }, + ); + const element = createNode( + "div", + { + id: "insertBefore", + }, + {}, + [child], + ); + appendNode(document.body, element); + const settings = { + selector: "#a", + prehidingSelector: "#a", + content: `
    BBB
    `, + meta: { + a: 1, + }, + }; + return insertBefore(settings, decorateProposition).then(() => { + const [insertedElement, existingElement] = selectNodes( + "div#insertBefore .ib", + ); + expect(insertedElement.innerHTML).toEqual("BBB"); + expect(getAttribute(insertedElement, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect( + getAttribute(insertedElement, INTERACT_ID_DATA_ATTRIBUTE), + ).not.toBeNull(); + expect(existingElement.innerHTML).toEqual("AAA"); + expect( + getAttribute(existingElement, INTERACT_ID_DATA_ATTRIBUTE), + ).toBeNull(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/move.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/move.spec.js new file mode 100644 index 000000000..0bc901b6c --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/move.spec.js @@ -0,0 +1,92 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_MOVE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::move", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("move"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_MOVE, + }); + }); + afterEach(() => { + cleanUpDomChanges("move"); + }); + it("should move personalized content", () => { + const modules = initDomActionsModules(); + const { move } = modules; + const element = createNode("div", { + id: "move", + }); + appendNode(document.body, element); + const settings = { + selector: "#move", + prehidingSelector: "#move", + content: { + left: "100px", + top: "100px", + }, + meta: { + a: 1, + }, + }; + move(settings, decorateProposition).then(() => { + expect(element.style.left).toEqual("100px"); + expect(element.style.top).toEqual("100px"); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); + }); + it("should move personalized content even if coordinates are not properly formatted", () => { + const modules = initDomActionsModules(); + const { move } = modules; + const element = createNode("div", { + id: "move", + }); + appendNode(document.body, element); + const settings = { + selector: "#move", + prehidingSelector: "#move", + content: { + left: "100", + top: "100", + }, + meta: { + a: 1, + }, + }; + move(settings, decorateProposition).then(() => { + expect(element.style.left).toEqual("100px"); + expect(element.style.top).toEqual("100px"); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js new file mode 100644 index 000000000..61df8a2f0 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js @@ -0,0 +1,88 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, + selectNodes, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_PREPEND_HTML } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::prependHtml", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("prependHtml"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_PREPEND_HTML, + }); + }); + afterEach(() => { + cleanUpDomChanges("prependHtml"); + }); + it("should prepend personalized content", () => { + const modules = initDomActionsModules(); + const { prependHtml } = modules; + const content = `
  • 3
  • `; + const element = createNode( + "ul", + { + id: "prependHtml", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, element); + const settings = { + selector: "#prependHtml", + prehidingSelector: "#prependHtml", + content: `
  • 1
  • 2
  • `, + meta: { + a: 1, + }, + }; + return prependHtml(settings, decorateProposition).then(() => { + const result = selectNodes("ul#prependHtml li"); + expect(result.length).toEqual(3); + // first li (prepended) + expect(result[0].innerHTML).toEqual("1"); + expect(getAttribute(result[0], CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect( + getAttribute(result[0], INTERACT_ID_DATA_ATTRIBUTE), + ).not.toBeNull(); + + // second li (prepended) + expect(result[1].innerHTML).toEqual("2"); + expect(getAttribute(result[1], CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect( + getAttribute(result[1], INTERACT_ID_DATA_ATTRIBUTE), + ).not.toBeNull(); + + // third li (pre-existing) + expect(result[2].innerHTML).toEqual("3"); + expect(getAttribute(result[2], CLICK_LABEL_DATA_ATTRIBUTE)).toBeNull(); + expect(getAttribute(result[2], INTERACT_ID_DATA_ATTRIBUTE)).toBeNull(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js new file mode 100644 index 000000000..6fbe07a85 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js @@ -0,0 +1,125 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, + selectNodes, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_REARRANGE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::rearrange", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("rearrange"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_REARRANGE, + }); + }); + afterEach(() => { + cleanUpDomChanges("rearrange"); + }); + it("should rearrange elements when from < to", () => { + const modules = initDomActionsModules(); + const { rearrange } = modules; + const content = ` +
  • 1
  • +
  • 2
  • +
  • 3
  • + `; + const element = createNode( + "ul", + { + id: "rearrange", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, element); + const settings = { + selector: "#rearrange", + prehidingSelector: "#rearrange", + content: { + from: 0, + to: 2, + }, + meta: { + a: 1, + }, + }; + return rearrange(settings, decorateProposition).then(() => { + const result = selectNodes("li"); + expect(result[0].textContent).toEqual("2"); + expect(getAttribute(result[0], CLICK_LABEL_DATA_ATTRIBUTE)).toBeNull(); + expect(getAttribute(result[0], INTERACT_ID_DATA_ATTRIBUTE)).toBeNull(); + expect(result[1].textContent).toEqual("3"); + expect(getAttribute(result[1], CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect( + getAttribute(result[1], INTERACT_ID_DATA_ATTRIBUTE), + ).not.toBeNull(); + expect(result[2].textContent).toEqual("1"); + expect(getAttribute(result[2], CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect( + getAttribute(result[2], INTERACT_ID_DATA_ATTRIBUTE), + ).not.toBeNull(); + }); + }); + it("should rearrange elements when from > to", () => { + const modules = initDomActionsModules(); + const { rearrange } = modules; + const content = ` +
  • 1
  • +
  • 2
  • +
  • 3
  • + `; + const element = createNode( + "ul", + { + id: "rearrange", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, element); + const settings = { + selector: "#rearrange", + prehidingSelector: "#rearrange", + content: { + from: 2, + to: 0, + }, + meta: { + a: 1, + }, + }; + return rearrange(settings, decorateProposition).then(() => { + const result = selectNodes("li"); + expect(result[0].textContent).toEqual("3"); + expect(result[1].textContent).toEqual("1"); + expect(result[2].textContent).toEqual("2"); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/remapCustomCodeOffers.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/remapCustomCodeOffers.spec.js new file mode 100644 index 000000000..fa8ce9189 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/remapCustomCodeOffers.spec.js @@ -0,0 +1,56 @@ +/* +Copyright 2021 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import remapCustomCodeOffers from "../../../../../../src/components/Personalization/dom-actions/remapCustomCodeOffers.js"; + +describe("remapCustomCodeOffers", () => { + it("changes target selector to parent for standard body selector", () => { + expect( + remapCustomCodeOffers({ + type: "customCode", + content: "
    superfluous
    ", + selector: "BODY > *:eq(0)", + }), + ).toEqual({ + type: "customCode", + content: "
    superfluous
    ", + selector: "BODY", + }); + }); + it("does not change selector if non-standard", () => { + expect( + remapCustomCodeOffers({ + type: "customCode", + content: "
    superfluous
    ", + selector: ".whoopie", + }), + ).toEqual({ + type: "customCode", + content: "
    superfluous
    ", + selector: ".whoopie", + }); + }); + it("only handles customCode type", () => { + expect( + remapCustomCodeOffers({ + type: "somethingSpecial", + content: "
    superfluous
    ", + selector: "BODY > *:eq(0)", + }), + ).toEqual({ + type: "somethingSpecial", + content: "
    superfluous
    ", + selector: "BODY > *:eq(0)", + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/remove.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/remove.spec.js new file mode 100644 index 000000000..ccc057869 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/remove.spec.js @@ -0,0 +1,60 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, + selectNodes, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_REMOVE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::remove", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("remove"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_REMOVE, + }); + }); + afterEach(() => { + cleanUpDomChanges("remove"); + }); + it("should remove element", () => { + const modules = initDomActionsModules(); + const { remove } = modules; + const content = `
    `; + const element = createNode( + "div", + { + id: "remove", + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, element); + const settings = { + selector: "#remove", + prehidingSelector: "#remove", + meta: { + a: 1, + }, + }; + return remove(settings, decorateProposition).then(() => { + const result = selectNodes("#child"); + expect(result.length).toEqual(0); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js new file mode 100644 index 000000000..d0ba848da --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js @@ -0,0 +1,81 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, + selectNodes, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_REPLACE_HTML } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::replaceHtml", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("replaceHtml"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_REPLACE_HTML, + }); + }); + afterEach(() => { + cleanUpDomChanges("replaceHtml"); + }); + it("should replace element with personalized content", () => { + const modules = initDomActionsModules(); + const { replaceHtml } = modules; + const child = createNode( + "div", + { + id: "a", + class: "rh", + }, + { + innerHTML: "AAA", + }, + ); + const element = createNode( + "div", + { + id: "replaceHtml", + }, + {}, + [child], + ); + appendNode(document.body, element); + const settings = { + selector: "#a", + prehidingSelector: "#a", + content: `
    BBB
    `, + meta: { + a: 1, + }, + }; + return replaceHtml(settings, decorateProposition).then(() => { + const result = selectNodes("div#replaceHtml .rh"); + expect(result.length).toEqual(1); + expect(result[0].innerHTML).toEqual("BBB"); + expect(getAttribute(result[0], CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect( + getAttribute(result[0], INTERACT_ID_DATA_ATTRIBUTE), + ).not.toBeNull(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/resize.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/resize.spec.js new file mode 100644 index 000000000..80cf9d001 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/resize.spec.js @@ -0,0 +1,92 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_RESIZE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::resize", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("resize"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_RESIZE, + }); + }); + afterEach(() => { + cleanUpDomChanges("resize"); + }); + it("should resize personalized content", () => { + const modules = initDomActionsModules(); + const { resize } = modules; + const element = createNode("div", { + id: "resize", + }); + appendNode(document.body, element); + const settings = { + selector: "#resize", + prehidingSelector: "#resize", + content: { + width: "100px", + height: "100px", + }, + meta: { + a: 1, + }, + }; + return resize(settings, decorateProposition).then(() => { + expect(element.style.width).toEqual("100px"); + expect(element.style.height).toEqual("100px"); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); + }); + it("should resize personalized content even if dimensions are not properly formatted", () => { + const modules = initDomActionsModules(); + const { resize } = modules; + const element = createNode("div", { + id: "resize", + }); + appendNode(document.body, element); + const settings = { + selector: "#resize", + prehidingSelector: "#resize", + content: { + width: "100", + height: "100", + }, + meta: { + a: 1, + }, + }; + return resize(settings, decorateProposition).then(() => { + expect(element.style.width).toEqual("100px"); + expect(element.style.height).toEqual("100px"); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/scripts.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/scripts.spec.js new file mode 100644 index 000000000..1f3578d7d --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/scripts.spec.js @@ -0,0 +1,72 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + getInlineScripts, + getRemoteScriptsUrls, + executeInlineScripts, +} from "../../../../../../src/components/Personalization/dom-actions/scripts.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { createFragment } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import { DIV } from "../../../../../../src/constants/tagName.js"; +import { createNode } from "../../../../../../src/utils/dom/index.js"; + +describe("Personalization::helper::scripts", () => { + beforeEach(() => { + cleanUpDomChanges("fooDiv"); + }); + afterEach(() => { + cleanUpDomChanges("fooDiv"); + }); + it("should get an inline script", () => { + const fragmentHTML = + ""; + const fragment = createFragment(fragmentHTML); + const inlineScripts = getInlineScripts(fragment); + expect(inlineScripts.length).toEqual(1); + }); + it("should return null if inlineScript doesn't have text code", () => { + const fragmentHTML = + ""; + const fragment = createFragment(fragmentHTML); + const inlineScripts = getInlineScripts(fragment); + expect(inlineScripts.length).toEqual(0); + }); + it("should get a remote script", () => { + const fragmentHTML = + "
    "; + const fragment = createFragment(fragmentHTML); + const remoteScripts = getRemoteScriptsUrls(fragment); + expect(remoteScripts.length).toEqual(1); + expect(remoteScripts[0]).toEqual("http://foo.com"); + }); + it("should get a empty array if remote script doesn't have url attr", () => { + const fragmentHTML = + "
    "; + const fragment = createFragment(fragmentHTML); + const remoteScripts = getRemoteScriptsUrls(fragment); + expect(remoteScripts.length).toEqual(0); + }); + it("should execute inline script", () => { + const fragmentHTML = + ""; + const fragment = createFragment(fragmentHTML); + const inlineScripts = getInlineScripts(fragment); + const container = createNode(DIV); + vi.spyOn(container, "appendChild"); + vi.spyOn(container, "removeChild"); + executeInlineScripts(container, inlineScripts); + expect(container.appendChild).toHaveBeenCalledWith(inlineScripts[0]); + expect(container.removeChild).toHaveBeenCalledWith(inlineScripts[0]); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js new file mode 100644 index 000000000..36d66cc06 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js @@ -0,0 +1,63 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_SET_ATTRIBUTE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::setAttribute", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("setAttribute"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_SET_ATTRIBUTE, + }); + }); + afterEach(() => { + cleanUpDomChanges("setAttribute"); + }); + it("should set element attribute", () => { + const modules = initDomActionsModules(); + const { setAttribute } = modules; + const element = createNode("div", { + id: "setAttribute", + }); + appendNode(document.body, element); + const settings = { + selector: "#setAttribute", + prehidingSelector: "#setAttribute", + content: { + "data-test": "bar", + }, + meta: { + a: 1, + }, + }; + return setAttribute(settings, decorateProposition).then(() => { + expect(element.getAttribute("data-test")).toEqual("bar"); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/setHtml.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/setHtml.spec.js new file mode 100644 index 000000000..855361b01 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/setHtml.spec.js @@ -0,0 +1,115 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import pause from "../../../../helpers/pause.js"; +import { DOM_ACTION_SET_HTML } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::setHtml", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("setHtml"); + delete window.someEvar123; + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_SET_HTML, + }); + }); + afterEach(() => { + cleanUpDomChanges("setHtml"); + cleanUpDomChanges("btn"); + delete window.someEvar123; + }); + it("should set personalized content", async () => { + const modules = initDomActionsModules(); + const { setHtml } = modules; + const element = createNode("div", { + id: "setHtml", + }); + element.innerHTML = "foo"; + appendNode(document.body, element); + const settings = { + selector: "#setHtml", + prehidingSelector: "#setHtml", + content: "bar", + meta: { + a: 1, + }, + }; + await setHtml(settings, decorateProposition); + expect(element.innerHTML).toEqual("bar"); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); + it("should execute inline JavaScript", async () => { + const modules = initDomActionsModules(); + const { setHtml } = modules; + const element = createNode("div", { + id: "setHtml", + }); + element.innerHTML = "foo"; + appendNode(document.body, element); + const settings = { + selector: "#setHtml", + prehidingSelector: "#setHtml", + content: + "", + meta: { + a: 1, + }, + }; + await setHtml(settings, decorateProposition); + await pause(501); + expect(window.someEvar123).toEqual(1); + const scriptElements = document.querySelectorAll("#evar123"); + expect(scriptElements.length).toEqual(1); + }); + it("should execute inline JavaScript with event listeners", async () => { + const modules = initDomActionsModules(); + const { setHtml } = modules; + const button = createNode("button", { + id: "btn", + }); + const element = createNode("div", { + id: "setHtml", + }); + element.innerHTML = "foo"; + appendNode(document.body, button); + appendNode(document.body, element); + const settings = { + selector: "#setHtml", + prehidingSelector: "#setHtml", + content: ``, + meta: { + a: 1, + }, + }; + await setHtml(settings, decorateProposition); + button.click(); + expect(window.someEvar123).toEqual(2); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js new file mode 100644 index 000000000..01bb7f649 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js @@ -0,0 +1,63 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_SET_IMAGE_SOURCE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::setImageSource", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("setImageSource"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_SET_IMAGE_SOURCE, + }); + }); + afterEach(() => { + cleanUpDomChanges("setImageSource"); + }); + it("should swap image", () => { + const url = "http://foo.com/a.png"; + const modules = initDomActionsModules(); + const { setImageSource } = modules; + const element = createNode("img", { + id: "setImageSource", + src: url, + }); + appendNode(document.body, element); + const settings = { + selector: "#setImageSource", + prehidingSelector: "#setImageSource", + content: "http://foo.com/b.png", + meta: { + a: 1, + }, + }; + return setImageSource(settings, decorateProposition).then(() => { + expect(element.getAttribute("src")).toEqual("http://foo.com/b.png"); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/setStyles.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/setStyles.spec.js new file mode 100644 index 000000000..d6596f4af --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/setStyles.spec.js @@ -0,0 +1,64 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_SET_STYLE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::setStyle", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("setStyle"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_SET_STYLE, + }); + }); + afterEach(() => { + cleanUpDomChanges("setStyle"); + }); + it("should set styles", () => { + const modules = initDomActionsModules(); + const { setStyle } = modules; + const element = createNode("div", { + id: "setStyle", + }); + appendNode(document.body, element); + const settings = { + selector: "#setStyle", + prehidingSelector: "#setStyle", + content: { + "font-size": "33px", + priority: "important", + }, + meta: { + a: 1, + }, + }; + return setStyle(settings, decorateProposition).then(() => { + expect(element.style.getPropertyValue("font-size")).toEqual("33px"); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/dom-actions/setText.spec.js b/vtest/unit/specs/components/Personalization/dom-actions/setText.spec.js new file mode 100644 index 000000000..5cdf688b6 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/dom-actions/setText.spec.js @@ -0,0 +1,62 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, +} from "../../../../../../src/utils/dom/index.js"; +import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { DOM_ACTION_SET_TEXT } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; + +describe("Personalization::actions::setText", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("setText"); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_SET_TEXT, + }); + }); + afterEach(() => { + cleanUpDomChanges("setText"); + }); + it("should set personalized text", async () => { + const itemData = { + type: "setText", + selector: "#setText", + prehidingSelector: "#setText", + content: "bar", + meta: { + a: 1, + }, + }; + const modules = initDomActionsModules(); + const { setText } = modules; + const element = createNode("div", { + id: "setText", + }); + element.textContent = "foo"; + appendNode(document.body, element); + await setText(itemData, decorateProposition); + expect(element.textContent).toEqual("bar"); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "trackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/flicker/index.spec.js b/vtest/unit/specs/components/Personalization/flicker/index.spec.js new file mode 100644 index 000000000..63172c304 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/flicker/index.spec.js @@ -0,0 +1,46 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + selectNodes, + removeNode, +} from "../../../../../../src/utils/dom/index.js"; +import { + hideElements, + showElements, +} from "../../../../../../src/components/Personalization/flicker/index.js"; + +describe("Personalization::flicker", () => { + beforeEach(() => { + selectNodes("style").forEach(removeNode); + }); + afterEach(() => { + selectNodes("style").forEach(removeNode); + }); + it("should add prehiding style tags", () => { + const prehidingSelector = ".add"; + hideElements(prehidingSelector); + const styles = selectNodes("style"); + expect(styles.length).toEqual(1); + const styleDefinition = styles[0].textContent; + expect(styleDefinition).toEqual( + `${prehidingSelector} { visibility: hidden }`, + ); + }); + it("should remove prehiding style tags", () => { + const prehidingSelector = ".remove"; + hideElements(prehidingSelector); + showElements(prehidingSelector); + const styles = selectNodes("style"); + expect(styles.length).toEqual(0); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/handlers/createDecorateProposition.spec.js b/vtest/unit/specs/components/Personalization/handlers/createDecorateProposition.spec.js new file mode 100644 index 000000000..a3de34c1b --- /dev/null +++ b/vtest/unit/specs/components/Personalization/handlers/createDecorateProposition.spec.js @@ -0,0 +1,250 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + CLICK_LABEL_DATA_ATTRIBUTE, + INTERACT_ID_DATA_ATTRIBUTE, +} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + appendNode, + createNode, +} from "../../../../../../src/utils/dom/index.js"; +import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; +import { + DOM_ACTION_CLICK, + DOM_ACTION_SET_HTML, +} from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; +import { + DECORATED_ELEMENTS_ONLY, + NEVER, +} from "../../../../../../src/constants/propositionInteractionType.js"; +import { + ADOBE_JOURNEY_OPTIMIZER, + ADOBE_TARGET, +} from "../../../../../../src/constants/decisionProvider.js"; + +describe("Personalization::createDecorateProposition", () => { + let decorateProposition; + beforeEach(() => { + cleanUpDomChanges("something"); + }); + afterEach(() => { + cleanUpDomChanges("something"); + }); + it("sets a data-attribute for interact id and label", () => { + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_CLICK, + trackingLabel: "myTrackingLabel", + }); + const element = createNode( + "div", + { + id: "something", + }, + { + innerText: "superfluous", + }, + ); + appendNode(document.body, element); + decorateProposition(element); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "myTrackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); + it("sets a data-attribute for interact id and label when autoCollectPropositionInteractions=decoratedElementsOnly", () => { + decorateProposition = createDecoratePropositionForTest({ + autoCollectPropositionInteractions: { + [ADOBE_JOURNEY_OPTIMIZER]: DECORATED_ELEMENTS_ONLY, + [ADOBE_TARGET]: NEVER, + }, + type: DOM_ACTION_CLICK, + trackingLabel: "myTrackingLabel", + }); + const element = createNode( + "div", + { + id: "something", + }, + { + innerText: "superfluous", + }, + ); + appendNode(document.body, element); + decorateProposition(element); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "myTrackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); + it("does not set a data-attribute for label if no label is specified", () => { + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_CLICK, + trackingLabel: null, + }); + const element = createNode( + "div", + { + id: "something", + }, + { + innerText: "superfluous", + }, + ); + appendNode(document.body, element); + decorateProposition(element); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toBeNull(); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); + it("reuses interact ids when one is already present on an element", () => { + const element = createNode( + "div", + { + id: "something", + }, + { + innerText: "superfluous", + }, + ); + appendNode(document.body, element); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_CLICK, + itemId: "itemId1", + trackingLabel: "myTrackingLabel", + }); + decorateProposition(element); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "myTrackingLabel", + ); + const interactId = getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE); + expect(interactId).not.toBeNull(); + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_CLICK, + itemId: "itemId2", + trackingLabel: "myOtherTrackingLabel", + }); + decorateProposition(element); + + // tracking label remains the same despite another item targeting the same element with trackingLabel set to "myOtherTrackingLabel" (first one wins) + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "myTrackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).toEqual( + interactId, + ); + }); + it("provides a unique interact id for each element", () => { + const element = createNode( + "div", + { + id: "something", + }, + { + innerHTML: + "
  • one
  • two
  • three
  • ", + }, + ); + appendNode(document.body, element); + const interactIds = new Set(); + ["one", "two", "three"].forEach((value, idx) => { + decorateProposition = createDecoratePropositionForTest({ + type: DOM_ACTION_CLICK, + propositionId: `propId_${value}`, + itemId: `itemId_${idx}`, + trackingLabel: `trackingLabel${value}`, + notification: { + id: `notifyId${idx}`, + scope: "web://mywebsite.com", + scopeDetails: { + something: true, + }, + }, + }); + const li = document.querySelector(`#something .${value}`); + decorateProposition(li); + expect(getAttribute(li, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + `trackingLabel${value}`, + ); + const interactId = getAttribute(li, INTERACT_ID_DATA_ATTRIBUTE); + expect(interactId).not.toBeNull(); + interactIds.add(interactId); + }); + expect(interactIds.size).toEqual(3); + }); + it("does not set data-attribute for interact id and label if autoCollectPropositionInteractions does not include the appropriate decisionProvider and dom action is not 'click'", () => { + decorateProposition = createDecoratePropositionForTest({ + autoCollectPropositionInteractions: {}, + type: DOM_ACTION_SET_HTML, + trackingLabel: "myTrackingLabel", + }); + const element = createNode( + "div", + { + id: "something", + }, + { + innerText: "superfluous", + }, + ); + appendNode(document.body, element); + decorateProposition(element); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toBeNull(); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).toBeNull(); + }); + it("does not set data-attribute for interact id and label if autoCollectPropositionInteractions does not include the appropriate decisionProvider and dom action is not 'click'", () => { + decorateProposition = createDecoratePropositionForTest({ + autoCollectPropositionInteractions: { + [ADOBE_JOURNEY_OPTIMIZER]: NEVER, + [ADOBE_TARGET]: NEVER, + }, + type: DOM_ACTION_SET_HTML, + trackingLabel: "myTrackingLabel", + }); + const element = createNode( + "div", + { + id: "something", + }, + { + innerText: "superfluous", + }, + ); + appendNode(document.body, element); + decorateProposition(element); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toBeNull(); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).toBeNull(); + }); + it("sets data-attribute for interact id and label for all 'click' dom actions, regardless of autoCollectPropositionInteractions", () => { + decorateProposition = createDecoratePropositionForTest({ + autoCollectPropositionInteractions: {}, + type: DOM_ACTION_CLICK, + trackingLabel: "myTrackingLabel", + }); + const element = createNode( + "div", + { + id: "something", + }, + { + innerText: "superfluous", + }, + ); + appendNode(document.body, element); + decorateProposition(element); + expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( + "myTrackingLabel", + ); + expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js b/vtest/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js new file mode 100644 index 000000000..27a3baba7 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js @@ -0,0 +1,198 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, afterEach, describe, it, expect } from "vitest"; +import createProcessDomAction from "../../../../../../src/components/Personalization/handlers/createProcessDomAction.js"; +import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; +import { + appendNode, + createNode, +} from "../../../../../../src/utils/dom/index.js"; +import { DOM_ACTION } from "../../../../../../src/constants/schema.js"; +import { + ADOBE_JOURNEY_OPTIMIZER, + ADOBE_TARGET, +} from "../../../../../../src/constants/decisionProvider.js"; +import { + ALWAYS, + NEVER, +} from "../../../../../../src/constants/propositionInteractionType.js"; +import createMockProposition from "../../../../helpers/createMockProposition.js"; + +describe("createProcessDomAction", () => { + let modules; + let logger; + let storeInteractionMeta; + let storeClickMeta; + let processDomAction; + beforeEach(() => { + cleanUpDomChanges("click-element"); + modules = { + typeA: vi.fn(), + typeB: vi.fn(), + }; + logger = { + warn: vi.fn(), + }; + storeInteractionMeta = vi.fn(); + storeClickMeta = vi.fn(); + processDomAction = createProcessDomAction({ + modules, + logger, + storeInteractionMeta, + storeClickMeta, + autoCollectPropositionInteractions: { + [ADOBE_JOURNEY_OPTIMIZER]: ALWAYS, + [ADOBE_TARGET]: NEVER, + }, + }); + }); + afterEach(() => { + cleanUpDomChanges("click-element"); + }); + it("returns an empty object if the item has no data, and logs missing type", () => { + const proposition = createMockProposition({ + schema: DOM_ACTION, + data: undefined, + }); + expect(processDomAction(proposition.getItems()[0])).toEqual({ + setRenderAttempted: false, + includeInNotification: false, + }); + expect(logger.warn).toHaveBeenCalledWith( + "Invalid DOM action data: missing type.", + undefined, + ); + }); + it("returns an empty object if the item has no type, and logs missing type", () => { + const proposition = createMockProposition({ + schema: DOM_ACTION, + data: {}, + }); + expect(processDomAction(proposition.getItems()[0])).toEqual({ + setRenderAttempted: false, + includeInNotification: false, + }); + expect(logger.warn).toHaveBeenCalledWith( + "Invalid DOM action data: missing type.", + {}, + ); + }); + it("returns an empty object if the item has an unknown type, and logs unknown type", () => { + const proposition = createMockProposition({ + schema: DOM_ACTION, + data: { + type: "typeC", + }, + }); + expect(processDomAction(proposition.getItems()[0])).toEqual({ + setRenderAttempted: false, + includeInNotification: false, + }); + expect(logger.warn).toHaveBeenCalledWith( + "Invalid DOM action data: unknown type.", + { + type: "typeC", + }, + ); + }); + it("returns an empty object if the item has no selector for a click type, and logs missing selector", () => { + const proposition = createMockProposition({ + schema: DOM_ACTION, + data: { + type: "click", + }, + }); + expect(processDomAction(proposition.getItems()[0])).toEqual({ + setRenderAttempted: false, + includeInNotification: false, + }); + expect(logger.warn).toHaveBeenCalledWith( + "Invalid DOM action data: missing selector.", + { + type: "click", + }, + ); + }); + it("handles a click type", async () => { + const element = createNode("div", { + id: "click-element", + class: "click-element", + }); + element.innerHTML = "click element"; + appendNode(document.body, element); + const proposition = createMockProposition( + { + id: "itemId", + schema: DOM_ACTION, + data: { + type: "click", + selector: ".click-element", + }, + characteristics: { + trackingLabel: "mytrackinglabel", + }, + }, + { + characteristics: { + scopeType: "page", + trackingLabel: "mytrackinglabel", + }, + }, + ); + const clickAction = processDomAction(proposition.getItems()[0]); + expect(clickAction).toEqual({ + setRenderAttempted: true, + includeInNotification: false, + }); + expect(storeInteractionMeta).not.toHaveBeenCalled(); + expect(storeClickMeta).toHaveBeenCalledWith({ + selector: ".click-element", + meta: { + id: "id", + scope: "scope", + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + scopeType: "page", + trackingLabel: "mytrackinglabel", + }, + }, + trackingLabel: "mytrackinglabel", + scopeType: "proposition", + }, + }); + }); + it("handles a non-click known type", () => { + const proposition = createMockProposition({ + schema: DOM_ACTION, + data: { + type: "typeA", + a: "b", + }, + }); + const result = processDomAction(proposition.getItems()[0]); + expect(result).toEqual({ + render: expect.any(Function), + setRenderAttempted: true, + includeInNotification: true, + }); + expect(modules.typeA).not.toHaveBeenCalled(); + result.render(); + expect(modules.typeA).toHaveBeenCalledWith( + { + type: "typeA", + a: "b", + }, + expect.any(Function), + ); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js b/vtest/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js new file mode 100644 index 000000000..2ffd7f873 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js @@ -0,0 +1,131 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import { + ADOBE_JOURNEY_OPTIMIZER, + ADOBE_TARGET, +} from "../../../../../../src/constants/decisionProvider.js"; +import createProcessHtmlContent from "../../../../../../src/components/Personalization/handlers/createProcessHtmlContent.js"; +import createInteractionStorage from "../../../../../../src/components/Personalization/createInteractionStorage.js"; +import { HTML_CONTENT_ITEM } from "../../../../../../src/constants/schema.js"; +import { + ALWAYS, + NEVER, +} from "../../../../../../src/constants/propositionInteractionType.js"; +import createMockProposition from "../../../../helpers/createMockProposition.js"; + +describe("createProcessHtmlContent", () => { + let modules; + let logger; + let processHtmlContent; + beforeEach(() => { + const { storeInteractionMeta } = createInteractionStorage(); + modules = { + typeA: vi.fn(), + typeB: vi.fn(), + }; + logger = { + warn: vi.fn(), + }; + processHtmlContent = createProcessHtmlContent({ + modules, + logger, + storeInteractionMeta, + autoCollectPropositionInteractions: { + [ADOBE_JOURNEY_OPTIMIZER]: ALWAYS, + [ADOBE_TARGET]: NEVER, + }, + }); + }); + it("returns an empty object if the item has no data", () => { + const proposition = createMockProposition({ + schema: HTML_CONTENT_ITEM, + data: undefined, + }); + expect(processHtmlContent(proposition.getItems()[0])).toEqual({ + setRenderAttempted: false, + includeInNotification: false, + }); + expect(logger.warn).not.toHaveBeenCalled(); + }); + it("returns an empty object if the item has no type", () => { + const proposition = createMockProposition({ + schema: HTML_CONTENT_ITEM, + data: { + selector: ".myselector", + }, + }); + expect(processHtmlContent(proposition.getItems()[0])).toEqual({ + setRenderAttempted: false, + includeInNotification: false, + }); + expect(logger.warn).not.toHaveBeenCalled(); + }); + it("returns an empty object if the item has no selector", () => { + const proposition = createMockProposition({ + schema: HTML_CONTENT_ITEM, + data: { + type: "mytype", + }, + }); + expect(processHtmlContent(proposition.getItems()[0])).toEqual({ + setRenderAttempted: false, + includeInNotification: false, + }); + expect(logger.warn).not.toHaveBeenCalled(); + }); + it("returns an empty object if the item has an unknown type, and logs unknown type", () => { + const proposition = createMockProposition({ + schema: HTML_CONTENT_ITEM, + data: { + type: "typeC", + selector: ".myselector", + content: "mycontent", + }, + }); + expect(processHtmlContent(proposition.getItems()[0])).toEqual({ + setRenderAttempted: false, + includeInNotification: false, + }); + expect(logger.warn).toHaveBeenCalledWith("Invalid HTML content data", { + type: "typeC", + selector: ".myselector", + content: "mycontent", + }); + }); + it("handles a known type", () => { + const proposition = createMockProposition({ + schema: HTML_CONTENT_ITEM, + data: { + type: "typeA", + selector: ".myselector", + content: "mycontent", + }, + }); + const result = processHtmlContent(proposition.getItems()[0]); + expect(result).toEqual({ + render: expect.any(Function), + setRenderAttempted: true, + includeInNotification: true, + }); + expect(modules.typeA).not.toHaveBeenCalled(); + result.render(); + expect(modules.typeA).toHaveBeenCalledWith( + { + type: "typeA", + selector: ".myselector", + content: "mycontent", + }, + expect.any(Function), + ); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/handlers/createProcessInAppMessage.spec.js b/vtest/unit/specs/components/Personalization/handlers/createProcessInAppMessage.spec.js new file mode 100644 index 000000000..7db4b5242 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/handlers/createProcessInAppMessage.spec.js @@ -0,0 +1,139 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createProcessInAppMessage from "../../../../../../src/components/Personalization/handlers/createProcessInAppMessage.js"; + +describe("Personalization::handlers::createProcessInAppMessage", () => { + let item; + let data; + let meta; + let modules; + let logger; + let processInAppMessage; + beforeEach(() => { + item = { + getData() { + return data; + }, + getProposition() { + return { + getNotification: () => meta, + shouldSuppressDisplay: () => false, + }; + }, + }; + modules = { + defaultContent: vi.fn(), + }; + logger = { + warn: vi.fn(), + }; + processInAppMessage = createProcessInAppMessage({ + modules, + logger, + }); + }); + it("returns an empty object if the item has no data, and logs missing type", () => { + data = undefined; + expect(processInAppMessage(item)).toEqual({}); + expect(logger.warn).toHaveBeenCalledWith( + "Invalid in-app message data: undefined.", + undefined, + ); + }); + it("returns an empty object if the item has an unknown type, and logs unknown type", () => { + data = { + type: "wtf", + }; + expect(processInAppMessage(item)).toEqual({}); + expect(logger.warn).toHaveBeenCalledWith( + "Invalid in-app message data: unknown type.", + data, + ); + }); + it("handles a valid in app message type", () => { + meta = { + id: "abc", + scope: "web://mywebsite.com", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "21d", + characteristics: { + eventToken: "yadayaya", + }, + activity: { + id: "foo#bar", + }, + }, + }; + data = { + mobileParameters: { + verticalAlign: "center", + horizontalAlign: "center", + uiTakeover: true, + width: 72, + backdropColor: "#4CA206", + height: 63, + }, + webParameters: {}, + content: "", + contentType: "text/html", + qualifiedDate: 1694731987996, + }; + const result = processInAppMessage(item); + expect(result).toEqual({ + render: expect.any(Function), + setRenderAttempted: true, + includeInNotification: true, + }); + expect(modules.defaultContent).not.toHaveBeenCalled(); + result.render(); + expect(modules.defaultContent).toHaveBeenCalledWith({ + ...data, + meta, + }); + }); + it("handles an invalid in app message type, and logs", () => { + meta = { + id: "abc", + scope: "web://mywebsite.com", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "21d", + characteristics: { + eventToken: "yadayaya", + }, + activity: { + id: "foo#bar", + }, + }, + }; + data = { + mobileParameters: { + verticalAlign: "center", + horizontalAlign: "center", + uiTakeover: true, + width: 72, + backdropColor: "#4CA206", + height: 63, + }, + webParameters: {}, + contentType: "text/html", + qualifiedDate: 1694731987996, + }; + expect(processInAppMessage(item)).toEqual({}); + expect(logger.warn).toHaveBeenCalledWith( + "Invalid in-app message data: missing property 'content'.", + data, + ); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js b/vtest/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js new file mode 100644 index 000000000..4d8cfef5d --- /dev/null +++ b/vtest/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js @@ -0,0 +1,482 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createProcessPropositions from "../../../../../../src/components/Personalization/handlers/createProcessPropositions.js"; +import injectCreateProposition from "../../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; + +describe("createProcessPropositions", () => { + let schemaProcessors; + let logger; + let createProposition; + let processPropositions; + let render; + let always; + let noNotification; + let never; + let noRender; + let redirect; + beforeEach(() => { + render = vi.fn().mockReturnValue(Promise.resolve()); + always = (item) => ({ + render: () => render(item.getData()), + setRenderAttempted: true, + includeInNotification: true, + }); + noNotification = (item) => ({ + render: () => render(item.getData()), + setRenderAttempted: true, + includeInNotification: false, + }); + never = () => ({}); + noRender = () => ({ + setRenderAttempted: true, + includeInNotification: true, + }); + redirect = (item) => ({ + render: () => render(item.getData()), + setRenderAttempted: true, + onlyRenderThis: true, + }); + schemaProcessors = { + always, + noNotification, + never, + noRender, + redirect, + }; + logger = { + info: vi.fn(), + error: vi.fn(), + logOnContentRendering: vi.fn(), + }; + processPropositions = createProcessPropositions({ + schemaProcessors, + logger, + }); + createProposition = injectCreateProposition({ + preprocess: (data) => data, + isPageWideSurface: () => false, + }); + }); + it("handles no propositions", async () => { + const result = processPropositions([]); + expect(result).toEqual({ + render: expect.any(Function), + returnedPropositions: [], + returnedDecisions: [], + }); + await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); + await expect(result.render()).resolves.toStrictEqual([]); + }); + it("processes a proposition with an always item", async () => { + const prop1 = createProposition({ + id: "always1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "always", + data: "mydata", + }, + ], + }); + const result = processPropositions([prop1]); + expect(result).toEqual({ + render: expect.any(Function), + returnedPropositions: [ + { + id: "always1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "always", + data: "mydata", + }, + ], + renderAttempted: true, + }, + ], + returnedDecisions: [], + }); + await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); + expect(render).not.toHaveBeenCalled(); + await expect(result.render()).resolves.toStrictEqual([ + { + id: "always1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + }, + ]); + expect(render).toHaveBeenCalledWith("mydata"); + }); + it("processes a proposition with a noNotification item", async () => { + const prop1 = createProposition({ + id: "noNotification1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "noNotification", + data: "mydata", + }, + ], + }); + const result = processPropositions([prop1]); + expect(result).toEqual({ + render: expect.any(Function), + returnedPropositions: [ + { + id: "noNotification1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "noNotification", + data: "mydata", + }, + ], + renderAttempted: true, + }, + ], + returnedDecisions: [], + }); + await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); + expect(render).not.toHaveBeenCalled(); + await expect(result.render()).resolves.toStrictEqual([]); + expect(render).toHaveBeenCalledWith("mydata"); + }); + it("processes a proposition with a never item", async () => { + const prop1 = createProposition({ + id: "never1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "never", + data: "mydata", + }, + ], + }); + const result = processPropositions([prop1]); + expect(result).toEqual({ + render: expect.any(Function), + returnedPropositions: [ + { + id: "never1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "never", + data: "mydata", + }, + ], + renderAttempted: false, + }, + ], + returnedDecisions: [ + { + id: "never1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "never", + data: "mydata", + }, + ], + }, + ], + }); + await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); + await expect(result.render()).resolves.toStrictEqual([]); + expect(render).not.toHaveBeenCalled(); + }); + it("processes a proposition with a noRender item", async () => { + const prop1 = createProposition({ + id: "noRender1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "noRender", + data: "mydata", + }, + ], + }); + const result = processPropositions([prop1]); + expect(result).toEqual({ + render: expect.any(Function), + returnedPropositions: [ + { + id: "noRender1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "noRender", + data: "mydata", + }, + ], + renderAttempted: true, + }, + ], + returnedDecisions: [], + }); + await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); + await expect(result.render()).resolves.toStrictEqual([ + { + id: "noRender1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + }, + ]); + expect(render).not.toHaveBeenCalled(); + }); + it("processes a proposition with a redirect item", async () => { + const prop1 = createProposition({ + id: "redirect1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "redirect", + data: "mydata", + }, + ], + }); + const result = processPropositions([prop1]); + expect(result).toEqual({ + render: expect.any(Function), + returnedPropositions: [ + { + id: "redirect1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "redirect", + data: "mydata", + }, + ], + renderAttempted: true, + }, + ], + returnedDecisions: [], + }); + await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); + expect(render).not.toHaveBeenCalled(); + await expect(result.render()).resolves.toStrictEqual([]); + expect(render).toHaveBeenCalledWith("mydata"); + }); + it("doesn't render other propositions if one has a redirect", async () => { + const prop1 = createProposition({ + id: "always1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "always", + data: "mydata1", + }, + ], + }); + const prop2 = createProposition({ + id: "redirect2", + scope: "myscope", + scopeDetails: { + a: 2, + }, + items: [ + { + schema: "redirect", + data: "mydata2", + }, + ], + }); + const prop3 = createProposition({ + id: "always3", + scope: "myscope", + scopeDetails: { + a: 3, + }, + items: [ + { + schema: "always", + data: "mydata3", + }, + ], + }); + const result = processPropositions([prop1, prop2, prop3]); + expect(result).toEqual({ + render: expect.any(Function), + returnedPropositions: [ + { + id: "redirect2", + scope: "myscope", + scopeDetails: { + a: 2, + }, + items: [ + { + schema: "redirect", + data: "mydata2", + }, + ], + renderAttempted: true, + }, + { + id: "always1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "always", + data: "mydata1", + }, + ], + renderAttempted: false, + }, + { + id: "always3", + scope: "myscope", + scopeDetails: { + a: 3, + }, + items: [ + { + schema: "always", + data: "mydata3", + }, + ], + renderAttempted: false, + }, + ], + returnedDecisions: [ + { + id: "always1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "always", + data: "mydata1", + }, + ], + }, + { + id: "always3", + scope: "myscope", + scopeDetails: { + a: 3, + }, + items: [ + { + schema: "always", + data: "mydata3", + }, + ], + }, + ], + }); + await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); + expect(render).not.toHaveBeenCalled(); + await expect(result.render()).resolves.toStrictEqual([]); + expect(render).toHaveBeenCalledWith("mydata2"); + }); + it("processes nonRenderPropositions", async () => { + const prop1 = createProposition({ + id: "always1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "always", + data: "mydata", + }, + ], + }); + const result = processPropositions([], [prop1]); + expect(result).toEqual({ + render: expect.any(Function), + returnedPropositions: [ + { + id: "always1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "always", + data: "mydata", + }, + ], + renderAttempted: false, + }, + ], + returnedDecisions: [ + { + id: "always1", + scope: "myscope", + scopeDetails: { + a: 1, + }, + items: [ + { + schema: "always", + data: "mydata", + }, + ], + }, + ], + }); + expect(logger.logOnContentRendering).not.toHaveBeenCalled(); + await expect(result.render()).resolves.toStrictEqual([]); + expect(render).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/handlers/createProcessRedirect.spec.js b/vtest/unit/specs/components/Personalization/handlers/createProcessRedirect.spec.js new file mode 100644 index 000000000..57225aee1 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/handlers/createProcessRedirect.spec.js @@ -0,0 +1,109 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import { defer } from "../../../../../../src/utils/index.js"; +import flushPromiseChains from "../../../../helpers/flushPromiseChains.js"; +import createProcessRedirect from "../../../../../../src/components/Personalization/handlers/createProcessRedirect.js"; + +describe("createProcessRedirect", () => { + let logger; + let executeRedirect; + let collect; + let collectDefer; + let item; + let data; + let proposition; + let meta; + let processRedirect; + beforeEach(() => { + logger = { + warn: vi.fn(), + logOnContentRendering: vi.fn(), + }; + executeRedirect = vi.fn(); + collectDefer = defer(); + collect = vi.fn().mockReturnValue(collectDefer.promise); + proposition = { + getNotification() { + return meta; + }, + }; + item = { + getData() { + return data; + }, + getProposition() { + return proposition; + }, + }; + processRedirect = createProcessRedirect({ + logger, + executeRedirect, + collect, + }); + }); + it("returns an empty object if the item has no data", () => { + data = undefined; + expect(processRedirect(item)).toEqual({}); + expect(logger.warn).toHaveBeenCalledWith( + "Invalid Redirect data", + undefined, + ); + }); + it("returns an empty object if the item has no content", () => { + data = { + a: 1, + }; + expect(processRedirect(item)).toEqual({}); + expect(logger.logOnContentRendering).not.toHaveBeenCalled(); + expect(logger.warn).toHaveBeenCalledWith("Invalid Redirect data", { + a: 1, + }); + }); + it("redirects", async () => { + data = { + content: "mycontent", + }; + meta = "mymetavalue"; + const result = processRedirect(item); + expect(result).toEqual({ + render: expect.any(Function), + setRenderAttempted: true, + onlyRenderThis: true, + }); + expect(collect).not.toHaveBeenCalled(); + expect(executeRedirect).not.toHaveBeenCalled(); + const renderPromise = result.render(); + await flushPromiseChains(); + expect(collect).toHaveBeenCalledWith({ + decisionsMeta: ["mymetavalue"], + documentMayUnload: true, + }); + expect(executeRedirect).not.toHaveBeenCalled(); + collectDefer.resolve(); + await flushPromiseChains(); + expect(logger.logOnContentRendering).toHaveBeenCalledTimes(1); + expect(executeRedirect).toHaveBeenCalledWith("mycontent"); + expect(await renderPromise).toBeUndefined(); + }); + it("doesn't eat the exception", async () => { + data = { + content: "mycontent", + }; + meta = "mymetavalue"; + const result = processRedirect(item); + const renderPromise = result.render(); + collectDefer.reject("myerror"); + expect(logger.logOnContentRendering).not.toHaveBeenCalled(); + await expect(renderPromise).rejects.toThrowError("myerror"); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/handlers/injectCreateProposition.spec.js b/vtest/unit/specs/components/Personalization/handlers/injectCreateProposition.spec.js new file mode 100644 index 000000000..9acb42322 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/handlers/injectCreateProposition.spec.js @@ -0,0 +1,89 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import injectCreateProposition from "../../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; + +describe("injectCreateProposition", () => { + const preprocess = (data) => `preprocessed ${data}`; + const isPageWideSurface = (scope) => scope === "__surface__"; + const createProposition = injectCreateProposition({ + preprocess, + isPageWideSurface, + }); + it("creates a proposition from nothing", () => { + const proposition = createProposition({}); + expect(proposition.getScope()).toBeUndefined(); + expect(proposition.getScopeType()).toEqual("proposition"); + expect(proposition.getItems()).toEqual([]); + expect(proposition.getNotification()).toEqual({ + id: undefined, + scope: undefined, + scopeDetails: undefined, + }); + expect(proposition.toJSON()).toEqual({}); + }); + it("creates a full proposition", () => { + const proposition = createProposition({ + id: "id", + scope: "scope", + scopeDetails: { + characteristics: { + scopeType: "view", + }, + }, + items: [ + { + schema: "schema", + data: "data", + characteristics: { + trackingLabel: "trackingLabel", + }, + }, + ], + }); + expect(proposition.getScope()).toEqual("scope"); + expect(proposition.getScopeType()).toEqual("view"); + const item = proposition.getItems()[0]; + expect(item.getSchema()).toEqual("schema"); + expect(item.getData()).toEqual("preprocessed data"); + expect(item.getProposition()).toEqual(proposition); + expect(item.getTrackingLabel()).toEqual("trackingLabel"); + expect(item.getOriginalItem()).toEqual({ + schema: "schema", + data: "data", + characteristics: { + trackingLabel: "trackingLabel", + }, + }); + expect(proposition.getNotification()).toEqual({ + id: "id", + scope: "scope", + scopeDetails: { + characteristics: { + scopeType: "view", + }, + }, + }); + }); + it("creates a page wide surface proposition", () => { + const proposition = createProposition({ + scope: "__surface__", + }); + expect(proposition.getScopeType()).toEqual("page"); + }); + it("creates a page wide scope proposition", () => { + const proposition = createProposition({ + scope: "__view__", + }); + expect(proposition.getScopeType()).toEqual("page"); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/handlers/processDefaultContent.spec.js b/vtest/unit/specs/components/Personalization/handlers/processDefaultContent.spec.js new file mode 100644 index 000000000..5f506352f --- /dev/null +++ b/vtest/unit/specs/components/Personalization/handlers/processDefaultContent.spec.js @@ -0,0 +1,24 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import processDefaultContent from "../../../../../../src/components/Personalization/handlers/processDefaultContent.js"; + +describe("processDefaultContent", () => { + it("always renders the default content", () => { + const result = processDefaultContent(); + expect(result).toEqual({ + render: expect.any(Function), + setRenderAttempted: true, + includeInNotification: true, + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js b/vtest/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js new file mode 100644 index 000000000..9adb2ccd4 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js @@ -0,0 +1,425 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + buildStyleFromMobileParameters, + createIframe, + createIframeClickHandler, + displayHTMLContentInIframe, +} from "../../../../../../../src/components/Personalization/in-app-message-actions/actions/displayIframeContent.js"; +import cleanUpDomChanges from "../../../../../helpers/cleanUpDomChanges.js"; +import { getNonce } from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; +import { testResetCachedNonce } from "../../../../../../../src/components/Personalization/dom-actions/dom/getNonce.js"; +import { TEXT_HTML } from "../../../../../../../src/constants/contentType.js"; + +describe("DOM Actions on Iframe", () => { + beforeEach(() => { + cleanUpDomChanges("alloy-messaging-container"); + cleanUpDomChanges("alloy-overlay-container"); + cleanUpDomChanges("alloy-content-iframe"); + }); + afterEach(() => { + cleanUpDomChanges("alloy-messaging-container"); + cleanUpDomChanges("alloy-overlay-container"); + cleanUpDomChanges("alloy-content-iframe"); + }); + describe("buildStyleFromParameters", () => { + it("should build the style object correctly", () => { + const mobileParameters = { + verticalAlign: "center", + width: 80, + horizontalAlign: "left", + backdropColor: "rgba(0, 0, 0, 0.7)", + height: 60, + cornerRadius: 10, + horizontalInset: 5, + verticalInset: 10, + uiTakeover: true, + }; + const style = buildStyleFromMobileParameters(mobileParameters); + expect(style.width).toBe("80%"); + expect(style.backgroundColor).toBe("rgba(0, 0, 0, 0.7)"); + expect(style.borderRadius).toBe("10px"); + expect(style.border).toBe("none"); + expect(style.position).toBe("fixed"); + expect(style.overflow).toBe("hidden"); + expect(style.left).toBe("5%"); + expect(style.height).toBe("60vh"); + }); + }); + describe("createIframe function", () => { + it("should create an iframe element with specified properties", () => { + const mockHtmlContent = + '\u003c!doctype html\u003e\\n\u003chtml\u003e\\n\u003chead\u003e\\n \u003ctitle\u003eBumper Sale!\u003c/title\u003e\\n \u003cstyle\u003e\\n body {\\n margin: 0;\\n padding: 0;\\n font-family: Arial, sans-serif;\\n }\\n\\n #announcement {\\n position: fixed;\\n top: 0;\\n left: 0;\\n width: 100%;\\n height: 100%;\\n background-color: rgba(0, 0, 0, 0.8);\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n justify-content: center;\\n color: #fff;\\n }\\n\\n #announcement img {\\n max-width: 80%;\\n height: auto;\\n margin-bottom: 20px;\\n }\\n\\n #cross {\\n position: absolute;\\n top: 10px;\\n right: 10px;\\n cursor: pointer;\\n font-size: 24px;\\n color: #fff;\\n }\\n\\n #buttons {\\n display: flex;\\n justify-content: center;\\n margin-top: 20px;\\n }\\n\\n #buttons a {\\n margin: 0 10px;\\n padding: 10px 20px;\\n background-color: #ff5500;\\n color: #fff;\\n text-decoration: none;\\n border-radius: 4px;\\n font-weight: bold;\\n transition: background-color 0.3s ease;\\n }\\n\\n #buttons a:hover {\\n background-color: #ff3300;\\n }\\n \u003c/style\u003e\\n\u003c/head\u003e\\n\u003cbody\u003e\\n\u003cdiv id\u003d"announcement" class\u003d"fullscreen"\u003e\\n \u003cspan id\u003d"cross" class\u003d"dismiss"\u003e✕\u003c/span\u003e\\n \u003ch2\u003eBlack Friday Sale!\u003c/h2\u003e\\n \u003cimg src\u003d"https://source.unsplash.com/800x600/?technology,gadget" alt\u003d"Technology Image"\u003e\\n \u003cp\u003eDon\u0027t miss out on our incredible discounts and deals at our gadgets!\u003c/p\u003e\\n \u003cdiv id\u003d"buttons"\u003e\\n \u003ca class\u003d"forward" href\u003d"http://localhost:3000/"\u003eShop\u003c/a\u003e\\n \u003ca class\u003d"dismiss"\u003eDismiss\u003c/a\u003e\\n \u003c/div\u003e\\n\u003c/div\u003e\\n\\n\u003c/body\u003e\u003c/html\u003e\\n'; + const mockClickHandler = vi.fn(); + const iframe = createIframe(mockHtmlContent, mockClickHandler); + expect(iframe).toBeDefined(); + expect(iframe instanceof HTMLIFrameElement).toBe(true); + expect(iframe.src).toContain("blob:"); + }); + it("should set 'nonce' attribute on script tag if it exists", async () => { + const mockHtmlContentWithScript = + "\n" + + "\n" + + "\n" + + " Bumper Sale!\n" + + " \n" + + "\n" + + "\n" + + '
    \n' + + ' \n' + + "

    Black Friday Sale!

    \n" + + ' Technology Image\n' + + "

    Don't miss out on our incredible discounts and deals at our gadgets!

    \n" + + '
    \n' + + ' Shop\n' + + ' Dismiss\n' + + "
    \n" + + "
    \n" + + "\n" + + "\n" + + "\n"; + testResetCachedNonce(); + const childElement = document.createElement("div"); + childElement.setAttribute("nonce", "12345"); + const parentElement = document.createElement("div"); + parentElement.appendChild(childElement); + const originalGetNonce = getNonce(parentElement); + const mockClickHandler = vi.fn(); + const iframe = createIframe(mockHtmlContentWithScript, mockClickHandler); + const blob = await fetch(iframe.src).then((r) => r.blob()); + const text = await blob.text(); + const parser = new DOMParser(); + const iframeDocument = parser.parseFromString(text, TEXT_HTML); + const scriptTag = iframeDocument.querySelector("script"); + expect(scriptTag).toBeDefined(); + expect(scriptTag.getAttribute("nonce")).toEqual(originalGetNonce); + }); + }); + describe("createIframeClickHandler", () => { + let container; + let mockedInteract; + let mobileParameters; + beforeEach(() => { + container = document.createElement("div"); + container.setAttribute("id", "alloy-messaging-container"); + document.body.appendChild(container); + mockedInteract = vi.fn(); + mobileParameters = { + verticalAlign: "center", + width: 80, + horizontalAlign: "left", + backdropColor: "rgba(0, 0, 0, 0.7)", + height: 60, + cornerRadius: 10, + horizontalInset: 5, + verticalInset: 10, + }; + }); + it("should remove display message when dismiss is clicked and UI takeover is false", () => { + Object.assign(mobileParameters, { + uiTakeover: false, + }); + const anchor = document.createElement("a"); + anchor.setAttribute("data-uuid", "12345"); + anchor.href = "adbinapp://dismiss?interaction=cancel"; + anchor.innerText = "Cancel"; + const mockEvent = { + target: anchor, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + }; + const iframeClickHandler = createIframeClickHandler(mockedInteract); + iframeClickHandler(mockEvent); + const alloyMessagingContainer = document.getElementById( + "alloy-messaging-container", + ); + expect(mockedInteract).toHaveBeenNthCalledWith(1, "dismiss", { + label: "Cancel", + id: "cancel", + uuid: "12345", + link: "", + }); + expect(alloyMessagingContainer).toBeNull(); + }); + it("should remove display message when dismiss is clicked and Ui takeover is true", () => { + Object.assign(mobileParameters, { + uiTakeover: true, + }); + const overlayContainer = document.createElement("div"); + overlayContainer.setAttribute("id", "alloy-overlay-container"); + document.body.appendChild(overlayContainer); + const anchor = document.createElement("a"); + anchor.setAttribute("data-uuid", "54321"); + anchor.href = "adbinapp://dismiss?interaction=cancel"; + anchor.innerText = "Aloha"; + const mockEvent = { + target: anchor, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + }; + const iframeClickHandler = createIframeClickHandler(mockedInteract); + iframeClickHandler(mockEvent); + const overlayContainerAfterDismissal = document.getElementById( + "alloy-overlay-container", + ); + expect(mockedInteract).toHaveBeenNthCalledWith(1, "dismiss", { + label: "Aloha", + id: "cancel", + uuid: "54321", + link: "", + }); + expect(overlayContainerAfterDismissal).toBeNull(); + }); + it("extracts propositionAction details from anchor tag and sends to interact()", () => { + const mockNavigateToUrl = vi.fn(); + Object.assign(mobileParameters, { + uiTakeover: true, + }); + const anchor = document.createElement("a"); + anchor.setAttribute("data-uuid", "blippi"); + anchor.href = + "adbinapp://dismiss?interaction=accept&link=https%3A%2F%2Fwww.google.com"; + anchor.innerText = "Woof"; + const mockEvent = { + target: anchor, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + }; + const iframeClickHandler = createIframeClickHandler( + mockedInteract, + mockNavigateToUrl, + ); + iframeClickHandler(mockEvent); + const overlayContainerAfterDismissal = document.getElementById( + "alloy-overlay-container", + ); + expect(mockedInteract).toHaveBeenNthCalledWith(1, "dismiss", { + label: "Woof", + id: "accept", + uuid: "blippi", + link: "https://www.google.com", + }); + expect(mockNavigateToUrl).toHaveBeenNthCalledWith( + 1, + "https://www.google.com", + true, + ); + expect(overlayContainerAfterDismissal).toBeNull(); + }); + }); + describe("displayHTMLContentInIframe", () => { + let originalAppendChild; + let originalBodyStyle; + let mockCollect; + let originalCreateIframe; + beforeEach(() => { + mockCollect = vi.fn(); + originalAppendChild = document.body.appendChild; + document.body.appendChild = vi.fn(); + originalBodyStyle = document.body.style; + document.body.style = {}; + originalCreateIframe = window.createIframe; + window.createIframe = vi.fn().mockImplementation(() => { + const element = document.createElement("iframe"); + element.id = "alloy-content-iframe"; + return element; + }); + }); + afterEach(() => { + document.body.appendChild = originalAppendChild; + document.body.style = originalBodyStyle; + document.body.innerHTML = ""; + window.createIframe = originalCreateIframe; + }); + it("should display HTML content in iframe with overlay using mobile parameters", () => { + const settings = { + type: "custom", + webParameters: { + info: "this is a placeholder", + }, + mobileParameters: { + verticalAlign: "center", + dismissAnimation: "bottom", + verticalInset: 20, + backdropOpacity: 0.78, + cornerRadius: 20, + gestures: {}, + horizontalInset: -14, + uiTakeover: true, + horizontalAlign: "center", + width: 72, + displayAnimation: "bottom", + backdropColor: "#4CA206", + height: 63, + }, + content: + '\n\n\n Bumper Sale!\n \n\n\n
    \n \n

    Black Friday Sale!

    \n Technology Image\n

    Don\'t miss out on our incredible discounts and deals at our gadgets!

    \n
    \n Shop\n Dismiss\n
    \n
    \n\n\n\n', + contentType: TEXT_HTML, + schema: "https://ns.adobe.com/personalization/message/in-app", + meta: { + id: "9441e3c4-d673-4c1b-8fb9-d1c0f7826dcc", + scope: "mobileapp://com.adobe.iamTutorialiOS", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "8794bfb9-3254-478a-860e-04f9da59ad82", + characteristics: { + eventToken: + "eyJtZXNzYWdlRXhlY3V0aW9uIjp7Im1lc3NhZ2VFeGVjdXRpb25JRCI6Ik5BIiwibWVzc2FnZUlEIjoiODc5NGJmYjktMzI1NC00NzhhLTg2MGUtMDRmOWRhNTlhZDgyIiwibWVzc2FnZVB1YmxpY2F0aW9uSUQiOiI1ZmYzZmM5Zi0zZTY2LTRiNzktODRmMS1kNzUzMGYwOWQ1ZTIiLCJtZXNzYWdlVHlwZSI6Im1hcmtldGluZyIsImNhbXBhaWduSUQiOiIyOGJlYTAxMS1lNTk2LTQ0MjktYjhmNy1iNWJkNjMwYzY3NDMiLCJjYW1wYWlnblZlcnNpb25JRCI6ImQ5OTQzODJhLTJjZDAtNDkwYS04NGM4LWM0NTk2NmMwYjYwZiIsImNhbXBhaWduQWN0aW9uSUQiOiJiNDU0OThjYi05NmQxLTQxN2EtODFlYi0yZjA5MTU3YWQ4YzYifSwibWVzc2FnZVByb2ZpbGUiOnsibWVzc2FnZVByb2ZpbGVJRCI6IjQ2MTg5Yjg1LWEwYTYtNDc4NS1hNmJlLTg4OWRiZjU3NjhiOSIsImNoYW5uZWwiOnsiX2lkIjoiaHR0cHM6Ly9ucy5hZG9iZS5jb20veGRtL2NoYW5uZWxzL2luQXBwIiwiX3R5cGUiOiJodHRwczovL25zLmFkb2JlLmNvbS94ZG0vY2hhbm5lbC10eXBlcy9pbkFwcCJ9fX0=", + }, + activity: { + id: "28bea011-e596-4429-b8f7-b5bd630c6743#b45498cb-96d1-417a-81eb-2f09157ad8c6", + }, + }, + }, + }; + displayHTMLContentInIframe(settings, mockCollect); + expect(document.body.appendChild).toHaveBeenCalledTimes(2); + }); + it("should display HTML content in iframe with overlay using web parameters", () => { + const settings = { + webParameters: { + "alloy-overlay-container": { + style: { + position: "fixed", + top: "0", + left: "0", + width: "100%", + height: "100%", + background: "transparent", + opacity: 0.5, + backgroundColor: "#FFFFFF", + }, + params: { + enabled: true, + parentElement: "body", + insertionMethod: "appendChild", + }, + }, + "alloy-messaging-container": { + style: { + width: "72%", + backgroundColor: "orange", + borderRadius: "20px", + border: "none", + position: "fixed", + overflow: "hidden", + left: "50%", + transform: "translateX(-50%) translateY(-50%)", + top: "50%", + display: "flex", + alignItems: "center", + justifyContent: "center", + height: "63vh", + }, + params: { + enabled: true, + parentElement: "body", + insertionMethod: "appendChild", + }, + }, + "alloy-content-iframe": { + style: { + width: "100%", + height: "100%", + }, + params: { + enabled: true, + parentElement: "#alloy-messaging-container", + insertionMethod: "appendChild", + }, + }, + }, + content: + '\n\n\n Bumper Sale!\n \n\n\n
    \n \n

    Black Friday Sale!

    \n Technology Image\n

    Don\'t miss out on our incredible discounts and deals at our gadgets!

    \n
    \n Shop\n Dismiss\n
    \n
    \n\n\n\n', + contentType: TEXT_HTML, + schema: "https://ns.adobe.com/personalization/message/in-app", + }; + displayHTMLContentInIframe(settings, mockCollect); + expect(document.body.appendChild).toHaveBeenCalledTimes(2); + }); + it("should display HTML content in iframe with no overlay using web parameters", () => { + const settings = { + webParameters: { + "alloy-overlay-container": { + style: { + position: "fixed", + top: "0", + left: "0", + width: "100%", + height: "100%", + background: "transparent", + opacity: 0.5, + backgroundColor: "#FFFFFF", + }, + params: { + enabled: false, + parentElement: "body", + insertionMethod: "appendChild", + }, + }, + "alloy-messaging-container": { + style: { + width: "72%", + backgroundColor: "orange", + borderRadius: "20px", + border: "none", + position: "fixed", + overflow: "hidden", + left: "50%", + transform: "translateX(-50%) translateY(-50%)", + top: "50%", + display: "flex", + alignItems: "center", + justifyContent: "center", + height: "63vh", + }, + params: { + enabled: true, + parentElement: "body", + insertionMethod: "appendChild", + }, + }, + "alloy-content-iframe": { + style: { + width: "100%", + height: "100%", + }, + params: { + enabled: true, + parentElement: "#alloy-messaging-container", + insertionMethod: "appendChild", + }, + }, + }, + content: + '\n\n\n Bumper Sale!\n \n\n\n
    \n \n

    Black Friday Sale!

    \n Technology Image\n

    Don\'t miss out on our incredible discounts and deals at our gadgets!

    \n
    \n Shop\n Dismiss\n
    \n
    \n\n\n\n', + contentType: TEXT_HTML, + schema: "https://ns.adobe.com/personalization/message/in-app", + }; + displayHTMLContentInIframe(settings, mockCollect); + expect(document.body.appendChild).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.spec.js b/vtest/unit/specs/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.spec.js new file mode 100644 index 000000000..e761018ff --- /dev/null +++ b/vtest/unit/specs/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.spec.js @@ -0,0 +1,25 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import initInAppMessageActionsModules from "../../../../../../src/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.js"; + +describe("Personalization::turbine::initInAppMessageActionsModules", () => { + const noop = () => undefined; + it("should have all the required modules", () => { + const messagingActionsModules = initInAppMessageActionsModules(noop); + expect(Object.keys(messagingActionsModules).length).toEqual(1); + expect(messagingActionsModules.defaultContent).toEqual( + expect.any(Function), + ); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/in-app-message-actions/utils.spec.js b/vtest/unit/specs/components/Personalization/in-app-message-actions/utils.spec.js new file mode 100644 index 000000000..82100afa3 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/in-app-message-actions/utils.spec.js @@ -0,0 +1,34 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, describe, it, expect } from "vitest"; +import { removeElementById } from "../../../../../../src/components/Personalization/in-app-message-actions/utils.js"; + +describe("removeElementById", () => { + beforeEach(() => { + document.body.innerHTML = ` +
    +`; + }); + it("should remove an element when it exists", () => { + const elementId = "test-element"; + const element = document.getElementById(elementId); + expect(element).toBeTruthy(); + removeElementById(elementId); + expect(document.getElementById(elementId)).toBeNull(); + }); + it("should do nothing when the element does not exist", () => { + const nonExistentId = "non-existent-element"; + removeElementById(nonExistentId); + expect(document.getElementById(nonExistentId)).toBeNull(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/responsesMock/eventResponses.js b/vtest/unit/specs/components/Personalization/responsesMock/eventResponses.js new file mode 100644 index 000000000..b27f2a59a --- /dev/null +++ b/vtest/unit/specs/components/Personalization/responsesMock/eventResponses.js @@ -0,0 +1,445 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +export const SCOPES_FOO1_FOO2_DECISIONS = [ + { + id: "TNT:ABC:A", + scope: "Foo1", + scopeDetails: { + blah: "test", + }, + items: [ + { + schema: "https://ns.adove.com/experience/item-article", + data: { + id: "1", + url: "https://foo.com/article/1", + thumbnailUrl: "https://foo.com/image/1?size=400x300", + }, + }, + { + schema: "https://ns.adove.com/experience/item-article", + data: { + id: "2", + url: "https://foo.com/article/2", + thumbnailUrl: "https://foo.com/image/2?size=400x300", + }, + }, + { + schema: "https://ns.adove.com/experience/item-article", + data: { + id: "3", + url: "https://foo.com/article/3", + thumbnailUrl: "https://foo.com/image/3?size=400x300", + }, + }, + ], + }, + { + id: "TNT:ABC:A", + scope: "Foo2", + items: [ + { + schema: "https://ns.adove.com/experience/item", + data: { + id: "A", + content: "Banner A ....", + }, + }, + ], + }, +]; +export const PAGE_WIDE_SCOPE_DECISIONS = [ + { + id: "TNT:activity1:experience1", + scope: "__view__", + scopeDetails: { + blah: "test", + }, + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo", + content: "
    Hola Mundo
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo2", + content: "
    here is a target activity
    ", + }, + }, + { + schema: "https://ns.adove.com/experience/item", + data: { + id: "A", + content: "Banner A ....", + }, + }, + { + schema: "https://ns.adove.com/experience/item", + data: { + id: "B", + content: "Banner B ....", + }, + }, + { + schema: "https://ns.adobe.com/personalization/default-content-item", + }, + ], + }, + { + id: "AJO:campaign1:message1", + scope: "web://alloy.test.com/test/page/1", + scopeDetails: { + decisionProvider: "AJO", + }, + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo", + content: "
    Hola Mundo
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo2", + content: "
    here is a target activity
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/default-content-item", + }, + ], + }, +]; +export const PAGE_WIDE_SCOPE_DECISIONS_WITHOUT_DOM_ACTION_SCHEMA_ITEMS = [ + { + id: "TNT:activity1:experience1", + scope: "__view__", + scopeDetails: { + blah: "test", + }, + items: [ + { + schema: "https://ns.adove.com/experience/item", + data: { + id: "A", + content: "Banner A ....", + }, + }, + { + schema: "https://ns.adove.com/experience/item", + data: { + id: "B", + content: "Banner B ....", + }, + }, + ], + }, +]; +export const PAGE_WIDE_DECISIONS_WITH_DOM_ACTION_SCHEMA_ITEMS = [ + { + id: "TNT:activity1:experience1", + scope: "__view__", + scopeDetails: { + blah: "test", + }, + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo", + content: "
    Hola Mundo
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo2", + content: "
    here is a target activity
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/default-content-item", + }, + ], + }, + { + id: "AJO:campaign1:message1", + scope: "web://alloy.test.com/test/page/1", + scopeDetails: { + decisionProvider: "AJO", + }, + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo", + content: "
    Hola Mundo
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo2", + content: "
    here is a target activity
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/default-content-item", + }, + ], + }, +]; +export const CART_VIEW_DECISIONS = [ + { + id: "TNT:activity4:experience9", + scope: "cart", + scopeDetails: { + blah: "test", + characteristics: { + scopeType: "view", + }, + }, + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo", + content: "
    welcome to cart view
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo2", + content: "
    here is a target activity for cart view
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/default-content-item", + }, + ], + }, +]; +export const PRODUCTS_VIEW_DECISIONS = [ + { + id: "TNT:activity3:experience4", + scope: "products", + scopeDetails: { + blah: "test", + characteristics: { + scopeType: "view", + }, + }, + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo3", + content: "
    welcome to products view
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo4", + content: "
    here is a target activity for products view
    ", + }, + }, + ], + }, +]; +export const REDIRECT_PAGE_WIDE_SCOPE_DECISION = [ + { + id: "TNT:activity15:experience1", + scope: "__view__", + scopeDetails: { + blah: "test", + }, + items: [ + { + schema: "https://ns.adobe.com/personalization/redirect-item", + data: { + type: "redirect", + content: "http://example.com/redirect/offer", + }, + }, + ], + }, +]; +export const MERGED_METRIC_DECISIONS = [ + { + id: "TNT:activity6:experience1", + scope: "testScope", + scopeDetails: { + eventTokens: { + display: "displayToken1", + click: "clickToken1", + }, + }, + items: [ + { + id: "0", + schema: "https://ns.adobe.com/personalization/html-content-item", + data: { + id: "0", + format: "text/html", + content: "testScope content1", + }, + }, + { + schema: "https://ns.adobe.com/personalization/default-content-item", + }, + { + schema: "https://ns.adobe.com/personalization/measurement", + data: { + type: "click", + format: "application/vnd.adobe.target.metric", + }, + }, + ], + }, +]; +export const MIXED_PROPOSITIONS = [ + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn0=", + scope: "home", + scopeDetails: { + decisionProvider: "AJO", + }, + items: [ + { + id: "442358", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + format: "application/vnd.adobe.target.dom-action", + selector: "#root", + }, + }, + ], + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", + scope: "home", + scopeDetails: { + decisionProvider: "AJO", + }, + items: [ + { + id: "442359", + schema: "https://ns.adobe.com/personalization/html-content-item", + data: { + content: "

    Some custom content for the home page

    ", + format: "text/html", + id: "1202448", + }, + }, + ], + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", + scope: "home", + scopeDetails: { + decisionProvider: "AJO", + }, + items: [ + { + id: "442360", + schema: "https://ns.adobe.com/personalization/json-content-item", + data: { + content: "{'field1': 'custom content'}", + format: "text/javascript", + id: "1202449", + }, + }, + ], + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiMTQxNjY0IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", + scope: "home", + scopeDetails: { + decisionProvider: "AJO", + }, + items: [ + { + id: "xcore:personalized-offer:134ce877e13a04ca", + etag: "4", + schema: + "https://ns.adobe.com/experience/offer-management/content-component-html", + data: { + id: "xcore:personalized-offer:134ce877e13a04ca", + format: "text/html", + language: ["en-us"], + content: "

    An html offer from Offer Decisioning

    ", + characteristics: { + testing: "true", + }, + }, + }, + ], + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", + scope: "__view__", + scopeDetails: { + decisionProvider: "AJO", + }, + items: [ + { + id: "442358", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + format: "application/vnd.adobe.target.dom-action", + selector: "#root", + }, + }, + ], + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn2=", + scope: "__view__", + scopeDetails: { + decisionProvider: "AJO", + }, + items: [ + { + id: "442379", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + format: "application/vnd.adobe.target.dom-action", + selector: "#root", + }, + }, + ], + }, +]; diff --git a/vtest/unit/specs/components/Personalization/topLevel/buildAlloy.js b/vtest/unit/specs/components/Personalization/topLevel/buildAlloy.js new file mode 100644 index 000000000..7f2cb38e7 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/topLevel/buildAlloy.js @@ -0,0 +1,254 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi } from "vitest"; +import createEvent from "../../../../../../src/core/createEvent.js"; +import flushPromiseChains from "../../../../helpers/flushPromiseChains.js"; +import createComponent from "../../../../../../src/components/Personalization/createComponent.js"; +import createCollect from "../../../../../../src/utils/createCollect.js"; +import createFetchDataHandler from "../../../../../../src/components/Personalization/createFetchDataHandler.js"; +import collectInteractions from "../../../../../../src/components/Personalization/dom-actions/clicks/collectInteractions.js"; +import isAuthoringModeEnabled from "../../../../../../src/components/Personalization/utils/isAuthoringModeEnabled.js"; +import { + mergeDecisionsMeta, + mergeQuery, +} from "../../../../../../src/utils/event.js"; +import createOnClickHandler from "../../../../../../src/components/Personalization/createOnClickHandler.js"; +import createViewCacheManager from "../../../../../../src/components/Personalization/createViewCacheManager.js"; +import createViewChangeHandler from "../../../../../../src/components/Personalization/createViewChangeHandler.js"; +import createInteractionStorage from "../../../../../../src/components/Personalization/createInteractionStorage.js"; +import createClickStorage from "../../../../../../src/components/Personalization/createClickStorage.js"; +import createApplyPropositions from "../../../../../../src/components/Personalization/createApplyPropositions.js"; +import createSetTargetMigration from "../../../../../../src/components/Personalization/createSetTargetMigration.js"; +import { createCallbackAggregator } from "../../../../../../src/utils/index.js"; +import injectCreateProposition from "../../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; +import createProcessPropositions from "../../../../../../src/components/Personalization/handlers/createProcessPropositions.js"; +import createAsyncArray from "../../../../../../src/components/Personalization/utils/createAsyncArray.js"; +import * as schema from "../../../../../../src/constants/schema.js"; +import createProcessDomAction from "../../../../../../src/components/Personalization/handlers/createProcessDomAction.js"; +import createProcessHtmlContent from "../../../../../../src/components/Personalization/handlers/createProcessHtmlContent.js"; +import createProcessRedirect from "../../../../../../src/components/Personalization/handlers/createProcessRedirect.js"; +import processDefaultContent from "../../../../../../src/components/Personalization/handlers/processDefaultContent.js"; +import { isPageWideSurface } from "../../../../../../src/components/Personalization/utils/surfaceUtils.js"; +import createOnDecisionHandler from "../../../../../../src/components/Personalization/createOnDecisionHandler.js"; +import createNotificationHandler from "../../../../../../src/components/Personalization/createNotificationHandler.js"; +import { + DOM_ACTION_APPEND_HTML, + DOM_ACTION_CLICK, + DOM_ACTION_CUSTOM_CODE, + DOM_ACTION_INSERT_AFTER, + DOM_ACTION_INSERT_BEFORE, + DOM_ACTION_MOVE, + DOM_ACTION_PREPEND_HTML, + DOM_ACTION_REARRANGE, + DOM_ACTION_REMOVE, + DOM_ACTION_REPLACE_HTML, + DOM_ACTION_RESIZE, + DOM_ACTION_SET_ATTRIBUTE, + DOM_ACTION_SET_HTML, + DOM_ACTION_SET_IMAGE_SOURCE, + DOM_ACTION_SET_STYLE, + DOM_ACTION_SET_TEXT, +} from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; +import collectClicks from "../../../../../../src/components/Personalization/dom-actions/clicks/collectClicks.js"; + +const createAction = + (renderFunc) => + ({ selector, content }) => { + if (selector === "#error") { + return Promise.reject(new Error(`Error while rendering ${content}`)); + } + return renderFunc(selector, content); + }; +const buildComponent = ({ + actions, + config, + logger, + eventManager, + getPageLocation, + window, + hideContainers, + showContainers, +}) => { + const initDomActionsModulesMocks = () => { + return { + [DOM_ACTION_SET_HTML]: createAction(actions.setHtml), + [DOM_ACTION_CUSTOM_CODE]: createAction(actions.prependHtml), + [DOM_ACTION_SET_TEXT]: createAction(actions.setText), + [DOM_ACTION_SET_ATTRIBUTE]: createAction(actions.setAttributes), + [DOM_ACTION_SET_IMAGE_SOURCE]: createAction(actions.swapImage), + [DOM_ACTION_SET_STYLE]: createAction(actions.setStyles), + [DOM_ACTION_MOVE]: createAction(actions.setStyles), + [DOM_ACTION_RESIZE]: createAction(actions.setStyles), + [DOM_ACTION_REARRANGE]: createAction(actions.rearrangeChildren), + [DOM_ACTION_REMOVE]: createAction(actions.removeNode), + [DOM_ACTION_INSERT_AFTER]: createAction(actions.insertHtmlAfter), + [DOM_ACTION_INSERT_BEFORE]: createAction(actions.insertHtmlBefore), + [DOM_ACTION_REPLACE_HTML]: createAction(actions.replaceHtml), + [DOM_ACTION_PREPEND_HTML]: createAction(actions.prependHtml), + [DOM_ACTION_APPEND_HTML]: createAction(actions.appendHtml), + [DOM_ACTION_CLICK]: createAction(actions.click), + }; + }; + const { + targetMigrationEnabled, + prehidingStyle, + autoCollectPropositionInteractions, + } = config; + const collect = createCollect({ + eventManager, + mergeDecisionsMeta, + }); + const { storeInteractionMeta, getInteractionMetas } = + createInteractionStorage(); + const { storeClickMeta, getClickSelectors, getClickMetas } = + createClickStorage(); + const preprocess = (action) => action; + const createProposition = injectCreateProposition({ + preprocess, + isPageWideSurface, + }); + const viewCache = createViewCacheManager({ + createProposition, + }); + const modules = initDomActionsModulesMocks(); + const schemaProcessors = { + [schema.DEFAULT_CONTENT_ITEM]: processDefaultContent, + [schema.DOM_ACTION]: createProcessDomAction({ + modules, + logger, + storeInteractionMeta, + storeClickMeta, + autoCollectPropositionInteractions, + }), + [schema.HTML_CONTENT_ITEM]: createProcessHtmlContent({ + modules, + logger, + storeInteractionMeta, + autoCollectPropositionInteractions, + }), + [schema.REDIRECT_ITEM]: createProcessRedirect({ + logger, + executeRedirect: (url) => window.location.replace(url), + collect, + }), + }; + const processPropositions = createProcessPropositions({ + schemaProcessors, + logger, + }); + const renderedPropositions = createAsyncArray(); + const notificationHandler = createNotificationHandler( + collect, + renderedPropositions, + ); + const consent = { + current: vi.fn(), + }; + consent.current.mockReturnValue({ + state: "in", + wasSet: false, + }); + const fetchDataHandler = createFetchDataHandler({ + logger, + prehidingStyle, + showContainers, + hideContainers, + mergeQuery, + processPropositions, + createProposition, + notificationHandler, + consent, + }); + const onClickHandler = createOnClickHandler({ + mergeDecisionsMeta, + collectInteractions, + collectClicks, + getInteractionMetas, + getClickMetas, + getClickSelectors, + }); + const viewChangeHandler = createViewChangeHandler({ + logger, + processPropositions, + viewCache, + }); + const applyPropositions = createApplyPropositions({ + processPropositions, + createProposition, + renderedPropositions, + viewCache, + }); + const setTargetMigration = createSetTargetMigration({ + targetMigrationEnabled, + }); + const onDecisionHandler = createOnDecisionHandler({ + processPropositions, + createProposition, + notificationHandler, + }); + return createComponent({ + getPageLocation, + logger, + fetchDataHandler, + viewChangeHandler, + onClickHandler, + isAuthoringModeEnabled, + mergeQuery, + viewCache, + showContainers, + applyPropositions, + setTargetMigration, + mergeDecisionsMeta, + renderedPropositions, + onDecisionHandler, + }); +}; +export default (mocks) => { + const component = buildComponent(mocks); + const { response } = mocks; + return { + async sendEvent({ + xdm, + data, + renderDecisions, + decisionScopes, + personalization, + }) { + const event = createEvent(); + event.setUserXdm(xdm); + event.setUserData(data); + const callbacks = createCallbackAggregator(); + await component.lifecycle.onBeforeEvent({ + event, + renderDecisions, + decisionScopes, + personalization: personalization || { + sendDisplayEvent: true, + }, + onResponse: callbacks.add, + }); + const results = await callbacks.call({ + response, + }); + const result = Object.assign({}, ...results); + await flushPromiseChains(); + event.finalize(); + return { + event, + result, + }; + }, + applyPropositions(args) { + return component.commands.applyPropositions.run(args); + }, + }; +}; diff --git a/vtest/unit/specs/components/Personalization/topLevel/buildMocks.js b/vtest/unit/specs/components/Personalization/topLevel/buildMocks.js new file mode 100644 index 000000000..35ec4495a --- /dev/null +++ b/vtest/unit/specs/components/Personalization/topLevel/buildMocks.js @@ -0,0 +1,91 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi } from "vitest"; +import createEvent from "../../../../../../src/core/createEvent.js"; +import createResponse from "../../../../helpers/createResponse.js"; +import { + ADOBE_JOURNEY_OPTIMIZER, + ADOBE_TARGET, +} from "../../../../../../src/constants/decisionProvider.js"; +import { + ALWAYS, + NEVER, +} from "../../../../../../src/constants/propositionInteractionType.js"; + +export default (decisions) => { + const response = createResponse({ + content: { + handle: decisions.map((payload) => ({ + type: "personalization:decisions", + payload, + })), + }, + }); + const actions = { + setHtml: vi.fn().mockReturnValue(() => Promise.resolve()), + setText: vi.fn().mockReturnValue(() => Promise.resolve()), + setAttributes: vi.fn().mockReturnValue(() => Promise.resolve()), + swapImage: vi.fn().mockReturnValue(() => Promise.resolve()), + setStyles: vi.fn().mockReturnValue(() => Promise.resolve()), + rearrangeChildren: vi.fn().mockReturnValue(() => Promise.resolve()), + removeNode: vi.fn().mockReturnValue(() => Promise.resolve()), + replaceHtml: vi.fn().mockReturnValue(() => Promise.resolve()), + appendHtml: vi.fn().mockReturnValue(() => Promise.resolve()), + prependHtml: vi.fn().mockReturnValue(() => Promise.resolve()), + insertHtmlAfter: vi.fn().mockReturnValue(() => Promise.resolve()), + insertHtmlBefore: vi.fn().mockReturnValue(() => Promise.resolve()), + click: vi.fn().mockReturnValue(() => Promise.resolve()), + }; + const config = { + targetMigrationEnabled: true, + prehidingStyle: "myprehidingstyle", + autoCollectPropositionInteractions: { + [ADOBE_JOURNEY_OPTIMIZER]: ALWAYS, + [ADOBE_TARGET]: NEVER, + }, + }; + const logger = { + warn: vi.spyOn(console, "warn"), + error: vi.spyOn(console, "error"), + logOnContentRendering: vi.fn(), + logOnContentHiding: vi.fn(), + }; + const sendEvent = vi.fn(); + const eventManager = { + createEvent, + async sendEvent(event) { + event.finalize(); + sendEvent(event.toJSON()); + return Promise.resolve(); + }, + }; + const getPageLocation = () => new URL("http://example.com/home"); + const window = { + location: { + replace: vi.fn(), + }, + }; + const hideContainers = vi.fn(); + const showContainers = vi.fn(); + return { + actions, + config, + logger, + sendEvent, + eventManager, + getPageLocation, + window, + hideContainers, + showContainers, + response, + }; +}; diff --git a/vtest/unit/specs/components/Personalization/topLevel/cartViewDecisions.spec.js b/vtest/unit/specs/components/Personalization/topLevel/cartViewDecisions.spec.js new file mode 100644 index 000000000..c0795fa41 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/topLevel/cartViewDecisions.spec.js @@ -0,0 +1,268 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { CART_VIEW_DECISIONS } from "../responsesMock/eventResponses.js"; +import buildMocks from "./buildMocks.js"; +import buildAlloy from "./buildAlloy.js"; +import resetMocks from "./resetMocks.js"; +import flushPromiseChains from "../../../../helpers/flushPromiseChains.js"; + +describe("PersonalizationComponent", () => { + it("CART_VIEW_DECISIONS", async () => { + const mocks = buildMocks(CART_VIEW_DECISIONS); + const alloy = buildAlloy(mocks); + let { event, result } = await alloy.sendEvent( + { + renderDecisions: true, + }, + CART_VIEW_DECISIONS, + ); + expect(event.toJSON()).toEqual({ + query: { + personalization: { + schemas: [ + "https://ns.adobe.com/personalization/default-content-item", + "https://ns.adobe.com/personalization/html-content-item", + "https://ns.adobe.com/personalization/json-content-item", + "https://ns.adobe.com/personalization/redirect-item", + "https://ns.adobe.com/personalization/ruleset-item", + "https://ns.adobe.com/personalization/message/in-app", + "https://ns.adobe.com/personalization/message/content-card", + "https://ns.adobe.com/personalization/dom-action", + ], + decisionScopes: ["__view__"], + surfaces: ["web://example.com/home"], + }, + }, + }); + expect(result).toEqual({ + propositions: [], + decisions: [], + }); + expect(mocks.sendEvent).not.toHaveBeenCalled(); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + resetMocks(mocks); + ({ event, result } = await alloy.sendEvent( + { + renderDecisions: true, + xdm: { + web: { + webPageDetails: { + viewName: "cart", + }, + }, + }, + }, + [], + )); + expect(event.toJSON()).toEqual({ + xdm: { + _experience: { + decisioning: { + propositions: [ + { + id: "TNT:activity4:experience9", + scope: "cart", + scopeDetails: { + blah: "test", + characteristics: { + scopeType: "view", + }, + }, + }, + ], + propositionEventType: { + display: 1, + }, + }, + }, + web: { + webPageDetails: { + viewName: "cart", + }, + }, + }, + }); + expect(result).toEqual({ + propositions: [ + { + renderAttempted: true, + id: "TNT:activity4:experience9", + scope: "cart", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo", + content: "
    welcome to cart view
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo2", + content: "
    here is a target activity for cart view
    ", + }, + }, + { + schema: + "https://ns.adobe.com/personalization/default-content-item", + }, + ], + scopeDetails: { + blah: "test", + characteristics: { + scopeType: "view", + }, + }, + }, + ], + decisions: [], + }); + expect(mocks.sendEvent).not.toHaveBeenCalled(); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo", + "
    welcome to cart view
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo2", + "
    here is a target activity for cart view
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledTimes(2); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + }); + it("CART_VIEW_DECISIONS 2", async () => { + const mocks = buildMocks(CART_VIEW_DECISIONS); + const alloy = buildAlloy(mocks); + const { event, result } = await alloy.sendEvent( + { + renderDecisions: true, + xdm: { + web: { + webPageDetails: { + viewName: "cart", + }, + }, + }, + }, + CART_VIEW_DECISIONS, + ); + await flushPromiseChains(); + expect(event.toJSON()).toEqual({ + query: { + personalization: { + schemas: [ + "https://ns.adobe.com/personalization/default-content-item", + "https://ns.adobe.com/personalization/html-content-item", + "https://ns.adobe.com/personalization/json-content-item", + "https://ns.adobe.com/personalization/redirect-item", + "https://ns.adobe.com/personalization/ruleset-item", + "https://ns.adobe.com/personalization/message/in-app", + "https://ns.adobe.com/personalization/message/content-card", + "https://ns.adobe.com/personalization/dom-action", + ], + decisionScopes: ["__view__"], + surfaces: ["web://example.com/home"], + }, + }, + xdm: { + web: { + webPageDetails: { + viewName: "cart", + }, + }, + }, + }); + expect(result).toEqual({ + propositions: [ + { + renderAttempted: true, + id: "TNT:activity4:experience9", + scope: "cart", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo", + content: "
    welcome to cart view
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo2", + content: "
    here is a target activity for cart view
    ", + }, + }, + { + schema: + "https://ns.adobe.com/personalization/default-content-item", + }, + ], + scopeDetails: { + blah: "test", + characteristics: { + scopeType: "view", + }, + }, + }, + ], + decisions: [], + }); + expect(mocks.sendEvent).toHaveBeenCalledWith({ + xdm: { + _experience: { + decisioning: { + propositions: [ + { + id: "TNT:activity4:experience9", + scope: "cart", + scopeDetails: { + blah: "test", + characteristics: { + scopeType: "view", + }, + }, + }, + ], + propositionEventType: { + display: 1, + }, + }, + }, + eventType: "decisioning.propositionDisplay", + web: { + webPageDetails: { + viewName: "cart", + }, + }, + }, + }); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo", + "
    welcome to cart view
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo2", + "
    here is a target activity for cart view
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledTimes(2); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/topLevel/mergedMetricDecisions.spec.js b/vtest/unit/specs/components/Personalization/topLevel/mergedMetricDecisions.spec.js new file mode 100644 index 000000000..b1e631360 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/topLevel/mergedMetricDecisions.spec.js @@ -0,0 +1,120 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { MERGED_METRIC_DECISIONS } from "../responsesMock/eventResponses.js"; +import buildMocks from "./buildMocks.js"; +import buildAlloy from "./buildAlloy.js"; + +describe("PersonalizationComponent", () => { + it("MERGED_METRIC_DECISIONS", async () => { + const mocks = buildMocks(MERGED_METRIC_DECISIONS); + const alloy = buildAlloy(mocks); + const { event, result } = await alloy.sendEvent( + { + renderDecisions: true, + }, + MERGED_METRIC_DECISIONS, + ); + expect(event.toJSON()).toEqual({ + query: { + personalization: { + schemas: [ + "https://ns.adobe.com/personalization/default-content-item", + "https://ns.adobe.com/personalization/html-content-item", + "https://ns.adobe.com/personalization/json-content-item", + "https://ns.adobe.com/personalization/redirect-item", + "https://ns.adobe.com/personalization/ruleset-item", + "https://ns.adobe.com/personalization/message/in-app", + "https://ns.adobe.com/personalization/message/content-card", + "https://ns.adobe.com/personalization/dom-action", + ], + decisionScopes: ["__view__"], + surfaces: ["web://example.com/home"], + }, + }, + }); + expect(result).toEqual({ + propositions: [ + { + renderAttempted: false, + id: "TNT:activity6:experience1", + scope: "testScope", + items: [ + { + id: "0", + schema: "https://ns.adobe.com/personalization/html-content-item", + data: { + id: "0", + format: "text/html", + content: "testScope content1", + }, + }, + { + schema: + "https://ns.adobe.com/personalization/default-content-item", + }, + { + schema: "https://ns.adobe.com/personalization/measurement", + data: { + type: "click", + format: "application/vnd.adobe.target.metric", + }, + }, + ], + scopeDetails: { + eventTokens: { + display: "displayToken1", + click: "clickToken1", + }, + }, + }, + ], + decisions: [ + { + id: "TNT:activity6:experience1", + scope: "testScope", + items: [ + { + id: "0", + schema: "https://ns.adobe.com/personalization/html-content-item", + data: { + id: "0", + format: "text/html", + content: "testScope content1", + }, + }, + { + schema: + "https://ns.adobe.com/personalization/default-content-item", + }, + { + schema: "https://ns.adobe.com/personalization/measurement", + data: { + type: "click", + format: "application/vnd.adobe.target.metric", + }, + }, + ], + scopeDetails: { + eventTokens: { + display: "displayToken1", + click: "clickToken1", + }, + }, + }, + ], + }); + expect(mocks.sendEvent).not.toHaveBeenCalled(); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js b/vtest/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js new file mode 100644 index 000000000..755a9ff68 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js @@ -0,0 +1,293 @@ +/* mixedpro +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import { MIXED_PROPOSITIONS } from "../responsesMock/eventResponses.js"; +import buildMocks from "./buildMocks.js"; +import buildAlloy from "./buildAlloy.js"; +import resetMocks from "./resetMocks.js"; +import flushPromiseChains from "../../../../helpers/flushPromiseChains.js"; + +describe("PersonalizationComponent", () => { + it("MIXED_PROPOSITIONS", async () => { + const mocks = buildMocks(MIXED_PROPOSITIONS); + const alloy = buildAlloy(mocks); + const { event, result } = await alloy.sendEvent( + { + renderDecisions: true, + }, + MIXED_PROPOSITIONS, + ); + expect(event.toJSON()).toEqual({ + query: { + personalization: { + schemas: [ + "https://ns.adobe.com/personalization/default-content-item", + "https://ns.adobe.com/personalization/html-content-item", + "https://ns.adobe.com/personalization/json-content-item", + "https://ns.adobe.com/personalization/redirect-item", + "https://ns.adobe.com/personalization/ruleset-item", + "https://ns.adobe.com/personalization/message/in-app", + "https://ns.adobe.com/personalization/message/content-card", + "https://ns.adobe.com/personalization/dom-action", + ], + decisionScopes: ["__view__"], + surfaces: ["web://example.com/home"], + }, + }, + }); + expect(result.propositions).toEqual([ + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", + scope: "__view__", + scopeDetails: { decisionProvider: "AJO" }, + items: [ + { + id: "442358", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + format: "application/vnd.adobe.target.dom-action", + selector: "#root", + }, + }, + ], + renderAttempted: true, + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn2=", + scope: "__view__", + scopeDetails: { decisionProvider: "AJO" }, + items: [ + { + id: "442379", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + format: "application/vnd.adobe.target.dom-action", + selector: "#root", + }, + }, + ], + renderAttempted: true, + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn0=", + scope: "home", + scopeDetails: { decisionProvider: "AJO" }, + items: [ + { + id: "442358", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + format: "application/vnd.adobe.target.dom-action", + selector: "#root", + }, + }, + ], + renderAttempted: false, + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", + scope: "home", + scopeDetails: { decisionProvider: "AJO" }, + items: [ + { + id: "442359", + schema: "https://ns.adobe.com/personalization/html-content-item", + data: { + content: "

    Some custom content for the home page

    ", + format: "text/html", + id: "1202448", + }, + }, + ], + renderAttempted: false, + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", + scope: "home", + scopeDetails: { decisionProvider: "AJO" }, + items: [ + { + id: "442360", + schema: "https://ns.adobe.com/personalization/json-content-item", + data: { + content: "{'field1': 'custom content'}", + format: "text/javascript", + id: "1202449", + }, + }, + ], + renderAttempted: false, + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiMTQxNjY0IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", + scope: "home", + scopeDetails: { decisionProvider: "AJO" }, + items: [ + { + id: "xcore:personalized-offer:134ce877e13a04ca", + etag: "4", + schema: + "https://ns.adobe.com/experience/offer-management/content-component-html", + data: { + id: "xcore:personalized-offer:134ce877e13a04ca", + format: "text/html", + language: ["en-us"], + content: "

    An html offer from Offer Decisioning

    ", + characteristics: { testing: "true" }, + }, + }, + ], + renderAttempted: false, + }, + ]); + + expect(result.decisions).toEqual([ + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn0=", + scope: "home", + scopeDetails: { decisionProvider: "AJO" }, + items: [ + { + id: "442358", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + format: "application/vnd.adobe.target.dom-action", + selector: "#root", + }, + }, + ], + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", + scope: "home", + scopeDetails: { decisionProvider: "AJO" }, + items: [ + { + id: "442359", + schema: "https://ns.adobe.com/personalization/html-content-item", + data: { + content: "

    Some custom content for the home page

    ", + format: "text/html", + id: "1202448", + }, + }, + ], + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", + scope: "home", + scopeDetails: { decisionProvider: "AJO" }, + items: [ + { + id: "442360", + schema: "https://ns.adobe.com/personalization/json-content-item", + data: { + content: "{'field1': 'custom content'}", + format: "text/javascript", + id: "1202449", + }, + }, + ], + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiMTQxNjY0IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", + scope: "home", + scopeDetails: { decisionProvider: "AJO" }, + items: [ + { + id: "xcore:personalized-offer:134ce877e13a04ca", + etag: "4", + schema: + "https://ns.adobe.com/experience/offer-management/content-component-html", + data: { + id: "xcore:personalized-offer:134ce877e13a04ca", + format: "text/html", + language: ["en-us"], + content: "

    An html offer from Offer Decisioning

    ", + characteristics: { testing: "true" }, + }, + }, + ], + }, + ]); + expect(mocks.sendEvent).not.toHaveBeenCalled(); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + resetMocks(mocks); + const applyPropositionsResult = await alloy.applyPropositions({ + propositions: result.propositions, + metadata: { + home: { + selector: "#myhomeselector", + actionType: "appendHtml", + }, + }, + }); + expect(applyPropositionsResult.propositions).toEqual([ + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn0=", + scope: "home", + items: [ + { + id: "442358", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + format: "application/vnd.adobe.target.dom-action", + selector: "#root", + }, + }, + ], + renderAttempted: true, + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", + scope: "home", + items: [ + { + id: "442359", + schema: "https://ns.adobe.com/personalization/html-content-item", + data: { + content: "

    Some custom content for the home page

    ", + format: "text/html", + id: "1202448", + selector: "#myhomeselector", + type: "appendHtml", + }, + }, + ], + renderAttempted: true, + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ]); + expect(applyPropositionsResult.decisions).toBeUndefined(); + await flushPromiseChains(); + expect(mocks.sendEvent).not.toHaveBeenCalled(); + expect(mocks.actions.appendHtml).toHaveBeenNthCalledWith( + 1, + "#myhomeselector", + "

    Some custom content for the home page

    ", + ); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js b/vtest/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js new file mode 100644 index 000000000..5093dbd5f --- /dev/null +++ b/vtest/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js @@ -0,0 +1,179 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { PAGE_WIDE_DECISIONS_WITH_DOM_ACTION_SCHEMA_ITEMS } from "../responsesMock/eventResponses.js"; +import buildMocks from "./buildMocks.js"; +import buildAlloy from "./buildAlloy.js"; +import pause from "../../../../helpers/pause.js"; + +describe("PersonalizationComponent", () => { + it("PAGE_WIDE_DECISIONS_WITH_DOM_ACTION_SCHEMA_ITEMS", async () => { + const mocks = buildMocks(PAGE_WIDE_DECISIONS_WITH_DOM_ACTION_SCHEMA_ITEMS); + const alloy = buildAlloy(mocks); + const { event, result } = await alloy.sendEvent( + { + renderDecisions: true, + }, + PAGE_WIDE_DECISIONS_WITH_DOM_ACTION_SCHEMA_ITEMS, + ); + expect(event.toJSON()).toEqual({ + query: { + personalization: { + schemas: [ + "https://ns.adobe.com/personalization/default-content-item", + "https://ns.adobe.com/personalization/html-content-item", + "https://ns.adobe.com/personalization/json-content-item", + "https://ns.adobe.com/personalization/redirect-item", + "https://ns.adobe.com/personalization/ruleset-item", + "https://ns.adobe.com/personalization/message/in-app", + "https://ns.adobe.com/personalization/message/content-card", + "https://ns.adobe.com/personalization/dom-action", + ], + decisionScopes: ["__view__"], + surfaces: ["web://example.com/home"], + }, + }, + }); + expect(result).toEqual({ + propositions: [ + { + renderAttempted: true, + id: "TNT:activity1:experience1", + scope: "__view__", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo", + content: "
    Hola Mundo
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo2", + content: "
    here is a target activity
    ", + }, + }, + { + schema: + "https://ns.adobe.com/personalization/default-content-item", + }, + ], + scopeDetails: { + blah: "test", + }, + }, + { + renderAttempted: true, + id: "AJO:campaign1:message1", + scope: "web://alloy.test.com/test/page/1", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo", + content: "
    Hola Mundo
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo2", + content: "
    here is a target activity
    ", + }, + }, + { + schema: + "https://ns.adobe.com/personalization/default-content-item", + }, + ], + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + decisions: [], + }); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo", + "
    Hola Mundo
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo2", + "
    here is a target activity
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo", + "
    Hola Mundo
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo2", + "
    here is a target activity
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledTimes(4); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + await pause(100); + expect(mocks.sendEvent).toHaveBeenCalledWith({ + xdm: { + _experience: { + decisioning: { + propositions: [ + { + id: "TNT:activity1:experience1", + scope: "__view__", + scopeDetails: { + blah: "test", + }, + }, + { + id: "AJO:campaign1:message1", + scope: "web://alloy.test.com/test/page/1", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + propositionEventType: { + display: 1, + }, + }, + }, + eventType: "decisioning.propositionDisplay", + }, + }); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo", + "
    Hola Mundo
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo2", + "
    here is a target activity
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo", + "
    Hola Mundo
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo2", + "
    here is a target activity
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledTimes(4); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js b/vtest/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js new file mode 100644 index 000000000..a2623925b --- /dev/null +++ b/vtest/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js @@ -0,0 +1,206 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import { PAGE_WIDE_SCOPE_DECISIONS } from "../responsesMock/eventResponses.js"; +import buildMocks from "./buildMocks.js"; +import buildAlloy from "./buildAlloy.js"; +import pause from "../../../../helpers/pause.js"; + +describe("PersonalizationComponent", () => { + it("PAGE_WIDE_SCOPE_DECISIONS", async () => { + const mocks = buildMocks(PAGE_WIDE_SCOPE_DECISIONS); + const alloy = buildAlloy(mocks); + const { event, result } = await alloy.sendEvent( + { + renderDecisions: true, + }, + PAGE_WIDE_SCOPE_DECISIONS, + ); + expect(event.toJSON()).toEqual({ + query: { + personalization: { + schemas: [ + "https://ns.adobe.com/personalization/default-content-item", + "https://ns.adobe.com/personalization/html-content-item", + "https://ns.adobe.com/personalization/json-content-item", + "https://ns.adobe.com/personalization/redirect-item", + "https://ns.adobe.com/personalization/ruleset-item", + "https://ns.adobe.com/personalization/message/in-app", + "https://ns.adobe.com/personalization/message/content-card", + "https://ns.adobe.com/personalization/dom-action", + ], + decisionScopes: ["__view__"], + surfaces: ["web://example.com/home"], + }, + }, + }); + + expect(result.propositions).toEqual([ + { + id: "TNT:activity1:experience1", + scope: "__view__", + scopeDetails: { blah: "test" }, + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo", + content: "
    Hola Mundo
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo2", + content: "
    here is a target activity
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/default-content-item", + }, + ], + renderAttempted: true, + }, + { + id: "TNT:activity1:experience1", + scope: "__view__", + scopeDetails: { blah: "test" }, + items: [ + { + schema: "https://ns.adove.com/experience/item", + data: { id: "A", content: "Banner A ...." }, + }, + { + schema: "https://ns.adove.com/experience/item", + data: { id: "B", content: "Banner B ...." }, + }, + ], + renderAttempted: false, + }, + { + id: "AJO:campaign1:message1", + scope: "web://alloy.test.com/test/page/1", + scopeDetails: { decisionProvider: "AJO" }, + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo", + content: "
    Hola Mundo
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + selector: "#foo2", + content: "
    here is a target activity
    ", + }, + }, + { + schema: "https://ns.adobe.com/personalization/default-content-item", + }, + ], + renderAttempted: true, + }, + ]); + + expect(result.decisions).toEqual([ + { + id: "TNT:activity1:experience1", + scope: "__view__", + scopeDetails: { blah: "test" }, + items: [ + { + schema: "https://ns.adove.com/experience/item", + data: { id: "A", content: "Banner A ...." }, + }, + { + schema: "https://ns.adove.com/experience/item", + data: { id: "B", content: "Banner B ...." }, + }, + ], + }, + ]); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo", + "
    Hola Mundo
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo2", + "
    here is a target activity
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo", + "
    Hola Mundo
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo2", + "
    here is a target activity
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledTimes(4); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + await pause(100); + expect(mocks.sendEvent).toHaveBeenCalledWith({ + xdm: { + _experience: { + decisioning: { + propositions: [ + { + id: "TNT:activity1:experience1", + scope: "__view__", + scopeDetails: { + blah: "test", + }, + }, + { + id: "AJO:campaign1:message1", + scope: "web://alloy.test.com/test/page/1", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + propositionEventType: { + display: 1, + }, + }, + }, + eventType: "decisioning.propositionDisplay", + }, + }); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo", + "
    Hola Mundo
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo2", + "
    here is a target activity
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo", + "
    Hola Mundo
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledWith( + "#foo2", + "
    here is a target activity
    ", + ); + expect(mocks.actions.setHtml).toHaveBeenCalledTimes(4); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/topLevel/pageWideScopeDecisionsWithoutDomActionSchemaItems.spec.js b/vtest/unit/specs/components/Personalization/topLevel/pageWideScopeDecisionsWithoutDomActionSchemaItems.spec.js new file mode 100644 index 000000000..e4c644292 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/topLevel/pageWideScopeDecisionsWithoutDomActionSchemaItems.spec.js @@ -0,0 +1,104 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { PAGE_WIDE_SCOPE_DECISIONS_WITHOUT_DOM_ACTION_SCHEMA_ITEMS } from "../responsesMock/eventResponses.js"; +import buildMocks from "./buildMocks.js"; +import buildAlloy from "./buildAlloy.js"; + +describe("PersonalizationComponent", () => { + it("PAGE_WIDE_SCOPE_DECISIONS_WITHOUT_DOM_ACTION_SCHEMA_ITEMS", async () => { + const mocks = buildMocks( + PAGE_WIDE_SCOPE_DECISIONS_WITHOUT_DOM_ACTION_SCHEMA_ITEMS, + ); + const alloy = buildAlloy(mocks); + const { event, result } = await alloy.sendEvent( + { + renderDecisions: true, + }, + PAGE_WIDE_SCOPE_DECISIONS_WITHOUT_DOM_ACTION_SCHEMA_ITEMS, + ); + expect(event.toJSON()).toEqual({ + query: { + personalization: { + schemas: [ + "https://ns.adobe.com/personalization/default-content-item", + "https://ns.adobe.com/personalization/html-content-item", + "https://ns.adobe.com/personalization/json-content-item", + "https://ns.adobe.com/personalization/redirect-item", + "https://ns.adobe.com/personalization/ruleset-item", + "https://ns.adobe.com/personalization/message/in-app", + "https://ns.adobe.com/personalization/message/content-card", + "https://ns.adobe.com/personalization/dom-action", + ], + decisionScopes: ["__view__"], + surfaces: ["web://example.com/home"], + }, + }, + }); + expect(result).toEqual({ + propositions: [ + { + renderAttempted: false, + id: "TNT:activity1:experience1", + scope: "__view__", + items: [ + { + schema: "https://ns.adove.com/experience/item", + data: { + id: "A", + content: "Banner A ....", + }, + }, + { + schema: "https://ns.adove.com/experience/item", + data: { + id: "B", + content: "Banner B ....", + }, + }, + ], + scopeDetails: { + blah: "test", + }, + }, + ], + decisions: [ + { + id: "TNT:activity1:experience1", + scope: "__view__", + items: [ + { + schema: "https://ns.adove.com/experience/item", + data: { + id: "A", + content: "Banner A ....", + }, + }, + { + schema: "https://ns.adove.com/experience/item", + data: { + id: "B", + content: "Banner B ....", + }, + }, + ], + scopeDetails: { + blah: "test", + }, + }, + ], + }); + expect(mocks.sendEvent).not.toHaveBeenCalled(); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/topLevel/productsViewDecisions.spec.js b/vtest/unit/specs/components/Personalization/topLevel/productsViewDecisions.spec.js new file mode 100644 index 000000000..666dafa24 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/topLevel/productsViewDecisions.spec.js @@ -0,0 +1,53 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { PRODUCTS_VIEW_DECISIONS } from "../responsesMock/eventResponses.js"; +import buildMocks from "./buildMocks.js"; +import buildAlloy from "./buildAlloy.js"; + +describe("PersonalizationComponent", () => { + it("PRODUCTS_VIEW_DECISIONS", async () => { + const mocks = buildMocks(PRODUCTS_VIEW_DECISIONS); + const alloy = buildAlloy(mocks); + const { event, result } = await alloy.sendEvent( + { + renderDecisions: true, + }, + PRODUCTS_VIEW_DECISIONS, + ); + expect(event.toJSON()).toEqual({ + query: { + personalization: { + schemas: [ + "https://ns.adobe.com/personalization/default-content-item", + "https://ns.adobe.com/personalization/html-content-item", + "https://ns.adobe.com/personalization/json-content-item", + "https://ns.adobe.com/personalization/redirect-item", + "https://ns.adobe.com/personalization/ruleset-item", + "https://ns.adobe.com/personalization/message/in-app", + "https://ns.adobe.com/personalization/message/content-card", + "https://ns.adobe.com/personalization/dom-action", + ], + decisionScopes: ["__view__"], + surfaces: ["web://example.com/home"], + }, + }, + }); + expect(result).toEqual({ + propositions: [], + decisions: [], + }); + expect(mocks.sendEvent).not.toHaveBeenCalled(); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/topLevel/redirectPageWideScopeDecision.spec.js b/vtest/unit/specs/components/Personalization/topLevel/redirectPageWideScopeDecision.spec.js new file mode 100644 index 000000000..8f9bddc91 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/topLevel/redirectPageWideScopeDecision.spec.js @@ -0,0 +1,70 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { REDIRECT_PAGE_WIDE_SCOPE_DECISION } from "../responsesMock/eventResponses.js"; +import buildMocks from "./buildMocks.js"; +import buildAlloy from "./buildAlloy.js"; + +describe("PersonalizationComponent", () => { + it("REDIRECT_PAGE_WIDE_SCOPE_DECISION", async () => { + const mocks = buildMocks(REDIRECT_PAGE_WIDE_SCOPE_DECISION); + const alloy = buildAlloy(mocks); + const { event } = await alloy.sendEvent( + { + renderDecisions: true, + }, + REDIRECT_PAGE_WIDE_SCOPE_DECISION, + ); + expect(event.toJSON()).toEqual({ + query: { + personalization: { + schemas: [ + "https://ns.adobe.com/personalization/default-content-item", + "https://ns.adobe.com/personalization/html-content-item", + "https://ns.adobe.com/personalization/json-content-item", + "https://ns.adobe.com/personalization/redirect-item", + "https://ns.adobe.com/personalization/ruleset-item", + "https://ns.adobe.com/personalization/message/in-app", + "https://ns.adobe.com/personalization/message/content-card", + "https://ns.adobe.com/personalization/dom-action", + ], + decisionScopes: ["__view__"], + surfaces: ["web://example.com/home"], + }, + }, + }); + // No expectation on the result value because the page will redirect soon. + expect(mocks.sendEvent).toHaveBeenCalledWith({ + xdm: { + _experience: { + decisioning: { + propositions: [ + { + id: "TNT:activity15:experience1", + scope: "__view__", + scopeDetails: { + blah: "test", + }, + }, + ], + propositionEventType: { + display: 1, + }, + }, + }, + eventType: "decisioning.propositionDisplay", + }, + }); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/topLevel/resetMocks.js b/vtest/unit/specs/components/Personalization/topLevel/resetMocks.js new file mode 100644 index 000000000..155a64ae0 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/topLevel/resetMocks.js @@ -0,0 +1,29 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +export default ({ + actions, + logger, + sendEvent, + window, + hideContainers, + showContainers, +}) => { + Object.keys(actions).forEach((key) => { + actions[key].mockClear(); + }); + logger.warn.mockClear(); + logger.error.mockClear(); + sendEvent.mockClear(); + window.location.replace.mockClear(); + hideContainers.mockClear(); + showContainers.mockClear(); +}; diff --git a/vtest/unit/specs/components/Personalization/topLevel/scopesFoo1Foo2Decisions.spec.js b/vtest/unit/specs/components/Personalization/topLevel/scopesFoo1Foo2Decisions.spec.js new file mode 100644 index 000000000..44d91ab9c --- /dev/null +++ b/vtest/unit/specs/components/Personalization/topLevel/scopesFoo1Foo2Decisions.spec.js @@ -0,0 +1,149 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { SCOPES_FOO1_FOO2_DECISIONS } from "../responsesMock/eventResponses.js"; +import buildMocks from "./buildMocks.js"; +import buildAlloy from "./buildAlloy.js"; + +describe("PersonalizationComponent", () => { + it("SCOPES_FOO1_FOO2_DECISIONS", async () => { + const mocks = buildMocks(SCOPES_FOO1_FOO2_DECISIONS); + const alloy = buildAlloy(mocks); + const { event, result } = await alloy.sendEvent( + { + renderDecisions: true, + }, + SCOPES_FOO1_FOO2_DECISIONS, + ); + expect(event.toJSON()).toEqual({ + query: { + personalization: { + schemas: [ + "https://ns.adobe.com/personalization/default-content-item", + "https://ns.adobe.com/personalization/html-content-item", + "https://ns.adobe.com/personalization/json-content-item", + "https://ns.adobe.com/personalization/redirect-item", + "https://ns.adobe.com/personalization/ruleset-item", + "https://ns.adobe.com/personalization/message/in-app", + "https://ns.adobe.com/personalization/message/content-card", + "https://ns.adobe.com/personalization/dom-action", + ], + decisionScopes: ["__view__"], + surfaces: ["web://example.com/home"], + }, + }, + }); + expect(result).toEqual({ + propositions: [ + { + renderAttempted: false, + id: "TNT:ABC:A", + scope: "Foo1", + items: [ + { + schema: "https://ns.adove.com/experience/item-article", + data: { + id: "1", + url: "https://foo.com/article/1", + thumbnailUrl: "https://foo.com/image/1?size=400x300", + }, + }, + { + schema: "https://ns.adove.com/experience/item-article", + data: { + id: "2", + url: "https://foo.com/article/2", + thumbnailUrl: "https://foo.com/image/2?size=400x300", + }, + }, + { + schema: "https://ns.adove.com/experience/item-article", + data: { + id: "3", + url: "https://foo.com/article/3", + thumbnailUrl: "https://foo.com/image/3?size=400x300", + }, + }, + ], + scopeDetails: { + blah: "test", + }, + }, + { + renderAttempted: false, + id: "TNT:ABC:A", + scope: "Foo2", + items: [ + { + schema: "https://ns.adove.com/experience/item", + data: { + id: "A", + content: "Banner A ....", + }, + }, + ], + }, + ], + decisions: [ + { + id: "TNT:ABC:A", + scope: "Foo1", + items: [ + { + schema: "https://ns.adove.com/experience/item-article", + data: { + id: "1", + url: "https://foo.com/article/1", + thumbnailUrl: "https://foo.com/image/1?size=400x300", + }, + }, + { + schema: "https://ns.adove.com/experience/item-article", + data: { + id: "2", + url: "https://foo.com/article/2", + thumbnailUrl: "https://foo.com/image/2?size=400x300", + }, + }, + { + schema: "https://ns.adove.com/experience/item-article", + data: { + id: "3", + url: "https://foo.com/article/3", + thumbnailUrl: "https://foo.com/image/3?size=400x300", + }, + }, + ], + scopeDetails: { + blah: "test", + }, + }, + { + id: "TNT:ABC:A", + scope: "Foo2", + items: [ + { + schema: "https://ns.adove.com/experience/item", + data: { + id: "A", + content: "Banner A ....", + }, + }, + ], + }, + ], + }); + expect(mocks.sendEvent).not.toHaveBeenCalled(); + expect(mocks.logger.warn).not.toHaveBeenCalled(); + expect(mocks.logger.error).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/utils/addRenderAttemptedToDecisions.spec.js b/vtest/unit/specs/components/Personalization/utils/addRenderAttemptedToDecisions.spec.js new file mode 100644 index 000000000..a82353289 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/utils/addRenderAttemptedToDecisions.spec.js @@ -0,0 +1,35 @@ +/* +Copyright 2021 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import addRenderAttemptedToDecisions from "../../../../../../src/components/Personalization/utils/addRenderAttemptedToDecisions.js"; + +describe("Personalization::addRenderAttemptedToDecisions", () => { + it("adds a renderAttempted flag", () => { + const decisions = [ + { + blah: "123", + }, + { + blah: "345", + }, + ]; + const result = addRenderAttemptedToDecisions({ + decisions, + renderAttempted: true, + }); + expect(result[0].renderAttempted).toEqual(true); + expect(result[1].renderAttempted).toEqual(true); + expect(decisions[0].renderAttempted).toBeUndefined(); + expect(decisions[1].renderAttempted).toBeUndefined(); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/utils/createAsyncArray.spec.js b/vtest/unit/specs/components/Personalization/utils/createAsyncArray.spec.js new file mode 100644 index 000000000..abfeaaada --- /dev/null +++ b/vtest/unit/specs/components/Personalization/utils/createAsyncArray.spec.js @@ -0,0 +1,57 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import createAsyncArray from "../../../../../../src/components/Personalization/utils/createAsyncArray.js"; +import { defer } from "../../../../../../src/utils/index.js"; +import flushPromiseChains from "../../../../helpers/flushPromiseChains.js"; + +const isPending = (promise) => { + const t = {}; + return Promise.race([promise, t]).then((v) => v === t); +}; + +describe("Personalization::utils::createAsyncArray", () => { + it("should start with an empty array", async () => { + const asyncArray = createAsyncArray(); + expect(await asyncArray.clear()).toEqual([]); + }); + it("should add items to the array, and clear the items", async () => { + const asyncArray = createAsyncArray(); + asyncArray.concat(Promise.resolve(["myitem1"])); + expect(await asyncArray.clear()).toEqual(["myitem1"]); + expect(await asyncArray.clear()).toEqual([]); + }); + it("should add multiple arrays", async () => { + const asyncArray = createAsyncArray(); + asyncArray.concat(Promise.resolve(["myitem1"])); + asyncArray.concat(Promise.resolve(["myitem2"])); + expect(await asyncArray.clear()).toEqual(["myitem1", "myitem2"]); + }); + it("should wait for items while clearing the array", async () => { + const asyncArray = createAsyncArray(); + const deferred = defer(); + asyncArray.concat(deferred.promise); + const clearPromise = asyncArray.clear(); + await flushPromiseChains(); + + expect(await isPending(clearPromise)).toBe(true); + + deferred.resolve(["myitem1"]); + expect(await clearPromise).toEqual(["myitem1"]); + }); + it("should handle rejected promises", async () => { + const asyncArray = createAsyncArray(); + asyncArray.concat(Promise.resolve([1, 2])); + asyncArray.concat(Promise.reject(new Error("Error!"))); + expect(await asyncArray.clear()).toEqual([1, 2]); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/utils/isAuthoringModeEnabled.spec.js b/vtest/unit/specs/components/Personalization/utils/isAuthoringModeEnabled.spec.js new file mode 100644 index 000000000..b6fc8221d --- /dev/null +++ b/vtest/unit/specs/components/Personalization/utils/isAuthoringModeEnabled.spec.js @@ -0,0 +1,33 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isAuthoringModeEnabled from "../../../../../../src/components/Personalization/utils/isAuthoringModeEnabled.js"; + +describe("Personalization::isAuthoringModeEnabled", () => { + it("returns true if authoring mode is enabled", () => { + const doc = { + location: { + href: "http://foo.com?adobe_authoring_enabled=1", + }, + }; + expect(isAuthoringModeEnabled(doc)).toEqual(true); + }); + it("returns false if authoring mode is disabled", () => { + const doc = { + location: { + href: "http://foo.com", + }, + }; + expect(isAuthoringModeEnabled(doc)).toEqual(false); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/utils/metaUtils.spec.js b/vtest/unit/specs/components/Personalization/utils/metaUtils.spec.js new file mode 100644 index 000000000..fe290c67f --- /dev/null +++ b/vtest/unit/specs/components/Personalization/utils/metaUtils.spec.js @@ -0,0 +1,194 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { + cleanMetas, + dedupeMetas, +} from "../../../../../../src/components/Personalization/utils/metaUtils.js"; + +describe("Personalization::metaUtils", () => { + it("cleanMetas", () => { + expect( + cleanMetas([ + { + id: "8f5", + scope: "web://aepdemo.com/", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "39", + characteristics: { + eventToken: "eyJ", + }, + activity: { + id: "12", + }, + }, + trackingLabel: "lbl-buy-now", + scopeType: "page", + }, + { + id: "e9b", + scope: "web://aepdemo.com/", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "03", + characteristics: { + eventToken: "eyJ", + }, + activity: { + id: "4f2", + matchedSurfaces: ["web://aepdemo.com/"], + }, + }, + trackingLabel: "lbl-buy-now", + scopeType: "page", + }, + ]), + ).toEqual([ + { + id: "8f5", + scope: "web://aepdemo.com/", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "39", + characteristics: { + eventToken: "eyJ", + }, + activity: { + id: "12", + }, + }, + }, + { + id: "e9b", + scope: "web://aepdemo.com/", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "03", + characteristics: { + eventToken: "eyJ", + }, + activity: { + id: "4f2", + matchedSurfaces: ["web://aepdemo.com/"], + }, + }, + }, + ]); + }); + it("dedupeMetas", () => { + expect( + dedupeMetas([ + { + id: "8f5", + scope: "web://aepdemo.com/", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "39", + characteristics: { + eventToken: "eyJ", + }, + activity: { + id: "12", + }, + }, + trackingLabel: "lbl-buy-now", + scopeType: "page", + }, + { + id: "e9b", + scope: "web://aepdemo.com/", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "03", + characteristics: { + eventToken: "eyJ", + }, + activity: { + id: "4f2", + matchedSurfaces: ["web://aepdemo.com/"], + }, + }, + trackingLabel: "lbl-buy-now", + scopeType: "page", + }, + { + id: "8f5", + scope: "web://aepdemo.com/", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "39", + characteristics: { + eventToken: "eyJ", + }, + activity: { + id: "12", + }, + }, + trackingLabel: "lbl-buy-now", + scopeType: "page", + }, + { + id: "e9b", + scope: "web://aepdemo.com/", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "03", + characteristics: { + eventToken: "eyJ", + }, + activity: { + id: "4f2", + matchedSurfaces: ["web://aepdemo.com/"], + }, + }, + trackingLabel: "lbl-buy-now", + scopeType: "page", + }, + ]), + ).toEqual([ + { + id: "8f5", + scope: "web://aepdemo.com/", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "39", + characteristics: { + eventToken: "eyJ", + }, + activity: { + id: "12", + }, + }, + trackingLabel: "lbl-buy-now", + scopeType: "page", + }, + { + id: "e9b", + scope: "web://aepdemo.com/", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "03", + characteristics: { + eventToken: "eyJ", + }, + activity: { + id: "4f2", + matchedSurfaces: ["web://aepdemo.com/"], + }, + }, + trackingLabel: "lbl-buy-now", + scopeType: "page", + }, + ]); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/utils/surfaceUtils.spec.js b/vtest/unit/specs/components/Personalization/utils/surfaceUtils.spec.js new file mode 100644 index 000000000..91415dc49 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/utils/surfaceUtils.spec.js @@ -0,0 +1,248 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import { + buildPageSurface, + isPageWideSurface, + normalizeSurfaces, +} from "../../../../../../src/components/Personalization/utils/surfaceUtils.js"; + +let pageLocation; +let logger; +const getPageLocation = () => pageLocation; +describe("Personalization::surfaceUtils", () => { + beforeEach(() => { + logger = { + error: vi.fn(), + warn: vi.fn(), + }; + pageLocation = { + host: "domain.com", + pathname: "/products/test/", + }; + }); + it("builds page-wide surface from location", () => { + expect(buildPageSurface(getPageLocation)).toEqual( + "web://domain.com/products/test", + ); + pageLocation = { + host: "DOMain.com", + pathname: undefined, + }; + expect(buildPageSurface(getPageLocation)).toEqual("web://domain.com/"); + pageLocation = { + host: "domain.com:8080", + pathname: "/", + }; + expect(buildPageSurface(getPageLocation)).toEqual("web://domain.com:8080/"); + pageLocation = { + host: "domain.com", + pathname: "/a", + }; + expect(buildPageSurface(getPageLocation)).toEqual("web://domain.com/a"); + pageLocation = { + host: "domain.com", + pathname: "/a/", + }; + expect(buildPageSurface(getPageLocation)).toEqual("web://domain.com/a"); + }); + it("checks for a page-wide surface", () => { + expect(isPageWideSurface("__view__")).toBe(false); + expect(isPageWideSurface("name")).toBe(false); + expect(isPageWideSurface("web://domain.com")).toBe(true); + expect(isPageWideSurface("web://domain.com/path/page.html")).toBe(true); + expect(isPageWideSurface("web://domain.com#fragment")).toBe(false); + expect(isPageWideSurface("webapp://domain.com")).toBe(false); + expect(isPageWideSurface("webapp://domain.com#view")).toBe(false); + }); + it("expands fragment surfaces", () => { + let result = normalizeSurfaces([], getPageLocation, logger); + expect(result).toEqual([]); + result = normalizeSurfaces( + ["web://custom.surface.com", "#fragment1", "test"], + getPageLocation, + logger, + ); + expect(result).toEqual([ + "web://custom.surface.com/", + "web://domain.com/products/test#fragment1", + ]); + expect(logger.warn).toHaveBeenNthCalledWith(1, "Invalid surface: test"); + }); + it("validates & normalizes surface type", () => { + const result = normalizeSurfaces( + [ + "test://domain1.com/test", + "web://domain2.com/test", + "we b://domain3.com/test", + "webapp://domain4.com/test", + "webAPP://domain5.com/test", + "://domain5.com/test", + "web:///domain6.com/test", + "mobileapp://domain7.com/test", + ], + getPageLocation, + logger, + ); + expect(result).toEqual([ + "web://domain2.com/test", + "webapp://domain4.com/test", + "webapp://domain5.com/test", + ]); + expect(logger.warn).toHaveBeenCalledTimes(5); + }); + it("validates & normalizes surface authority", () => { + let result = normalizeSurfaces( + [ + "web://foo.com", + "web://www.example.com", + "web://✪df.ws", + "web://userid:password@example.com:8080", + "web://userid@example.com", + "web://userid@example.com:8080", + "web://userid:password@example.com", + "web://142.42.1.1", + "web://142.42.1.1:8080", + "web://➡.ws", + "web://⌘.ws", + "web://☺.damowmow.com", + "web://مثال.إختبار", + "web://例子.测试", + "web://उदाहरण.परीक्षा", + "web://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", + "web://1337.net", + "web://a.b-c.de", + "web://223.255.255.254", + "web://[::1]", + "web://[ff11:af21:::1]:3000", + ], + getPageLocation, + logger, + ); + expect(result).toEqual([ + "web://foo.com/", + "web://www.example.com/", + "web://✪df.ws/", + "web://userid:password@example.com:8080/", + "web://userid@example.com/", + "web://userid@example.com:8080/", + "web://userid:password@example.com/", + "web://142.42.1.1/", + "web://142.42.1.1:8080/", + "web://➡.ws/", + "web://⌘.ws/", + "web://☺.damowmow.com/", + "web://مثال.إختبار/", + "web://例子.测试/", + "web://उदाहरण.परीक्षा/", + "web://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com/", + "web://1337.net/", + "web://a.b-c.de/", + "web://223.255.255.254/", + "web://[::1]/", + "web://[ff11:af21:::1]:3000/", + ]); + expect(logger.warn).not.toHaveBeenCalled(); + result = normalizeSurfaces( + [ + "web://foo?.com", + "web://d f.ws", + "web://userid@example.com:8a080", + "web://userid@examp&le.com", + "web:///page", + "web://[::1)", + "web://[ff11:af21:12zx::1]:3000", + ], + getPageLocation, + logger, + ); + expect(result).toEqual([]); + expect(logger.warn).toHaveBeenCalledTimes(7); + }); + it("validates & normalizes surface path", () => { + let result = normalizeSurfaces( + [ + "web://domain1.com", + "web://domain2.com///", + "web://domain3.com/PROD.1/a", + "web://domain4.com/~prod-1/bb/c/", + "web://domain5.com/例子/☺", + "web://domain6.com/a/%D0%B6%D0%BE%D1%80%D0%B0%D1%82%D0%B5%D1%81%D1%82", + ], + getPageLocation, + logger, + ); + expect(result).toEqual([ + "web://domain1.com/", + "web://domain2.com/", + "web://domain3.com/PROD.1/a", + "web://domain4.com/~prod-1/bb/c", + "web://domain5.com/例子/☺", + "web://domain6.com/a/%D0%B6%D0%BE%D1%80%D0%B0%D1%82%D0%B5%D1%81%D1%82", + ]); + expect(logger.warn).not.toHaveBeenCalled(); + result = normalizeSurfaces( + [ + "web://domain1.com/pr od", + "web://domain2.com/+/1", + "web://domain3.com/$PROD.1/a", + "web://domain4.com/~prod-1/bb/c/?query=aa", + "web://domain5.com/例子/test*/1", + "web://domain6.com/a/%D0%B6%D0%ZX", + "web://domain7.com/a/%D0%B6%D0%%AF", + ], + getPageLocation, + logger, + ); + expect(result).toEqual([]); + expect(logger.warn).toHaveBeenCalledTimes(7); + }); + it("validates surface fragment", () => { + let result = normalizeSurfaces( + [ + "web://domain1.com#/home", + "web://domain2.com#home", + "web://domain3.com#PROD.1", + "web://domain4.com#~prod-1/bb/c/", + "web://domain5.com#例子/☺", + "web://domain6.com#my-%D0%B6%D0%BE%D1%80%D0%B0%D1%82%D0%B5%D1%81%D1%82", + ], + getPageLocation, + logger, + ); + expect(result).toEqual([ + "web://domain1.com/#/home", + "web://domain2.com/#home", + "web://domain3.com/#PROD.1", + "web://domain4.com/#~prod-1/bb/c/", + "web://domain5.com/#例子/☺", + "web://domain6.com/#my-%D0%B6%D0%BE%D1%80%D0%B0%D1%82%D0%B5%D1%81%D1%82", + ]); + expect(logger.warn).not.toHaveBeenCalled(); + result = normalizeSurfaces( + [ + "web://domain1.com/#pr od", + "web://domain2.com/#+/1", + "web://domain3.com/#$PROD.1/a", + "web://domain4.com/#~prod-1/bb/c/?query=aa", + "web://domain5.com/#例子/test*!/1", + "web://domain6.com/#a/%D0%B6%D0%ZX", + "web://domain7.com/#a/%D0%B6%D0%%AF", + ], + getPageLocation, + logger, + ); + expect(result).toEqual([]); + expect(logger.warn).toHaveBeenCalledTimes(7); + }); +}); diff --git a/vtest/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js b/vtest/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js new file mode 100644 index 000000000..504968b80 --- /dev/null +++ b/vtest/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js @@ -0,0 +1,250 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import { DOM_ACTION } from "@adobe/alloy/libEs6/constants/schema.js"; +import validateApplyPropositionsOptions, { + EMPTY_PROPOSITIONS, +} from "../../../../../src/components/Personalization/validateApplyPropositionsOptions.js"; + +const PROPOSITIONS = [ + { + id: "abc", + scope: "web://aepdemo.com/", + scopeDetails: { + decisionProvider: "AJO", + }, + items: [ + { + id: "abc", + schema: DOM_ACTION, + data: { + type: "setHtml", + content: "woof", + selector: "#paragraph-text-1", + }, + }, + ], + }, + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn0=", + scope: "__view__", + items: [ + { + id: "442358", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "click", + format: "application/vnd.adobe.target.dom-action", + selector: + "#root > DIV:nth-of-type(1) > UL:nth-of-type(1) > LI:nth-of-type(4) > A:nth-of-type(1)", + }, + }, + ], + scopeDetails: { + decisionProvider: "TGT", + activity: { + id: "442358", + }, + characteristics: { + eventToken: "+8J+6aZYrFL4hGqSwULhUQ==", + analyticsToken: "442358:0:0|32767", + }, + }, + }, +]; +const METADATA = { + scope1: { + selector: "#home-item1", + actionType: "setHtml", + }, +}; +describe("Personalization::validateApplyPropositionsOptions", () => { + let loggerSpy; + let logger; + const resetLogger = () => { + loggerSpy = vi.fn(); + logger = { + warn: loggerSpy, + }; + }; + beforeEach(() => { + resetLogger(); + }); + it("it should log a warning when no options are present", () => { + const result = validateApplyPropositionsOptions({ + logger, + }); + expect(loggerSpy).toHaveBeenCalled(); + expect(result).toEqual(EMPTY_PROPOSITIONS); + }); + it("it should log a warning when propositions array is missing from options", () => { + const result = validateApplyPropositionsOptions({ + logger, + options: {}, + }); + expect(loggerSpy).toHaveBeenCalled(); + expect(loggerSpy.mock.calls[0][1].message).toEqual( + "'propositions' is a required option", + ); + expect(result).toEqual(EMPTY_PROPOSITIONS); + }); + it("it should log a warning when propositions is empty array", () => { + const result = validateApplyPropositionsOptions({ + logger, + options: { + propositions: [], + }, + }); + expect(loggerSpy).toHaveBeenCalled(); + expect(loggerSpy.mock.calls[0][1].message).toEqual( + "'propositions': Expected a non-empty array, but got [].", + ); + expect(result).toEqual(EMPTY_PROPOSITIONS); + }); + it("it should log a warning when propositions are missing required values", () => { + const scopeDetails = { + decisionProvider: "AJO", + }; + const tests = [ + { + propositions: [{}], + errorMessage: + "'propositions[0].id' is a required option\n" + + "'propositions[0].scope' is a required option\n" + + "'propositions[0].scopeDetails' is a required option\n" + + "'propositions[0].items' is a required option", + }, + { + propositions: [ + { + id: "abc", + }, + ], + errorMessage: + "'propositions[0].scope' is a required option\n" + + "'propositions[0].scopeDetails' is a required option\n" + + "'propositions[0].items' is a required option", + }, + { + propositions: [ + { + id: "abc", + scope: "web://aepdemo.com/", + }, + ], + errorMessage: + "'propositions[0].scopeDetails' is a required option\n" + + "'propositions[0].items' is a required option", + }, + { + propositions: [ + { + id: "abc", + scope: "web://aepdemo.com/", + scopeDetails, + }, + ], + errorMessage: "'propositions[0].items' is a required option", + }, + { + propositions: [ + { + id: "abc", + scope: "web://aepdemo.com/", + scopeDetails, + items: [], + }, + ], + errorMessage: + "'propositions[0].items': Expected a non-empty array, but got [].", + }, + { + propositions: [ + { + id: "abc", + scope: "web://aepdemo.com/", + scopeDetails, + items: [{}], + }, + ], + errorMessage: + "'propositions[0].items[0].id' is a required option\n" + + "'propositions[0].items[0].schema' is a required option", + }, + { + propositions: [ + { + id: "abc", + scope: "web://aepdemo.com/", + scopeDetails, + items: [ + { + id: "abc", + }, + ], + }, + ], + errorMessage: "'propositions[0].items[0].schema' is a required option", + }, + ]; + for (let i = 0; i < tests.length; i += 1) { + const { propositions, errorMessage } = tests[i]; + resetLogger(); + const result = validateApplyPropositionsOptions({ + logger, + options: { + propositions, + }, + }); + expect(loggerSpy).toHaveBeenCalled(); + expect(loggerSpy.mock.calls[0][1].message).toEqual(errorMessage); + expect(result).toEqual(EMPTY_PROPOSITIONS); + } + }); + it("it should not log a warning when extra options are present", () => { + const result = validateApplyPropositionsOptions({ + logger, + options: { + bad: "bad", + propositions: PROPOSITIONS, + }, + }); + expect(loggerSpy).not.toHaveBeenCalled(); + expect(result).not.toEqual(EMPTY_PROPOSITIONS); + }); + it("it should log a warning when metadata is not an object", () => { + const result = validateApplyPropositionsOptions({ + logger, + options: { + propositions: PROPOSITIONS, + metadata: [], + }, + }); + expect(loggerSpy).toHaveBeenCalled(); + expect(loggerSpy.mock.calls[0][1].message).toEqual( + "'metadata': Expected an object, but got [].", + ); + expect(result).toEqual(EMPTY_PROPOSITIONS); + }); + it("it should not log a warning when propositions and metadata are present", () => { + const result = validateApplyPropositionsOptions({ + logger, + options: { + propositions: PROPOSITIONS, + metadata: METADATA, + }, + }); + expect(loggerSpy).not.toHaveBeenCalled(); + expect(result).not.toEqual(EMPTY_PROPOSITIONS); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.spec.js b/vtest/unit/specs/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.spec.js new file mode 100644 index 000000000..18fcae2e4 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.spec.js @@ -0,0 +1,64 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import inAppMessageConsequenceAdapter from "../../../../../../src/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.js"; +import { TEXT_HTML } from "../../../../../../src/constants/contentType.js"; + +describe("RulesEngine:inAppMessageConsequenceAdapter", () => { + it("handles cjmiam", () => { + expect( + inAppMessageConsequenceAdapter( + "72042c7c-4e34-44f6-af95-1072ae117424", + "cjmiam", + { + mobileParameters: { + verticalAlign: "center", + dismissAnimation: "top", + verticalInset: 0, + backdropOpacity: 0.2, + cornerRadius: 15, + horizontalInset: 0, + uiTakeover: true, + horizontalAlign: "center", + width: 80, + displayAnimation: "top", + backdropColor: "#000000", + height: 60, + }, + html: "", + }, + ), + ).toEqual({ + schema: "https://ns.adobe.com/personalization/message/in-app", + data: { + mobileParameters: { + verticalAlign: "center", + dismissAnimation: "top", + verticalInset: 0, + backdropOpacity: 0.2, + cornerRadius: 15, + horizontalInset: 0, + uiTakeover: true, + horizontalAlign: "center", + width: 80, + displayAnimation: "top", + backdropColor: "#000000", + height: 60, + }, + webParameters: expect.any(Object), + content: "", + contentType: TEXT_HTML, + }, + id: "72042c7c-4e34-44f6-af95-1072ae117424", + }); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.spec.js b/vtest/unit/specs/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.spec.js new file mode 100644 index 000000000..9630470ab --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.spec.js @@ -0,0 +1,71 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import schemaTypeConsequenceAdapter from "../../../../../../src/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.js"; +import { TEXT_HTML } from "../../../../../../src/constants/contentType.js"; + +describe("RulesEngine:schemaTypeConsequenceAdapter", () => { + it("handles schema", () => { + expect( + schemaTypeConsequenceAdapter( + "72042c7c-4e34-44f6-af95-1072ae117424", + "schema", + { + schema: "https://ns.adobe.com/personalization/message/in-app", + data: { + mobileParameters: { + verticalAlign: "center", + dismissAnimation: "top", + verticalInset: 0, + backdropOpacity: 0.2, + cornerRadius: 15, + horizontalInset: 0, + uiTakeover: true, + horizontalAlign: "center", + width: 80, + displayAnimation: "top", + backdropColor: "#000000", + height: 60, + }, + webParameters: expect.any(Object), + content: "", + contentType: TEXT_HTML, + }, + id: "72042c7c-4e34-44f6-af95-1072ae117424", + }, + ), + ).toEqual({ + schema: "https://ns.adobe.com/personalization/message/in-app", + data: { + mobileParameters: { + verticalAlign: "center", + dismissAnimation: "top", + verticalInset: 0, + backdropOpacity: 0.2, + cornerRadius: 15, + horizontalInset: 0, + uiTakeover: true, + horizontalAlign: "center", + width: 80, + displayAnimation: "top", + backdropColor: "#000000", + height: 60, + }, + webParameters: expect.any(Object), + content: "", + contentType: TEXT_HTML, + }, + id: "72042c7c-4e34-44f6-af95-1072ae117424", + }); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/contextTestUtils.js b/vtest/unit/specs/components/RulesEngine/contextTestUtils.js new file mode 100644 index 000000000..f4b81973b --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/contextTestUtils.js @@ -0,0 +1,154 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, expect } from "vitest"; +import createContextProvider from "../../../../../src/components/RulesEngine/createContextProvider.js"; +import createOnResponseHandler from "../../../../../src/components/RulesEngine/createOnResponseHandler.js"; +import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; +import createDecisionProvider from "../../../../../src/components/RulesEngine/createDecisionProvider.js"; +import { injectGetBrowser } from "../../../../../src/utils/index.js"; + +export const proposition = { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scopeDetails: { + activity: { + id: "abc#xyz", + }, + }, + items: [ + { + schema: "https://ns.adobe.com/personalization/mock-action", + data: { + hello: "kitty", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + ], + scope: "web://mywebsite.com", +}; +export const mockWindow = ({ + title = "My awesome website", + referrer = "https://www.google.com/search?q=adobe+journey+optimizer&oq=adobe+journey+optimizer", + url = "https://pro.mywebsite.org:8080/about?m=1&t=5&name=jimmy#home", + width = 100, + height = 100, + scrollX = 0, + scrollY = 10, + userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36", +}) => ({ + title, + referrer, + url, + width, + height, + scrollX, + scrollY, + navigator: { + userAgent, + }, +}); +export const payloadWithCondition = (condition) => { + return { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scopeDetails: { + activity: { + id: "abc#xyz", + }, + }, + items: [ + { + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + schema: "https://ns.adobe.com/personalization/ruleset-item", + data: { + version: 1, + rules: [ + { + condition: { + definition: { + conditions: [condition], + logic: "and", + }, + type: "group", + }, + consequences: [ + { + type: "schema", + detail: { + schema: "https://ns.adobe.com/personalization/mock-action", + data: { + hello: "kitty", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + ], + }, + ], + }, + }, + ], + scope: "web://mywebsite.com", + }; +}; +export const mockRulesetResponseWithCondition = (condition) => { + return { + getPayloadsByType: () => [ + payloadWithCondition({ + definition: { + conditions: [condition], + logic: "and", + }, + type: "group", + }), + ], + }; +}; +const mockEvent = { + getContent: () => ({ + query: {}, + }), + hasQuery: () => true, + getViewName: () => undefined, +}; +export const setupResponseHandler = (applyResponse, window, condition) => { + const storage = { + getItem: vi.fn(), + setItem: vi.fn(), + clear: vi.fn(), + }; + const eventRegistry = createEventRegistry({ + storage, + }); + const decisionProvider = createDecisionProvider({ + eventRegistry, + }); + const getBrowser = injectGetBrowser({ + userAgent: window.navigator.userAgent, + }); + const contextProvider = createContextProvider({ + eventRegistry, + window, + getBrowser, + }); + const onResponseHandler = createOnResponseHandler({ + renderDecisions: true, + decisionProvider, + applyResponse, + event: mockEvent, + decisionContext: contextProvider.getContext(), + }); + onResponseHandler({ + response: mockRulesetResponseWithCondition(condition), + }); +}; diff --git a/vtest/unit/specs/components/RulesEngine/createApplyResponse.spec.js b/vtest/unit/specs/components/RulesEngine/createApplyResponse.spec.js new file mode 100644 index 000000000..5fb667834 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/createApplyResponse.spec.js @@ -0,0 +1,85 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, describe, it, expect } from "vitest"; +import createApplyResponse from "../../../../../src/components/RulesEngine/createApplyResponse.js"; + +describe("RulesEngine:createApplyResponse", () => { + const proposition = { + id: "AT:eyJhY3Rpdml0eUlkIjoiMTQxMDY0IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", + scope: "__view__", + items: [], + }; + it("calls lifecycle.onDecision with propositions", () => { + const lifecycle = { + onDecision: vi.fn().mockReturnValue(Promise.resolve()), + }; + const applyResponse = createApplyResponse(lifecycle); + const mockEvent = { + getViewName: () => undefined, + }; + const personalization = {}; + applyResponse({ + propositions: [proposition], + event: mockEvent, + personalization, + }); + expect(lifecycle.onDecision).toHaveBeenCalledWith({ + renderDecisions: false, + propositions: [proposition], + event: mockEvent, + personalization: {}, + }); + }); + it("calls lifecycle.onDecision with viewName", () => { + const lifecycle = { + onDecision: vi.fn().mockReturnValue(Promise.resolve()), + }; + const applyResponse = createApplyResponse(lifecycle); + const mockEvent = { + getViewName: () => "oh hai", + }; + applyResponse({ + renderDecisions: true, + event: mockEvent, + personalization: {}, + propositions: [proposition], + }); + expect(lifecycle.onDecision).toHaveBeenCalledWith({ + renderDecisions: true, + propositions: [proposition], + event: mockEvent, + personalization: {}, + }); + }); + it("call lifecycle.onDecision even if no propositions", () => { + // this use case is necessary for content cards with no items + const lifecycle = { + onDecision: vi.fn().mockReturnValue(Promise.resolve()), + }; + const applyResponse = createApplyResponse(lifecycle); + const mockEvent = { + getViewName: () => undefined, + }; + applyResponse({ + renderDecisions: true, + propositions: [], + event: mockEvent, + personalization: {}, + }); + expect(lifecycle.onDecision).toHaveBeenCalledWith({ + renderDecisions: true, + propositions: [], + event: mockEvent, + personalization: {}, + }); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/createConsequenceAdapter.spec.js b/vtest/unit/specs/components/RulesEngine/createConsequenceAdapter.spec.js new file mode 100644 index 000000000..4ab2f03b0 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/createConsequenceAdapter.spec.js @@ -0,0 +1,76 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import createConsequenceAdapter from "../../../../../src/components/RulesEngine/createConsequenceAdapter.js"; +import { TEXT_HTML } from "../../../../../src/constants/contentType.js"; + +describe("RulesEngine:createConsequenceAdapter", () => { + const ADAPTED_CONSEQUENCE = { + schema: "https://ns.adobe.com/personalization/message/in-app", + data: { + mobileParameters: { + verticalAlign: "center", + dismissAnimation: "top", + verticalInset: 0, + backdropOpacity: 0.2, + cornerRadius: 15, + horizontalInset: 0, + uiTakeover: true, + horizontalAlign: "center", + width: 80, + displayAnimation: "top", + backdropColor: "#000000", + height: 60, + }, + webParameters: expect.any(Object), + content: "", + contentType: TEXT_HTML, + }, + id: "72042c7c-4e34-44f6-af95-1072ae117424", + }; + it("handles cjmiam", () => { + const consequenceAdapter = createConsequenceAdapter(); + const adaptedConsequence = consequenceAdapter({ + id: "72042c7c-4e34-44f6-af95-1072ae117424", + type: "cjmiam", + detail: { + mobileParameters: { + verticalAlign: "center", + dismissAnimation: "top", + verticalInset: 0, + backdropOpacity: 0.2, + cornerRadius: 15, + horizontalInset: 0, + uiTakeover: true, + horizontalAlign: "center", + width: 80, + displayAnimation: "top", + backdropColor: "#000000", + height: 60, + }, + html: "", + }, + }); + expect(adaptedConsequence).toEqual(ADAPTED_CONSEQUENCE); + }); + it("handles schema", () => { + const consequenceAdapter = createConsequenceAdapter(); + const adaptedConsequence = consequenceAdapter({ + id: "72042c7c-4e34-44f6-af95-1072ae117424", + type: "schema", + detail: { + ...ADAPTED_CONSEQUENCE, + }, + }); + expect(adaptedConsequence).toEqual(ADAPTED_CONSEQUENCE); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/createContextProvider.spec.js b/vtest/unit/specs/components/RulesEngine/createContextProvider.spec.js new file mode 100644 index 000000000..7b7034756 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/createContextProvider.spec.js @@ -0,0 +1,171 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, afterEach, describe, it, expect } from "vitest"; +import createContextProvider from "../../../../../src/components/RulesEngine/createContextProvider.js"; +import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; + +describe("RulesEngine:createContextProvider", () => { + let contextProvider; + let eventRegistry; + let storage; + let window; + let mockedTimestamp; + let getBrowser; + beforeEach(() => { + storage = { + getItem: vi.fn(), + setItem: vi.fn(), + clear: vi.fn(), + }; + window = { + title: "My awesome website", + referrer: "https://stage.applookout.net/", + url: "https://my.web-site.net:8080/about?m=1&t=5&name=jimmy#home", + width: 100, + height: 100, + scrollX: 10, + scrollY: 10, + navigator: { + userAgent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36", + }, + }; + mockedTimestamp = new Date(Date.UTC(2023, 4, 11, 12, 34, 56)); + vi.useFakeTimers(); + vi.setSystemTime(mockedTimestamp); + getBrowser = vi.fn().mockReturnValue("Chrome"); + }); + afterEach(() => { + vi.useRealTimers(); + }); + it("returns page context", () => { + eventRegistry = createEventRegistry({ + storage, + }); + contextProvider = createContextProvider({ + eventRegistry, + window, + getBrowser, + }); + expect(contextProvider.getContext()).toEqual( + expect.objectContaining({ + "page.title": "My awesome website", + "page.url": + "https://my.web-site.net:8080/about?m=1&t=5&name=jimmy#home", + "page.path": "/about", + "page.query": "m=1&t=5&name=jimmy", + "page.fragment": "home", + "page.domain": "my.web-site.net", + "page.subdomain": "my", + "page.topLevelDomain": "net", + }), + ); + }); + it("returns referring page context", () => { + eventRegistry = createEventRegistry({ + storage, + }); + contextProvider = createContextProvider({ + eventRegistry, + window, + getBrowser, + }); + expect(contextProvider.getContext()).toEqual( + expect.objectContaining({ + "referringPage.url": "https://stage.applookout.net/", + "referringPage.path": "/", + "referringPage.query": "", + "referringPage.fragment": "", + "referringPage.domain": "stage.applookout.net", + "referringPage.subdomain": "stage", + "referringPage.topLevelDomain": "net", + }), + ); + }); + it("returns browser context", () => { + eventRegistry = createEventRegistry({ + storage, + }); + contextProvider = createContextProvider({ + eventRegistry, + window, + getBrowser, + }); + expect(contextProvider.getContext()).toEqual( + expect.objectContaining({ + "browser.name": "Chrome", + }), + ); + }); + it("returns windows context", () => { + eventRegistry = createEventRegistry({ + storage, + }); + contextProvider = createContextProvider({ + eventRegistry, + window, + getBrowser, + }); + expect(contextProvider.getContext()).toEqual( + expect.objectContaining({ + "window.height": 100, + "window.width": 100, + "window.scrollY": 10, + "window.scrollX": 10, + }), + ); + }); + it("includes provided context passed in", () => { + eventRegistry = createEventRegistry({ + storage, + }); + contextProvider = createContextProvider({ + eventRegistry, + window, + getBrowser, + }); + expect( + contextProvider.getContext({ + cool: "beans", + }), + ).toEqual( + expect.objectContaining({ + cool: "beans", + }), + ); + }); + it("includes events context", () => { + const events = { + abc: { + event: { + id: "abc", + type: "display", + }, + timestamp: new Date().getTime(), + count: 1, + }, + }; + eventRegistry = { + toJSON: () => events, + }; + contextProvider = createContextProvider({ + eventRegistry, + window, + getBrowser, + }); + expect( + contextProvider.getContext({ + cool: "beans", + }).events, + ).toEqual(events); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/createDecisionHistory.spec.js b/vtest/unit/specs/components/RulesEngine/createDecisionHistory.spec.js new file mode 100644 index 000000000..931a35a75 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/createDecisionHistory.spec.js @@ -0,0 +1,79 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, afterEach, describe, it, expect } from "vitest"; +import createDecisionHistory from "../../../../../src/components/RulesEngine/createDecisionHistory.js"; +import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; + +describe("RulesEngine:decisionHistory", () => { + let storage; + let history; + let mockedTimestamp; + beforeEach(() => { + storage = { + getItem: vi.fn(), + setItem: vi.fn(), + clear: vi.fn(), + }; + history = createDecisionHistory({ + eventRegistry: createEventRegistry({ + storage, + }), + }); + + mockedTimestamp = new Date("2023-05-24T08:00:00Z"); + vi.useFakeTimers(); + vi.setSystemTime(mockedTimestamp); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("records decision time", () => { + const decision = history.recordQualified({ + id: "abc", + }); + expect(Object.getPrototypeOf(decision)).toEqual(Object.prototype); + expect(decision.timestamp).toEqual(expect.any(Number)); + }); + it("preserves first decision time, if decision already recorded", () => { + const firstDecision = history.recordQualified({ + id: "abc", + }); + + vi.advanceTimersByTime(60); + + expect( + history.recordQualified({ + id: "abc", + }).firstTimestamp, + ).toEqual(firstDecision.firstTimestamp); + + expect( + history.recordQualified({ + id: "abc", + }).firstTimestamp, + ).toEqual(firstDecision.timestamp); + }); + it("restores history from event storage", () => { + expect(storage.getItem).toHaveBeenCalledWith("events"); + }); + it("saves history to event storage", () => { + history.recordQualified({ + id: "abc", + }); + + vi.advanceTimersByTime(60); + + expect(storage.setItem).toHaveBeenCalledWith("events", expect.any(String)); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/createDecisionProvider.spec.js b/vtest/unit/specs/components/RulesEngine/createDecisionProvider.spec.js new file mode 100644 index 000000000..faac9d7d1 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/createDecisionProvider.spec.js @@ -0,0 +1,485 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createDecisionProvider from "../../../../../src/components/RulesEngine/createDecisionProvider.js"; +import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; + +describe("RulesEngine:createDecisionProvider", () => { + let decisionProvider; + let storage; + let eventRegistry; + beforeEach(() => { + storage = { + getItem: vi.fn(), + setItem: vi.fn(), + clear: vi.fn(), + }; + eventRegistry = createEventRegistry({ + storage, + }); + decisionProvider = createDecisionProvider({ + eventRegistry, + }); + decisionProvider.addPayloads([ + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scope: "web://mywebsite.com", + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + eventToken: "abc", + }, + strategies: [ + { + strategyID: "3VQe3oIqiYq2RAsYzmDTSf", + treatmentID: "yu7rkogezumca7i0i44v", + }, + ], + activity: { + id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", + }, + correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", + }, + items: [ + { + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + schema: "https://ns.adobe.com/personalization/ruleset-item", + data: { + version: 1, + rules: [ + { + condition: { + definition: { + conditions: [ + { + definition: { + conditions: [ + { + definition: { + key: "color", + matcher: "eq", + values: ["orange", "blue"], + }, + type: "matcher", + }, + { + definition: { + key: "action", + matcher: "eq", + values: ["lipstick"], + }, + type: "matcher", + }, + ], + logic: "and", + }, + type: "group", + }, + ], + logic: "and", + }, + type: "group", + }, + consequences: [ + { + type: "schema", + detail: { + schema: + "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + type: "schema", + detail: { + schema: + "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + }, + ], + }, + }, + ], + }, + { + id: "3d5d69cd-acde-4eca-b43b-a54574b67bb0", + scope: "web://mywebsite.com", + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + eventToken: "abc", + }, + strategies: [ + { + strategyID: "3VQe3oIqiYq2RAsYzmDTSf", + treatmentID: "yu7rkogezumca7i0i44v", + }, + ], + activity: { + id: "48ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d168", + }, + correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", + }, + items: [ + { + id: "5229f502-38d6-40c3-9a3a-b5b1a6adc441", + schema: "https://ns.adobe.com/personalization/ruleset-item", + data: { + version: 1, + rules: [ + { + condition: { + definition: { + conditions: [ + { + definition: { + conditions: [ + { + definition: { + key: "xdm.web.webPageDetails.viewName", + matcher: "eq", + values: ["home"], + }, + type: "matcher", + }, + ], + logic: "and", + }, + type: "group", + }, + ], + logic: "and", + }, + type: "group", + }, + consequences: [ + { + type: "schema", + detail: { + schema: + "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "div#spa #spa-content h3", + type: "setHtml", + content: "i can haz?", + prehidingSelector: "div#spa #spa-content h3", + }, + id: "8a0d7a45-70fb-4845-a093-2133b5744c8d", + }, + id: "8a0d7a45-70fb-4845-a093-2133b5744c8d", + }, + { + type: "schema", + detail: { + schema: + "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "div#spa #spa-content p", + type: "setHtml", + content: "ALL YOUR BASE ARE BELONG TO US", + prehidingSelector: "div#spa #spa-content p", + }, + id: "a44af51a-e073-4e8c-92e1-84ac28210043", + }, + id: "a44af51a-e073-4e8c-92e1-84ac28210043", + }, + ], + }, + ], + }, + }, + ], + }, + ]); + }); + it("returns a single payload with items that qualify", () => { + expect( + decisionProvider.evaluate({ + color: "blue", + action: "lipstick", + }), + ).toEqual([ + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scope: "web://mywebsite.com", + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + eventToken: "abc", + }, + strategies: [ + { + strategyID: "3VQe3oIqiYq2RAsYzmDTSf", + treatmentID: "yu7rkogezumca7i0i44v", + }, + ], + activity: { + id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", + }, + correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", + }, + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + }, + ]); + }); + it("returns a different single payload with items that qualify", () => { + expect( + decisionProvider.evaluate({ + "xdm.web.webPageDetails.viewName": "home", + }), + ).toEqual([ + { + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + eventToken: "abc", + }, + strategies: [ + { + strategyID: "3VQe3oIqiYq2RAsYzmDTSf", + treatmentID: "yu7rkogezumca7i0i44v", + }, + ], + activity: { + id: "48ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d168", + }, + correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", + }, + id: "3d5d69cd-acde-4eca-b43b-a54574b67bb0", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "div#spa #spa-content h3", + type: "setHtml", + content: "i can haz?", + prehidingSelector: "div#spa #spa-content h3", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "8a0d7a45-70fb-4845-a093-2133b5744c8d", + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "div#spa #spa-content p", + type: "setHtml", + content: "ALL YOUR BASE ARE BELONG TO US", + prehidingSelector: "div#spa #spa-content p", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "a44af51a-e073-4e8c-92e1-84ac28210043", + }, + ], + scope: "web://mywebsite.com", + }, + ]); + }); + it("returns two payloads with items that qualify", () => { + expect( + decisionProvider.evaluate({ + color: "blue", + action: "lipstick", + "xdm.web.webPageDetails.viewName": "home", + }), + ).toEqual([ + { + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + eventToken: "abc", + }, + strategies: [ + { + strategyID: "3VQe3oIqiYq2RAsYzmDTSf", + treatmentID: "yu7rkogezumca7i0i44v", + }, + ], + activity: { + id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", + }, + correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", + }, + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + scope: "web://mywebsite.com", + }, + { + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + eventToken: "abc", + }, + strategies: [ + { + strategyID: "3VQe3oIqiYq2RAsYzmDTSf", + treatmentID: "yu7rkogezumca7i0i44v", + }, + ], + activity: { + id: "48ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d168", + }, + correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", + }, + id: "3d5d69cd-acde-4eca-b43b-a54574b67bb0", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "div#spa #spa-content h3", + type: "setHtml", + content: "i can haz?", + prehidingSelector: "div#spa #spa-content h3", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "8a0d7a45-70fb-4845-a093-2133b5744c8d", + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "div#spa #spa-content p", + type: "setHtml", + content: "ALL YOUR BASE ARE BELONG TO US", + prehidingSelector: "div#spa #spa-content p", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "a44af51a-e073-4e8c-92e1-84ac28210043", + }, + ], + scope: "web://mywebsite.com", + }, + ]); + }); + it("ignores payloads that aren't json-ruleset type", () => { + decisionProvider.addPayload({ + id: "AT:eyJhY3Rpdml0eUlkIjoiMTQxMDY0IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", + scope: "__view__", + scopeDetails: { + decisionProvider: "TGT", + activity: { + id: "141064", + }, + experience: { + id: "0", + }, + strategies: [ + { + algorithmID: "0", + trafficType: "0", + }, + ], + characteristics: { + eventToken: "abc", + }, + correlationID: "141064:0:0:0", + }, + items: [ + { + id: "284525", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "setHtml", + format: "application/vnd.adobe.target.dom-action", + content: "
    oh hai
    ", + selector: "head", + prehidingSelector: "head", + }, + }, + ], + }); + expect(decisionProvider.evaluate()).toEqual([]); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/createEvaluableRulesetPayload.spec.js b/vtest/unit/specs/components/RulesEngine/createEvaluableRulesetPayload.spec.js new file mode 100644 index 000000000..c303a8b72 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/createEvaluableRulesetPayload.spec.js @@ -0,0 +1,364 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createEvaluableRulesetPayload from "../../../../../src/components/RulesEngine/createEvaluableRulesetPayload.js"; +import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; +import createDecisionHistory from "../../../../../src/components/RulesEngine/createDecisionHistory.js"; + +describe("RulesEngine:createEvaluableRulesetPayload", () => { + let storage; + let eventRegistry; + let decisionHistory; + beforeEach(() => { + storage = { + getItem: vi.fn(), + setItem: vi.fn(), + clear: vi.fn(), + }; + eventRegistry = createEventRegistry({ + storage, + }); + decisionHistory = createDecisionHistory({ + eventRegistry, + }); + }); + it("consumes ruleset-items", () => { + const evaluableRulesetPayload = createEvaluableRulesetPayload( + { + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + eventToken: "abc", + }, + strategies: [ + { + strategyID: "3VQe3oIqiYq2RAsYzmDTSf", + treatmentID: "yu7rkogezumca7i0i44v", + }, + ], + activity: { + id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", + }, + correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", + }, + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + items: [ + { + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + schema: "https://ns.adobe.com/personalization/ruleset-item", + data: { + version: 1, + rules: [ + { + condition: { + definition: { + conditions: [ + { + definition: { + conditions: [ + { + definition: { + key: "color", + matcher: "eq", + values: ["orange", "blue"], + }, + type: "matcher", + }, + { + definition: { + key: "action", + matcher: "eq", + values: ["lipstick"], + }, + type: "matcher", + }, + ], + logic: "and", + }, + type: "group", + }, + ], + logic: "and", + }, + type: "group", + }, + consequences: [ + { + type: "schema", + detail: { + schema: + "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + type: "schema", + detail: { + schema: + "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + }, + ], + }, + }, + ], + scope: "web://mywebsite.com", + }, + eventRegistry, + decisionHistory, + ); + expect( + evaluableRulesetPayload.evaluate({ + color: "orange", + action: "lipstick", + }), + ).toEqual({ + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + eventToken: "abc", + }, + strategies: [ + { + strategyID: "3VQe3oIqiYq2RAsYzmDTSf", + treatmentID: "yu7rkogezumca7i0i44v", + }, + ], + activity: { + id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", + }, + correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", + }, + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + scope: "web://mywebsite.com", + }); + }); + it("consumes json-content-items", () => { + const evaluableRulesetPayload = createEvaluableRulesetPayload( + { + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + eventToken: "abc", + }, + strategies: [ + { + strategyID: "3VQe3oIqiYq2RAsYzmDTSf", + treatmentID: "yu7rkogezumca7i0i44v", + }, + ], + activity: { + id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", + }, + correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", + }, + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + items: [ + { + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + schema: "https://ns.adobe.com/personalization/json-content-item", + data: { + content: { + version: 1, + rules: [ + { + condition: { + definition: { + conditions: [ + { + definition: { + conditions: [ + { + definition: { + key: "color", + matcher: "eq", + values: ["orange", "blue"], + }, + type: "matcher", + }, + { + definition: { + key: "action", + matcher: "eq", + values: ["lipstick"], + }, + type: "matcher", + }, + ], + logic: "and", + }, + type: "group", + }, + ], + logic: "and", + }, + type: "group", + }, + consequences: [ + { + type: "schema", + detail: { + schema: + "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + type: "schema", + detail: { + schema: + "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + }, + ], + }, + }, + }, + ], + scope: "web://mywebsite.com", + }, + eventRegistry, + decisionHistory, + ); + expect( + evaluableRulesetPayload.evaluate({ + color: "orange", + action: "lipstick", + }), + ).toEqual({ + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + eventToken: "abc", + }, + strategies: [ + { + strategyID: "3VQe3oIqiYq2RAsYzmDTSf", + treatmentID: "yu7rkogezumca7i0i44v", + }, + ], + activity: { + id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", + }, + correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", + }, + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + scope: "web://mywebsite.com", + }); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/createEvaluateRulesetsCommand.spec.js b/vtest/unit/specs/components/RulesEngine/createEvaluateRulesetsCommand.spec.js new file mode 100644 index 000000000..ae1b3225f --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/createEvaluateRulesetsCommand.spec.js @@ -0,0 +1,225 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createEvaluateRulesetsCommand from "../../../../../src/components/RulesEngine/createEvaluateRulesetsCommand.js"; +import createContextProvider from "../../../../../src/components/RulesEngine/createContextProvider.js"; +import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; +import createDecisionProvider from "../../../../../src/components/RulesEngine/createDecisionProvider.js"; +import createApplyResponse from "../../../../../src/components/RulesEngine/createApplyResponse.js"; +import injectGetBrowser from "../../../../../src/utils/injectGetBrowser.js"; + +describe("RulesEngine:evaluateRulesetsCommand", () => { + let onDecision; + let applyResponse; + let storage; + let eventRegistry; + let getBrowser; + let contextProvider; + let decisionProvider; + let evaluateRulesetsCommand; + beforeEach(() => { + onDecision = vi.fn(); + applyResponse = createApplyResponse({ + onDecision, + }); + storage = { + getItem: vi.fn(), + setItem: vi.fn(), + clear: vi.fn(), + }; + eventRegistry = createEventRegistry({ + storage, + }); + getBrowser = injectGetBrowser({ + userAgent: window.navigator.userAgent, + }); + contextProvider = createContextProvider({ + eventRegistry, + window, + getBrowser, + }); + decisionProvider = createDecisionProvider({ + eventRegistry, + }); + decisionProvider.addPayload({ + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scopeDetails: { + activity: { + id: "abc#xyz", + }, + }, + items: [ + { + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + schema: "https://ns.adobe.com/personalization/ruleset-item", + data: { + version: 1, + rules: [ + { + condition: { + definition: { + conditions: [ + { + definition: { + conditions: [ + { + definition: { + key: "color", + matcher: "eq", + values: ["orange", "blue"], + }, + type: "matcher", + }, + { + definition: { + key: "action", + matcher: "eq", + values: ["greet"], + }, + type: "matcher", + }, + ], + logic: "and", + }, + type: "group", + }, + ], + logic: "and", + }, + type: "group", + }, + consequences: [ + { + type: "schema", + detail: { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + ], + }, + ], + }, + }, + ], + scope: "web://mywebsite.com", + }); + evaluateRulesetsCommand = createEvaluateRulesetsCommand({ + contextProvider, + decisionProvider, + }); + }); + it("onDecisions receives renderDecisions=true", () => { + const result = evaluateRulesetsCommand.run({ + renderDecisions: true, + decisionContext: { + color: "orange", + action: "greet", + }, + applyResponse, + }); + const expectedResult = { + propositions: [ + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scopeDetails: { + activity: { + id: "abc#xyz", + }, + }, + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + ], + scope: "web://mywebsite.com", + }, + ], + }; + expect(result).toEqual(expectedResult); + expect(onDecision).toHaveBeenNthCalledWith(1, { + renderDecisions: true, + event: undefined, + personalization: undefined, + ...expectedResult, + }); + }); + it("onDecisions receives renderDecisions=false", () => { + const result = evaluateRulesetsCommand.run({ + decisionContext: { + color: "orange", + action: "greet", + }, + applyResponse, + }); + const expectedResult = { + propositions: [ + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scopeDetails: { + activity: { + id: "abc#xyz", + }, + }, + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + ], + scope: "web://mywebsite.com", + }, + ], + }; + expect(result).toEqual(expectedResult); + expect(onDecision).toHaveBeenNthCalledWith(1, { + renderDecisions: false, + event: undefined, + personalization: undefined, + ...expectedResult, + }); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/createEventRegistry.spec.js b/vtest/unit/specs/components/RulesEngine/createEventRegistry.spec.js new file mode 100644 index 000000000..2a6ece033 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/createEventRegistry.spec.js @@ -0,0 +1,401 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, afterEach, describe, it, expect } from "vitest"; +import createEventRegistry, { + createEventPruner, +} from "../../../../../src/components/RulesEngine/createEventRegistry.js"; + +describe("RulesEngine:createEventRegistry", () => { + let storage; + let mockedTimestamp; + beforeEach(() => { + storage = { + getItem: vi.fn(), + setItem: vi.fn(), + clear: vi.fn(), + }; + mockedTimestamp = new Date("2023-05-24T08:00:00Z"); + vi.useFakeTimers(); + vi.setSystemTime(mockedTimestamp); + }); + afterEach(() => { + vi.useRealTimers(); + }); + it("registers events", () => { + const eventRegistry = createEventRegistry({ + storage, + }); + const getContent = () => ({ + xdm: { + eventType: "decisioning.propositionDisplay", + _experience: { + decisioning: { + propositions: [ + { + id: "111", + scope: "mobileapp://com.adobe.aguaAppIos", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "ccaa539e-ca14-4d42-ac9a-0a17e69a63e4", + activity: { + id: "111#aaa", + }, + }, + }, + { + id: "222", + scope: "mobileapp://com.adobe.aguaAppIos", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "ccaa539e-ca14-4d42-ac9a-0a17e69a63e4", + activity: { + id: "222#bbb", + }, + }, + }, + { + id: "333", + scope: "web://something", + scopeDetails: { + decisionProvider: "TGT", + correlationID: "aasfsdf", + activity: { + id: "333#ccc", + }, + }, + }, + ], + propositionEventType: { + display: 1, + }, + }, + }, + }, + }); + const event = { + getContent, + }; + eventRegistry.addExperienceEdgeEvent(event); + expect(eventRegistry.toJSON()).toEqual({ + display: { + "111#aaa": { + event: expect.objectContaining({ + "iam.id": "111#aaa", + "iam.eventType": "display", + }), + firstTimestamp: expect.any(Number), + timestamp: expect.any(Number), + count: 1, + }, + "222#bbb": { + event: expect.objectContaining({ + "iam.id": "222#bbb", + "iam.eventType": "display", + }), + firstTimestamp: expect.any(Number), + timestamp: expect.any(Number), + count: 1, + }, + }, + }); + }); + it("does not register invalid events", () => { + const eventRegistry = createEventRegistry({ + storage, + }); + eventRegistry.addExperienceEdgeEvent({ + getContent: () => ({ + xdm: { + eventType: "display", + }, + }), + }); + eventRegistry.addExperienceEdgeEvent({ + getContent: () => ({ + xdm: { + eventType: "display", + _experience: {}, + }, + }), + }); + eventRegistry.addExperienceEdgeEvent({ + getContent: () => ({ + xdm: { + eventType: "display", + _experience: { + decisioning: {}, + }, + }, + }), + }); + eventRegistry.addExperienceEdgeEvent({ + getContent: () => ({}), + }); + expect(eventRegistry.toJSON()).toEqual({}); + }); + it("does not register events without type and id", () => { + const eventRegistry = createEventRegistry({ + storage, + }); + expect(eventRegistry.addEvent({}, "trigger")).toBeUndefined(); + expect(eventRegistry.addEvent({}, "trigger", undefined)).toBeUndefined(); + expect(eventRegistry.addEvent({})).toBeUndefined(); + expect(eventRegistry.toJSON()).toEqual({}); + expect( + eventRegistry.addEvent( + { + something: "special", + }, + "display", + "abc#123", + ), + ).toEqual({ + event: { + "iam.id": "abc#123", + "iam.eventType": "display", + "iam.action": undefined, + something: "special", + }, + firstTimestamp: expect.any(Number), + timestamp: expect.any(Number), + count: 1, + }); + expect(eventRegistry.toJSON()).toEqual({ + display: { + "abc#123": { + event: { + "iam.id": "abc#123", + "iam.eventType": "display", + "iam.action": undefined, + something: "special", + }, + firstTimestamp: expect.any(Number), + timestamp: expect.any(Number), + count: 1, + }, + }, + }); + }); + it("increments count and sets timestamp", () => { + const eventRegistry = createEventRegistry({ + storage, + }); + const getContent = () => ({ + xdm: { + eventType: "decisioning.propositionDisplay", + _experience: { + decisioning: { + propositions: [ + { + id: "111", + scope: "mobileapp://com.adobe.aguaAppIos", + scopeDetails: { + decisionProvider: "AJO", + correlationID: "ccaa539e-ca14-4d42-ac9a-0a17e69a63e4", + activity: { + id: "111#aaa", + }, + }, + }, + ], + propositionEventType: { + display: 1, + }, + }, + }, + }, + }); + const event = { + getContent, + }; + let lastEventTime = 0; + eventRegistry.addExperienceEdgeEvent(event); + expect(eventRegistry.getEvent("display", "111#aaa")).toEqual({ + event: expect.objectContaining({ + "iam.id": "111#aaa", + "iam.eventType": "display", + }), + firstTimestamp: expect.any(Number), + timestamp: expect.any(Number), + count: 1, + }); + expect( + eventRegistry.getEvent("display", "111#aaa").timestamp, + ).toBeGreaterThan(lastEventTime); + lastEventTime = eventRegistry.getEvent("display", "111#aaa").timestamp; + setTimeout(() => { + eventRegistry.addExperienceEdgeEvent(event); // again + + expect(eventRegistry.getEvent("display", "111#aaa")).toEqual({ + event: expect.objectContaining({ + "iam.id": "111#aaa", + "iam.eventType": "display", + }), + firstTimestamp: expect.any(Number), + timestamp: expect.any(Number), + count: 2, + }); + expect( + eventRegistry.getEvent("display", "111#aaa").timestamp, + ).toBeGreaterThan(lastEventTime); + }, 50); + vi.advanceTimersByTime(60); + }); + it("limits events to 1000 events", () => { + const prune = createEventPruner(); + const events = {}; + events["decisioning.propositionDisplay"] = {}; + events["decisioning.propositionInteract"] = {}; + for (let i = 0; i < 2000; i += 1) { + events["decisioning.propositionDisplay"][i] = { + event: { + "iam.id": i, + "iam.eventType": "decisioning.propositionDisplay", + }, + firstTimestamp: "2023-05-23T08:00:00Z", + timestamp: mockedTimestamp, + count: 1, + }; + events["decisioning.propositionInteract"][i] = { + event: { + "iam.id": i, + "iam.eventType": "decisioning.propositionInteract", + }, + firstTimestamp: "2023-05-23T08:00:00Z", + timestamp: mockedTimestamp, + count: 1, + }; + const pruned = prune(events); + const interactEvents = Object.values( + pruned["decisioning.propositionInteract"], + ); + const displayEvents = Object.values( + pruned["decisioning.propositionDisplay"], + ); + expect(interactEvents.length).not.toBeGreaterThan(1000); + expect(displayEvents.length).not.toBeGreaterThan(1000); + if (i > 1000) { + expect(interactEvents[0].event["iam.id"]).toEqual(i - 999); + expect(displayEvents[0].event["iam.id"]).toEqual(i - 999); + } + if (i > 0) { + expect( + interactEvents[0].timestamp < + interactEvents[interactEvents.length - 1].timestamp, + ); + expect( + displayEvents[0].timestamp < + displayEvents[interactEvents.length - 1].timestamp, + ); + } + } + }); + it("has configurable limits", () => { + const prune = createEventPruner(10); + const events = {}; + events["decisioning.propositionDisplay"] = {}; + for (let i = 0; i < 20; i += 1) { + events["decisioning.propositionDisplay"][i] = { + event: { + "iam.id": i, + "iam.eventType": "decisioning.propositionDisplay", + }, + firstTimestamp: 1, + timestamp: 1, + count: 1, + }; + const pruned = prune(events); + const displayEvents = Object.values( + pruned["decisioning.propositionDisplay"], + ); + expect(displayEvents.length).not.toBeGreaterThan(10); + } + }); + it("should filter events based on expiration date", () => { + const pruner = createEventPruner(4, 2); + const events = {}; + events["decisioning.propositionDisplay"] = { + 1: { + event: { + "iam.id": 1, + "iam.eventType": "decisioning.propositionInteract", + }, + firstTimestamp: "2023-05-20T10:00:00Z", + timestamp: mockedTimestamp, + count: 1, + }, + 2: { + event: { + "iam.id": 2, + "iam.eventType": "decisioning.propositionInteract", + }, + firstTimestamp: "2023-05-24T15:00:00Z", + timestamp: mockedTimestamp, + count: 1, + }, + }; + events["decisioning.propositionInteract"] = { + 3: { + event: { + "iam.id": 3, + "iam.eventType": "decisioning.propositionInteract", + }, + firstTimestamp: "2023-05-23T08:00:00Z", + timestamp: mockedTimestamp, + count: 1, + }, + 4: { + event: { + "iam.id": 4, + "iam.eventType": "decisioning.propositionInteract", + }, + firstTimestamp: "2023-05-23T08:00:00Z", + timestamp: mockedTimestamp, + count: 1, + }, + }; + const prunedEvents = pruner(events); + expect(prunedEvents).toEqual({ + "decisioning.propositionDisplay": { + 2: { + event: { + "iam.id": 2, + "iam.eventType": "decisioning.propositionInteract", + }, + firstTimestamp: "2023-05-24T15:00:00Z", + timestamp: mockedTimestamp, + count: 1, + }, + }, + "decisioning.propositionInteract": { + 3: { + event: { + "iam.id": 3, + "iam.eventType": "decisioning.propositionInteract", + }, + firstTimestamp: "2023-05-23T08:00:00Z", + timestamp: mockedTimestamp, + count: 1, + }, + 4: { + event: { + "iam.id": 4, + "iam.eventType": "decisioning.propositionInteract", + }, + firstTimestamp: "2023-05-23T08:00:00Z", + timestamp: mockedTimestamp, + count: 1, + }, + }, + }); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/createOnResponseHandler.spec.js b/vtest/unit/specs/components/RulesEngine/createOnResponseHandler.spec.js new file mode 100644 index 000000000..65893c439 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/createOnResponseHandler.spec.js @@ -0,0 +1,390 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createOnResponseHandler from "../../../../../src/components/RulesEngine/createOnResponseHandler.js"; +import createDecisionProvider from "../../../../../src/components/RulesEngine/createDecisionProvider.js"; +import createApplyResponse from "../../../../../src/components/RulesEngine/createApplyResponse.js"; +import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; + +describe("RulesEngine:createOnResponseHandler", () => { + let lifecycle; + let storage; + let eventRegistry; + let decisionProvider; + let applyResponse; + beforeEach(() => { + lifecycle = { + onDecision: vi.fn().mockReturnValue(Promise.resolve()), + }; + storage = { + getItem: vi.fn(), + setItem: vi.fn(), + clear: vi.fn(), + }; + eventRegistry = createEventRegistry({ + storage, + }); + decisionProvider = createDecisionProvider({ + eventRegistry, + }); + applyResponse = createApplyResponse(lifecycle); + }); + it("calls lifecycle.onDecision with propositions based on decisionContext", () => { + const event = { + getViewName: () => undefined, + hasQuery: () => true, + getContent: () => ({ + query: {}, + xdm: { + web: { + webPageDetails: { + viewName: "contact", + URL: "https://mywebsite.com", + }, + webReferrer: { + URL: "https://google.com", + }, + }, + timestamp: new Date().toISOString(), + implementationDetails: { + name: "https://ns.adobe.com/experience/alloy", + version: "2.15.0", + environment: "browser", + }, + }, + data: { + moo: "woof", + }, + }), + }; + const decisionContext = { + color: "orange", + action: "lipstick", + }; + const personalization = { + surfaces: ["#woof"], + }; + const responseHandler = createOnResponseHandler({ + renderDecisions: true, + decisionProvider, + applyResponse, + event, + personalization, + decisionContext, + }); + const response = { + getPayloadsByType: () => [ + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scopeDetails: { + activity: { + id: "abc#xyz", + }, + }, + items: [ + { + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + schema: "https://ns.adobe.com/personalization/ruleset-item", + data: { + version: 1, + rules: [ + { + condition: { + definition: { + conditions: [ + { + definition: { + conditions: [ + { + definition: { + key: "color", + matcher: "eq", + values: ["orange", "blue"], + }, + type: "matcher", + }, + { + definition: { + key: "action", + matcher: "eq", + values: ["lipstick"], + }, + type: "matcher", + }, + ], + logic: "and", + }, + type: "group", + }, + ], + logic: "and", + }, + type: "group", + }, + consequences: [ + { + type: "schema", + detail: { + schema: + "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + type: "schema", + detail: { + schema: + "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + }, + ], + }, + }, + ], + scope: "web://target.jasonwaters.dev/aep.html", + }, + ], + }; + responseHandler({ + response, + }); + expect(lifecycle.onDecision).toHaveBeenCalledWith({ + event, + personalization, + renderDecisions: true, + propositions: [ + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scopeDetails: { + activity: { + id: "abc#xyz", + }, + }, + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + scope: "web://target.jasonwaters.dev/aep.html", + }, + ], + }); + }); + it("calls lifecycle.onDecision with propositions based on xdm and event data", () => { + const event = { + getViewName: () => "home", + hasQuery: () => true, + getContent: () => ({ + query: {}, + xdm: { + web: { + webPageDetails: { + viewName: "contact", + URL: "https://mywebsite.com", + }, + webReferrer: { + URL: "https://google.com", + }, + }, + timestamp: new Date().toISOString(), + implementationDetails: { + name: "https://ns.adobe.com/experience/alloy", + version: "12345", + environment: "browser", + }, + }, + data: { + moo: "woof", + }, + }), + }; + const decisionContext = {}; + const personalization = {}; + const responseHandler = createOnResponseHandler({ + renderDecisions: true, + decisionProvider, + applyResponse, + event, + personalization, + decisionContext, + }); + const response = { + getPayloadsByType: () => [ + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scopeDetails: { + activity: { + id: "abc#xyz", + }, + }, + items: [ + { + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + schema: "https://ns.adobe.com/personalization/ruleset-item", + data: { + version: 1, + rules: [ + { + condition: { + definition: { + conditions: [ + { + definition: { + conditions: [ + { + definition: { + key: "xdm.web.webPageDetails.viewName", + matcher: "eq", + values: ["contact"], + }, + type: "matcher", + }, + { + definition: { + key: "xdm.implementationDetails.version", + matcher: "eq", + values: ["12345"], + }, + type: "matcher", + }, + { + definition: { + key: "data.moo", + matcher: "eq", + values: ["woof"], + }, + type: "matcher", + }, + ], + logic: "and", + }, + type: "group", + }, + ], + logic: "and", + }, + type: "group", + }, + consequences: [ + { + type: "schema", + detail: { + schema: + "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + }, + ], + }, + }, + ], + scope: "web://target.jasonwaters.dev/aep.html", + }, + ], + }; + responseHandler({ + response, + }); + expect(lifecycle.onDecision).toHaveBeenCalledWith({ + renderDecisions: true, + propositions: [ + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scopeDetails: { + activity: { + id: "abc#xyz", + }, + }, + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + qualifiedDate: expect.any(Number), + displayedDate: undefined, + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + scope: "web://target.jasonwaters.dev/aep.html", + }, + ], + event, + personalization, + }); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/createSubscribeRulesetItems.spec.js b/vtest/unit/specs/components/RulesEngine/createSubscribeRulesetItems.spec.js new file mode 100644 index 000000000..1c51b43bf --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/createSubscribeRulesetItems.spec.js @@ -0,0 +1,1437 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import { + DOM_ACTION, + MESSAGE_CONTENT_CARD, +} from "../../../../../src/constants/schema.js"; +import createSubscribeRulesetItems from "../../../../../src/components/RulesEngine/createSubscribeRulesetItems.js"; +import { PropositionEventType } from "../../../../../src/constants/propositionEventType.js"; + +describe("RulesEngine:subscribeRulesetItems", () => { + let collect; + let subscribeRulesetItems; + const PROPOSITIONS = [ + { + id: "abc", + items: [ + { + schema: DOM_ACTION, + data: { + selector: "a", + type: "setAttribute", + content: { + src: "img/test.png", + }, + prehidingSelector: "a", + qualifiedDate: 1694198274647, + displayedDate: 1694198274647, + }, + id: "aabbcc", + }, + ], + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + items: [ + { + schema: DOM_ACTION, + data: { + selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + qualifiedDate: 1683042673380, + displayedDate: 1683042673395, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + schema: DOM_ACTION, + data: { + selector: "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + qualifiedDate: 1683042673387, + displayedDate: 1683042673395, + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", + items: [ + { + schema: MESSAGE_CONTENT_CARD, + data: { + expiryDate: 1712190456, + publishedDate: 1677752640000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/lumon.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "a handshake is available upon request.", + title: "Welcome to Lumon!", + }, + contentType: "application/json", + qualifiedDate: 1683042628060, + displayedDate: 1683042628070, + }, + id: "a48ca420-faea-467e-989a-5d179d9f562d", + }, + { + schema: MESSAGE_CONTENT_CARD, + data: { + expiryDate: 1712190456, + publishedDate: 1677839040000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/achievement.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Great job, you completed your profile.", + title: "Achievement Unlocked!", + }, + contentType: "application/json", + qualifiedDate: 1683042628064, + displayedDate: 1683042628070, + }, + id: "b7173290-588f-40c6-a05c-43ed5ec08b28", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", + items: [ + { + schema: MESSAGE_CONTENT_CARD, + data: { + expiryDate: 1712190456, + publishedDate: 1678098240000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/twitter.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Posting on social media helps us spread the word.", + title: "Thanks for sharing!", + }, + contentType: "application/json", + qualifiedDate: 1683042658312, + displayedDate: 1683042658316, + }, + id: "cfcb1af7-7bc2-45b2-a86a-0aa93fe69ce7", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "d1f7d411-a549-47bc-a4d8-c8e638b0a46b", + items: [ + { + schema: MESSAGE_CONTENT_CARD, + data: { + expiryDate: 1712190456, + publishedDate: 1678184640000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/gold-coin.jpg", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Now you're ready to earn!", + title: "Funds deposited!", + }, + contentType: "application/json", + qualifiedDate: 1683042653905, + displayedDate: 1683042653909, + }, + id: "0263e171-fa32-4c7a-9611-36b28137a81d", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ]; + beforeEach(() => { + collect = vi.fn().mockReturnValue(Promise.resolve()); + subscribeRulesetItems = createSubscribeRulesetItems({ + collect, + }); + }); + it("has a command defined", () => { + const { command } = subscribeRulesetItems; + expect(command).toEqual({ + optionsValidator: expect.any(Function), + run: expect.any(Function), + }); + }); + it("calls the callback with list of content cards", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + + // register a subscription. equivalent to alloy("subscribeRulesetItems", ... + command.run({ + surfaces: ["web://mywebsite.com/my-cards"], + schemas: [MESSAGE_CONTENT_CARD], + callback, + }); + refresh(PROPOSITIONS); + expect(callback).toHaveBeenNthCalledWith( + 1, + { + propositions: [ + { + id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", + items: [ + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1677752640000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/lumon.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "a handshake is available upon request.", + title: "Welcome to Lumon!", + }, + contentType: "application/json", + qualifiedDate: 1683042628060, + displayedDate: 1683042628070, + }, + id: "a48ca420-faea-467e-989a-5d179d9f562d", + }, + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1677839040000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/achievement.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Great job, you completed your profile.", + title: "Achievement Unlocked!", + }, + contentType: "application/json", + qualifiedDate: 1683042628064, + displayedDate: 1683042628070, + }, + id: "b7173290-588f-40c6-a05c-43ed5ec08b28", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", + items: [ + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1678098240000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/twitter.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Posting on social media helps us spread the word.", + title: "Thanks for sharing!", + }, + contentType: "application/json", + qualifiedDate: 1683042658312, + displayedDate: 1683042658316, + }, + id: "cfcb1af7-7bc2-45b2-a86a-0aa93fe69ce7", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "d1f7d411-a549-47bc-a4d8-c8e638b0a46b", + items: [ + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1678184640000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/gold-coin.jpg", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Now you're ready to earn!", + title: "Funds deposited!", + }, + contentType: "application/json", + qualifiedDate: 1683042653905, + displayedDate: 1683042653909, + }, + id: "0263e171-fa32-4c7a-9611-36b28137a81d", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + }, + expect.any(Function), + ); + }); + it("calls the callback with list of content cards at time of subscription (when there are existing propositions)", () => { + const { command, refresh } = subscribeRulesetItems; + refresh(PROPOSITIONS); + const callback = vi.fn(); + command.run({ + surfaces: ["web://mywebsite.com/my-cards"], + schemas: [MESSAGE_CONTENT_CARD], + callback, + }); + expect(callback).toHaveBeenNthCalledWith( + 1, + { + propositions: [ + { + id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", + items: [ + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1677752640000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/lumon.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "a handshake is available upon request.", + title: "Welcome to Lumon!", + }, + contentType: "application/json", + qualifiedDate: 1683042628060, + displayedDate: 1683042628070, + }, + id: "a48ca420-faea-467e-989a-5d179d9f562d", + }, + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1677839040000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/achievement.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Great job, you completed your profile.", + title: "Achievement Unlocked!", + }, + contentType: "application/json", + qualifiedDate: 1683042628064, + displayedDate: 1683042628070, + }, + id: "b7173290-588f-40c6-a05c-43ed5ec08b28", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", + items: [ + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1678098240000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/twitter.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Posting on social media helps us spread the word.", + title: "Thanks for sharing!", + }, + contentType: "application/json", + qualifiedDate: 1683042658312, + displayedDate: 1683042658316, + }, + id: "cfcb1af7-7bc2-45b2-a86a-0aa93fe69ce7", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "d1f7d411-a549-47bc-a4d8-c8e638b0a46b", + items: [ + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1678184640000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/gold-coin.jpg", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Now you're ready to earn!", + title: "Funds deposited!", + }, + contentType: "application/json", + qualifiedDate: 1683042653905, + displayedDate: 1683042653909, + }, + id: "0263e171-fa32-4c7a-9611-36b28137a81d", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + }, + expect.any(Function), + ); + }); + it("calls the callback with list of dom action items", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + + // register a subscription. equivalent to alloy("subscribeRulesetItems", ... + command.run({ + surfaces: ["web://mywebsite.com/my-cards"], + schemas: [DOM_ACTION], + callback, + }); + refresh(PROPOSITIONS); + expect(callback).toHaveBeenNthCalledWith( + 1, + { + propositions: [ + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + qualifiedDate: 1683042673380, + displayedDate: 1683042673395, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + qualifiedDate: 1683042673387, + displayedDate: 1683042673395, + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + }, + expect.any(Function), + ); + }); + it("calls the callback with list of dom action items at time of subscription (when there are existing propositions)", () => { + const { command, refresh } = subscribeRulesetItems; + refresh(PROPOSITIONS); + const callback = vi.fn(); + command.run({ + surfaces: ["web://mywebsite.com/my-cards"], + schemas: [DOM_ACTION], + callback, + }); + expect(callback).toHaveBeenNthCalledWith( + 1, + { + propositions: [ + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + qualifiedDate: 1683042673380, + displayedDate: 1683042673395, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + qualifiedDate: 1683042673387, + displayedDate: 1683042673395, + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + }, + expect.any(Function), + ); + }); + it("calls the callback with list of all schema-based items for single schema", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + + // register a subscription. equivalent to alloy("subscribeRulesetItems", ... + command.run({ + surfaces: ["web://mywebsite.com/my-cards"], + callback, + }); + refresh(PROPOSITIONS); + expect(callback).toHaveBeenNthCalledWith( + 1, + { + propositions: [ + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + qualifiedDate: 1683042673380, + displayedDate: 1683042673395, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + qualifiedDate: 1683042673387, + displayedDate: 1683042673395, + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", + items: [ + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1677752640000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/lumon.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "a handshake is available upon request.", + title: "Welcome to Lumon!", + }, + contentType: "application/json", + qualifiedDate: 1683042628060, + displayedDate: 1683042628070, + }, + id: "a48ca420-faea-467e-989a-5d179d9f562d", + }, + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1677839040000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/achievement.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Great job, you completed your profile.", + title: "Achievement Unlocked!", + }, + contentType: "application/json", + qualifiedDate: 1683042628064, + displayedDate: 1683042628070, + }, + id: "b7173290-588f-40c6-a05c-43ed5ec08b28", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", + items: [ + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1678098240000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/twitter.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Posting on social media helps us spread the word.", + title: "Thanks for sharing!", + }, + contentType: "application/json", + qualifiedDate: 1683042658312, + displayedDate: 1683042658316, + }, + id: "cfcb1af7-7bc2-45b2-a86a-0aa93fe69ce7", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "d1f7d411-a549-47bc-a4d8-c8e638b0a46b", + items: [ + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1678184640000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/gold-coin.jpg", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Now you're ready to earn!", + title: "Funds deposited!", + }, + contentType: "application/json", + qualifiedDate: 1683042653905, + displayedDate: 1683042653909, + }, + id: "0263e171-fa32-4c7a-9611-36b28137a81d", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + }, + expect.any(Function), + ); + }); + it("filters out all surfaces", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + + // register a subscription. equivalent to alloy("subscribeRulesetItems", ... + command.run({ + surfaces: [], + schemas: [MESSAGE_CONTENT_CARD], + callback, + }); + refresh(PROPOSITIONS); + expect(callback).toHaveBeenNthCalledWith( + 1, + { + propositions: [], + }, + expect.any(Function), + ); + }); + it("filters on surface", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + + // register a subscription. equivalent to alloy("subscribeRulesetItems", ... + command.run({ + surfaces: ["web://something.com"], + callback, + }); + refresh(PROPOSITIONS); + expect(callback).toHaveBeenNthCalledWith( + 1, + { + propositions: [ + { + id: "abc", + items: [ + expect.objectContaining({ + schema: DOM_ACTION, + data: { + selector: "a", + type: "setAttribute", + content: { + src: "img/test.png", + }, + prehidingSelector: "a", + qualifiedDate: 1694198274647, + displayedDate: 1694198274647, + }, + id: "aabbcc", + }), + ], + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + }, + expect.any(Function), + ); + }); + it("returns all surfaces and schemas", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + + // register a subscription. equivalent to alloy("subscribeRulesetItems", ... + command.run({ + callback, + }); + refresh(PROPOSITIONS); + expect(callback).toHaveBeenNthCalledWith( + 1, + { + propositions: [ + { + id: "abc", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: "a", + type: "setAttribute", + content: { + src: "img/test.png", + }, + prehidingSelector: "a", + qualifiedDate: 1694198274647, + displayedDate: 1694198274647, + }, + id: "aabbcc", + }, + ], + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + items: [ + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + qualifiedDate: 1683042673380, + displayedDate: 1683042673395, + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + type: "setHtml", + content: "Hello Treatment A!", + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", + qualifiedDate: 1683042673387, + displayedDate: 1683042673395, + }, + id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", + items: [ + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1677752640000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/lumon.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "a handshake is available upon request.", + title: "Welcome to Lumon!", + }, + contentType: "application/json", + qualifiedDate: 1683042628060, + displayedDate: 1683042628070, + }, + id: "a48ca420-faea-467e-989a-5d179d9f562d", + }, + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1677839040000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/achievement.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Great job, you completed your profile.", + title: "Achievement Unlocked!", + }, + contentType: "application/json", + qualifiedDate: 1683042628064, + displayedDate: 1683042628070, + }, + id: "b7173290-588f-40c6-a05c-43ed5ec08b28", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", + items: [ + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1678098240000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/twitter.png", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Posting on social media helps us spread the word.", + title: "Thanks for sharing!", + }, + contentType: "application/json", + qualifiedDate: 1683042658312, + displayedDate: 1683042658316, + }, + id: "cfcb1af7-7bc2-45b2-a86a-0aa93fe69ce7", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "d1f7d411-a549-47bc-a4d8-c8e638b0a46b", + items: [ + { + schema: + "https://ns.adobe.com/personalization/message/content-card", + data: { + expiryDate: 1712190456, + publishedDate: 1678184640000, + meta: { + surface: "web://mywebsite.com/my-cards", + }, + content: { + imageUrl: "img/gold-coin.jpg", + actionTitle: "Shop the sale!", + actionUrl: "https://luma.com/sale", + body: "Now you're ready to earn!", + title: "Funds deposited!", + }, + contentType: "application/json", + qualifiedDate: 1683042653905, + displayedDate: 1683042653909, + }, + id: "0263e171-fa32-4c7a-9611-36b28137a81d", + }, + ], + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + }, + expect.any(Function), + ); + }); + it("does not invoke callback if unsubscribed", async () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + + // register a subscription. equivalent to alloy("subscribeRulesetItems", ... + const { unsubscribe } = await command.run({ + callback, + }); + expect(unsubscribe instanceof Function).toBe(true); + unsubscribe(); + refresh(PROPOSITIONS); + expect(callback).not.toHaveBeenCalled(); + }); + it("collects interact events", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + command.run({ + surface: "web://mywebsite.com/my-cards", + callback, + }); + refresh(PROPOSITIONS); + const [{ propositions = [] }, collectEvent] = callback.mock.calls[0]; + collectEvent(PropositionEventType.INTERACT, [propositions[0]]); + expect(collect).toHaveBeenCalledWith({ + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionInteract", + documentMayUnload: true, + }); + }); + it("collects only one interact event per proposition", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + command.run({ + surface: "web://mywebsite.com/my-cards", + callback, + }); + refresh(PROPOSITIONS); + const [{ propositions = [] }, collectEvent] = callback.mock.calls[0]; + collectEvent(PropositionEventType.INTERACT, [ + propositions[0], + propositions[0], + propositions[0], + ]); + expect(collect).toHaveBeenCalledWith({ + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionInteract", + documentMayUnload: true, + }); + }); + it("collects separate interact events for each distinct proposition", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + command.run({ + surface: "web://mywebsite.com/my-cards", + callback, + }); + refresh(PROPOSITIONS); + const [{ propositions = [] }, collectEvent] = callback.mock.calls[0]; + collectEvent(PropositionEventType.INTERACT, [propositions[0]]); + expect(collect).toHaveBeenCalledWith({ + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionInteract", + documentMayUnload: true, + }); + collectEvent(PropositionEventType.INTERACT, [propositions[0]]); + expect(collect).toHaveBeenCalledWith({ + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionInteract", + documentMayUnload: true, + }); + expect(collect).toHaveBeenCalledTimes(2); + }); + it("collects multiple interact events for distinct propositions", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + command.run({ + surface: "web://mywebsite.com/my-cards", + callback, + }); + refresh(PROPOSITIONS); + const [{ propositions = [] }, collectEvent] = callback.mock.calls[0]; + collectEvent(PropositionEventType.INTERACT, [ + propositions[0], + propositions[1], + ]); + expect(collect).toHaveBeenNthCalledWith(1, { + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionInteract", + documentMayUnload: true, + }); + }); + it("collects display events", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + command.run({ + surface: "web://mywebsite.com/my-cards", + callback, + }); + refresh(PROPOSITIONS); + const [{ propositions = [] }, collectEvent] = callback.mock.calls[0]; + collectEvent(PropositionEventType.DISPLAY, [propositions[0]]); + expect(collect).toHaveBeenCalledWith({ + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionDisplay", + documentMayUnload: true, + }); + }); + it("collects only one display event per proposition", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + command.run({ + surface: "web://mywebsite.com/my-cards", + callback, + }); + refresh(PROPOSITIONS); + const [{ propositions = [] }, collectEvent] = callback.mock.calls[0]; + collectEvent(PropositionEventType.DISPLAY, [propositions[0]]); + collectEvent(PropositionEventType.DISPLAY, [ + propositions[0], + propositions[0], + ]); + expect(collect).toHaveBeenNthCalledWith(1, { + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionDisplay", + documentMayUnload: true, + }); + }); + it("collects multiple display events for distinct propositions", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + command.run({ + surface: "web://mywebsite.com/my-cards", + callback, + }); + refresh(PROPOSITIONS); + const [{ propositions = [] }, collectEvent] = callback.mock.calls[0]; + collectEvent(PropositionEventType.DISPLAY, [ + propositions[0], + propositions[1], + ]); + expect(collect).toHaveBeenNthCalledWith(1, { + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionDisplay", + documentMayUnload: true, + }); + }); + it("collects display events only once per session", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + command.run({ + surface: "web://mywebsite.com/my-cards", + callback, + }); + refresh(PROPOSITIONS); + const [{ propositions = [] }, collectEvent] = callback.mock.calls[0]; + collectEvent(PropositionEventType.DISPLAY, [ + propositions[0], + propositions[1], + ]); + collectEvent(PropositionEventType.DISPLAY, [ + propositions[0], + propositions[1], + ]); + collectEvent(PropositionEventType.DISPLAY, [propositions[2]]); + expect(collect).toHaveBeenCalledTimes(2); + expect(collect).toHaveBeenCalledWith({ + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionDisplay", + documentMayUnload: true, + }); + expect(collect).toHaveBeenCalledWith({ + decisionsMeta: [ + { + id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionDisplay", + documentMayUnload: true, + }); + }); + it("collects dismiss events", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + command.run({ + surface: "web://mywebsite.com/my-cards", + callback, + }); + refresh(PROPOSITIONS); + const [{ propositions = [] }, collectEvent] = callback.mock.calls[0]; + collectEvent(PropositionEventType.DISMISS, [propositions[0]]); + expect(collect).toHaveBeenCalledWith({ + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + documentMayUnload: true, + eventType: "decisioning.propositionDismiss", + }); + }); + it("collects only one dismiss event per proposition", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + command.run({ + surface: "web://mywebsite.com/my-cards", + callback, + }); + refresh(PROPOSITIONS); + const [{ propositions = [] }, collectEvent] = callback.mock.calls[0]; + collectEvent(PropositionEventType.DISMISS, [ + propositions[0], + propositions[0], + propositions[0], + ]); + expect(collect).toHaveBeenNthCalledWith(1, { + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionDismiss", + documentMayUnload: true, + }); + }); + it("collects separate dismiss events for each distinct proposition", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + command.run({ + surface: "web://mywebsite.com/my-cards", + callback, + }); + refresh(PROPOSITIONS); + const [{ propositions = [] }, collectEvent] = callback.mock.calls[0]; + collectEvent(PropositionEventType.DISMISS, [propositions[0]]); + expect(collect).toHaveBeenCalledWith({ + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionDismiss", + documentMayUnload: true, + }); + collectEvent(PropositionEventType.DISMISS, [propositions[0]]); + expect(collect).toHaveBeenCalledWith({ + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionDismiss", + documentMayUnload: true, + }); + expect(collect).toHaveBeenCalledTimes(2); + }); + it("collects multiple dismiss events for distinct propositions", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + command.run({ + surface: "web://mywebsite.com/my-cards", + callback, + }); + refresh(PROPOSITIONS); + const [{ propositions = [] }, collectEvent] = callback.mock.calls[0]; + collectEvent(PropositionEventType.DISMISS, [ + propositions[0], + propositions[1], + ]); + expect(collect).toHaveBeenNthCalledWith(1, { + decisionsMeta: [ + { + id: "abc", + scope: "web://something.com", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scope: "web://mywebsite.com/my-cards", + scopeDetails: { + decisionProvider: "AJO", + }, + }, + ], + eventType: "decisioning.propositionDismiss", + documentMayUnload: true, + }); + }); + it("does not call collect event when there are no propositions", () => { + const { command, refresh } = subscribeRulesetItems; + const callback = vi.fn(); + command.run({ + surfaces: ["web://mywebsite.com/my-cards"], + schemas: [MESSAGE_CONTENT_CARD], + callback, + }); + refresh([]); + const [{ propositions = [] }, collectEvent] = callback.mock.calls[0]; + collectEvent(PropositionEventType.DISPLAY, propositions); + expect(collect).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/decisioningContext.browser.spec.js b/vtest/unit/specs/components/RulesEngine/decisioningContext.browser.spec.js new file mode 100644 index 000000000..8965d8ed7 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/decisioningContext.browser.spec.js @@ -0,0 +1,56 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import { + mockWindow, + setupResponseHandler, + proposition, +} from "./contextTestUtils.js"; + +describe("RulesEngine:globalContext:browser", () => { + let applyResponse; + beforeEach(() => { + applyResponse = vi.fn(); + }); + it("satisfies rule based on matched browser", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "browser.name", + matcher: "eq", + values: ["chrome"], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched browser", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "browser.name", + matcher: "co", + values: ["Edge"], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/decisioningContext.page.spec.js b/vtest/unit/specs/components/RulesEngine/decisioningContext.page.spec.js new file mode 100644 index 000000000..1131b7f71 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/decisioningContext.page.spec.js @@ -0,0 +1,337 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import { + mockWindow, + setupResponseHandler, + proposition, +} from "./contextTestUtils.js"; + +describe("RulesEngine:globalContext:page", () => { + let applyResponse; + beforeEach(() => { + applyResponse = vi.fn(); + }); + it("satisfies rule based on matched page url", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", + }), + { + definition: { + key: "page.url", + matcher: "eq", + values: [ + "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", + ], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched page url", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", + }), + { + definition: { + key: "page.url", + matcher: "eq", + values: [ + "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", + ], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfy rule based on matched domain", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", + }), + { + definition: { + key: "page.domain", + matcher: "eq", + values: ["pro.mywebsite.org"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched domain", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", + }), + { + definition: { + key: "page.domain", + matcher: "eq", + values: ["pro.mywebsite.com"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfied rule based on matched page subdomain", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", + }), + { + definition: { + key: "page.subdomain", + matcher: "eq", + values: ["pro"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + // Note that we have custom parse url [refer to implementation] which will give empty string in case of www + it("does not satisfy rule due to unmatched page subdomain", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://www.mywebsite.org:8080/about?m=1&t=5&name=bob#home", + }), + { + definition: { + key: "page.subdomain", + matcher: "eq", + values: ["www"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched page topLevelDomain", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", + }), + { + definition: { + key: "page.topLevelDomain", + matcher: "eq", + values: ["org"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched page topLevelDomain", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", + }), + { + definition: { + key: "page.topLevelDomain", + matcher: "eq", + values: ["com"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched page path", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", + }), + { + definition: { + key: "page.path", + matcher: "eq", + values: ["/about"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched page path", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", + }), + { + definition: { + key: "page.path", + matcher: "eq", + values: ["/home"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched page query", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", + }), + { + definition: { + key: "page.query", + matcher: "co", + values: ["name=bob"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched page query", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", + }), + { + definition: { + key: "page.query", + matcher: "co", + values: ["name=bob"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched page fragment", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", + }), + { + definition: { + key: "page.fragment", + matcher: "eq", + values: ["home"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched page fragment", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#about", + }), + { + definition: { + key: "page.fragment", + matcher: "eq", + values: ["home"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/decisioningContext.referringPage.spec.js b/vtest/unit/specs/components/RulesEngine/decisioningContext.referringPage.spec.js new file mode 100644 index 000000000..14228ea22 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/decisioningContext.referringPage.spec.js @@ -0,0 +1,278 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import { + mockWindow, + setupResponseHandler, + proposition, +} from "./contextTestUtils.js"; + +describe("RulesEngine:globalContext:referringPage", () => { + let applyResponse; + beforeEach(() => { + applyResponse = vi.fn(); + }); + it("satisfies rule based on matched domain", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + referrer: + "https://business.adobe.com/search?q=adobe+journey+optimizer&oq=adobe+journey+optimizer#home", + }), + { + definition: { + key: "referringPage.domain", + matcher: "eq", + values: ["business.adobe.com"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched domain", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + referrer: + "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", + }), + { + definition: { + key: "referringPage.domain", + matcher: "co", + values: ["business.adobe.com"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched referringPage subdomain", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + referrer: + "https://business.adobe.com/search?q=adobe+journey+optimizer&oq=adobe+journey+optimizer#home", + }), + { + definition: { + key: "referringPage.subdomain", + matcher: "co", + values: ["business"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched subdomain", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + referrer: + "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", + }), + { + definition: { + key: "referringPage.subdomain", + matcher: "eq", + values: ["business"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched referringPage topLevelDomain", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "referringPage.topLevelDomain", + matcher: "eq", + values: ["com"], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched topLevelDomain", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + referrer: + "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", + }), + { + definition: { + key: "referringPage.topLevelDomain", + matcher: "eq", + values: ["com"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched referringPage path", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "referringPage.path", + matcher: "co", + values: ["/search"], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched referringPage path", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + referrer: + "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", + }), + { + definition: { + key: "referringPage.path", + matcher: "co", + values: ["/search"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched referringPage query", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "referringPage.query", + matcher: "co", + values: ["q=adobe+journey+optimizer&oq=adobe+journey+optimizer"], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched referringPage query", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + referrer: + "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", + }), + { + definition: { + key: "referringPage.query", + matcher: "co", + values: ["q=adobe+journey+optimizer&oq=adobe+journey+optimizer"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched referringPage fragment", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + referrer: + "https://business.adobe.com/search?q=adobe+journey+optimizer&oq=adobe+journey+optimizer#home", + }), + { + definition: { + key: "referringPage.fragment", + matcher: "co", + values: ["home"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule based on unmatched referringPage fragment", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + referrer: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#about", + }), + { + definition: { + key: "referringPage.fragment", + matcher: "co", + values: ["home"], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/decisioningContext.sdkVersion.spec.js b/vtest/unit/specs/components/RulesEngine/decisioningContext.sdkVersion.spec.js new file mode 100644 index 000000000..8b88b55e4 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/decisioningContext.sdkVersion.spec.js @@ -0,0 +1,58 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import libraryVersion from "../../../../../src/constants/libraryVersion.js"; +import { + mockWindow, + setupResponseHandler, + proposition, +} from "./contextTestUtils.js"; + +describe("RulesEngine:globalContext:sdkVersion", () => { + let applyResponse; + const currentVersion = libraryVersion; + beforeEach(() => { + applyResponse = vi.fn(); + }); + it("satisfies rule based on matched alloy sdk version", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "~sdkver", + matcher: "eq", + values: [currentVersion], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched dk version", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "~sdkver", + matcher: "eq", + values: ["2.18.0-beta.0"], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/decisioningContext.timestamp.spec.js b/vtest/unit/specs/components/RulesEngine/decisioningContext.timestamp.spec.js new file mode 100644 index 000000000..47e559dd9 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/decisioningContext.timestamp.spec.js @@ -0,0 +1,353 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + mockWindow, + setupResponseHandler, + proposition, +} from "./contextTestUtils.js"; + +let mockedTimestamp; +describe("RulesEngine:globalContext:timeContext", () => { + let applyResponse; + beforeEach(() => { + applyResponse = vi.fn(); + mockedTimestamp = new Date(Date.UTC(2023, 4, 11, 13, 34, 56)); + vi.useFakeTimers(); + vi.setSystemTime(mockedTimestamp); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("satisfies rule based on matched pageLoadTimestamp", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "pageLoadTimestamp", + matcher: "eq", + values: [mockedTimestamp.getTime()], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched pageLoadTimestamp", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "pageLoadTimestamp", + matcher: "eq", + values: [mockedTimestamp.getTime() + 1], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched currentTimestamp", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "currentTimestamp", + matcher: "eq", + values: [mockedTimestamp.getTime()], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched currentTimestamp", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "currentTimestamp", + matcher: "eq", + values: [mockedTimestamp.getTime() + 1], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched currentDate", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "currentDate", + matcher: "eq", + values: [mockedTimestamp.getDate()], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched currentDate", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "currentDate", + matcher: "eq", + values: [mockedTimestamp.getDate() + 1], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched currentDay", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "~state.com.adobe.module.lifecycle/lifecyclecontextdata.dayofweek", + matcher: "eq", + values: [mockedTimestamp.getDay() + 1], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched currentDay", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "~state.com.adobe.module.lifecycle/lifecyclecontextdata.dayofweek", + matcher: "eq", + values: [mockedTimestamp.getDay()], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched currentHour", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "~state.com.adobe.module.lifecycle/lifecyclecontextdata.hourofday", + matcher: "eq", + values: [mockedTimestamp.getHours()], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched currentHour", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "~state.com.adobe.module.lifecycle/lifecyclecontextdata.hourofday", + matcher: "eq", + values: [mockedTimestamp.getHours() + 1], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched currentMinute", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "currentMinute", + matcher: "eq", + values: [mockedTimestamp.getMinutes()], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched currentMinute", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "currentMinute", + matcher: "eq", + values: [mockedTimestamp.getMinutes() + 1], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched currentMonth", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "currentMonth", + matcher: "eq", + values: [mockedTimestamp.getMonth()], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched currentMonth", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "currentMonth", + matcher: "eq", + values: [mockedTimestamp.getMonth() + 1], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched currentYear", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "currentYear", + matcher: "eq", + values: [mockedTimestamp.getFullYear()], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched currentYear", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "currentYear", + matcher: "eq", + values: [mockedTimestamp.getFullYear() + 1], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched pageVisitDuration", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "pageVisitDuration", + matcher: "eq", + values: [0], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched pageVisitDuration", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "pageVisitDuration", + matcher: "eq", + values: [1], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched ~timestampu", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "~timestampu", + matcher: "eq", + values: [mockedTimestamp.getTime() / 1000], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("satisfies rule based on matched ~timestampz", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "~timestampz", + matcher: "eq", + values: [mockedTimestamp.toISOString()], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/decisioningContext.window.spec.js b/vtest/unit/specs/components/RulesEngine/decisioningContext.window.spec.js new file mode 100644 index 000000000..fc6eb8983 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/decisioningContext.window.spec.js @@ -0,0 +1,182 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import { + mockWindow, + setupResponseHandler, + proposition, +} from "./contextTestUtils.js"; + +describe("RulesEngine:globalContext:window", () => { + let applyResponse; + beforeEach(() => { + applyResponse = vi.fn(); + }); + it("satisfies rule based on matched window height", () => { + setupResponseHandler(applyResponse, mockWindow({}), { + definition: { + key: "window.height", + matcher: "gt", + values: [90], + }, + type: "matcher", + }); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched window height", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + height: 50, + }), + { + definition: { + key: "window.height", + matcher: "gt", + values: [90], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched window width", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + width: 200, + }), + { + definition: { + key: "window.width", + matcher: "gt", + values: [90], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched window width", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + width: 50, + }), + { + definition: { + key: "window.width", + matcher: "gt", + values: [90], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched window scrollX", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + scrollX: 200, + }), + { + definition: { + key: "window.scrollX", + matcher: "gt", + values: [90], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [proposition], + }), + ); + }); + it("does not satisfy rule due to unmatched window scrollX", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + scrollX: 50, + }), + { + definition: { + key: "window.scrollX", + matcher: "gt", + values: [90], + }, + type: "matcher", + }, + ); + expect(applyResponse).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + propositions: [], + }), + ); + }); + it("satisfies rule based on matched window scrollY", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + scrollY: 200, + }), + { + definition: { + key: "window.scrollY", + matcher: "gt", + values: [90], + }, + type: "matcher", + }, + ); + }); + it("does not satisfy rule due to unmatched window scrollY", () => { + setupResponseHandler( + applyResponse, + mockWindow({ + scrollY: 50, + }), + { + definition: { + key: "window.scrollY", + matcher: "gt", + values: [90], + }, + type: "matcher", + }, + ); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/index.spec.js b/vtest/unit/specs/components/RulesEngine/index.spec.js new file mode 100644 index 000000000..1719604d9 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/index.spec.js @@ -0,0 +1,210 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createRulesEngine from "../../../../../src/components/RulesEngine/index.js"; +import { defer } from "../../../../../src/utils/index.js"; +import { + mockRulesetResponseWithCondition, + proposition, +} from "./contextTestUtils.js"; + +describe("createRulesEngine:commands:evaluateRulesets", () => { + let mergeData; + let mockEvent; + let onResponseHandler; + let awaitConsentDeferred; + let consent; + let getBrowser; + let persistentStorage; + let createNamespacedStorage; + beforeEach(() => { + mergeData = vi.fn(); + awaitConsentDeferred = defer(); + consent = { + awaitConsent: vi.fn().mockReturnValue(awaitConsentDeferred.promise), + }; + getBrowser = vi.fn().mockReturnValue("foo"); + window.referrer = + "https://www.google.com/search?q=adobe+journey+optimizer&oq=adobe+journey+optimizer"; + persistentStorage = { + getItem: vi.fn(), + setItem: vi.fn(), + clear: vi.fn(), + }; + createNamespacedStorage = vi.fn().mockReturnValue({ + persistent: persistentStorage, + }); + mockEvent = { + getContent: () => ({}), + hasQuery: () => true, + getViewName: () => undefined, + mergeData, + }; + }); + const setUpDecisionEngine = ({ personalizationStorageEnabled }) => { + const config = { + orgId: "exampleOrgId", + personalizationStorageEnabled, + }; + const rulesEngine = createRulesEngine({ + config, + createNamespacedStorage, + consent, + getBrowser, + }); + rulesEngine.lifecycle.onComponentsRegistered(() => {}); + return rulesEngine; + }; + it("should run the evaluateRulesets command and satisfy the rule based on global context", async () => { + const rulesEngine = setUpDecisionEngine({ + personalizationStorageEnabled: true, + }); + await awaitConsentDeferred.resolve(); + onResponseHandler = (onResponse) => { + onResponse({ + response: mockRulesetResponseWithCondition({ + definition: { + key: "referringPage.path", + matcher: "eq", + values: ["/search"], + }, + type: "matcher", + }), + }); + }; + rulesEngine.lifecycle.onBeforeEvent({ + event: mockEvent, + renderDecisions: true, + personalization: { + decisionContext: {}, + }, + onResponse: onResponseHandler, + }); + const result = rulesEngine.commands.evaluateRulesets.run({}); + expect(result).toEqual({ + propositions: [proposition], + }); + }); + it("should run the evaluateRulesets command and does not satisfy rule due to unmatched global context", async () => { + const rulesEngine = setUpDecisionEngine({ + personalizationStorageEnabled: true, + }); + await awaitConsentDeferred.resolve(); + onResponseHandler = (onResponse) => { + onResponse({ + response: mockRulesetResponseWithCondition({ + definition: { + key: "referringPage.path", + matcher: "eq", + values: ["/about"], + }, + type: "matcher", + }), + }); + }; + rulesEngine.lifecycle.onBeforeEvent({ + event: mockEvent, + renderDecisions: true, + personalization: { + decisionContext: {}, + }, + onResponse: onResponseHandler, + }); + const result = rulesEngine.commands.evaluateRulesets.run({}); + expect(result).toEqual({ + propositions: [], + }); + }); + it("should run the evaluateRulesets command and return propositions with renderDecisions true", async () => { + const rulesEngine = setUpDecisionEngine({ + personalizationStorageEnabled: true, + }); + await awaitConsentDeferred.resolve(); + onResponseHandler = (onResponse) => { + onResponse({ + response: mockRulesetResponseWithCondition({ + definition: { + key: "referringPage.path", + matcher: "eq", + values: ["/search"], + }, + type: "matcher", + }), + }); + }; + rulesEngine.lifecycle.onBeforeEvent({ + event: mockEvent, + renderDecisions: true, + personalization: { + decisionContext: {}, + }, + onResponse: onResponseHandler, + }); + const result = rulesEngine.commands.evaluateRulesets.run({}); + expect(result).toEqual({ + propositions: [proposition], + }); + }); + it("should run the evaluateRulesets command returns propositions with renderDecisions false", async () => { + const rulesEngine = setUpDecisionEngine({ + personalizationStorageEnabled: true, + }); + await awaitConsentDeferred.resolve(); + onResponseHandler = (onResponse) => { + onResponse({ + response: mockRulesetResponseWithCondition({ + definition: { + key: "referringPage.path", + matcher: "eq", + values: ["/search"], + }, + type: "matcher", + }), + }); + }; + rulesEngine.lifecycle.onBeforeEvent({ + event: mockEvent, + renderDecisions: false, + personalization: { + decisionContext: {}, + }, + onResponse: onResponseHandler, + }); + const result = rulesEngine.commands.evaluateRulesets.run({}); + expect(result).toEqual({ + propositions: [proposition], + }); + }); + it("should clear the local storage when personalizationStorageEnabled is false", async () => { + setUpDecisionEngine({ + personalizationStorageEnabled: false, + }); + await awaitConsentDeferred.resolve(); + expect(persistentStorage.clear).toHaveBeenCalled(); + }); + it("should set eventRegistry storage when consent is obtained", async () => { + setUpDecisionEngine({ + personalizationStorageEnabled: true, + }); + await awaitConsentDeferred.resolve(); + await expect(awaitConsentDeferred.promise).resolves.toBe(undefined); + expect(persistentStorage.getItem).toHaveBeenCalled(); + }); + it("should clear the local storage when consent is not obtained", async () => { + setUpDecisionEngine({ + personalizationStorageEnabled: true, + }); + await awaitConsentDeferred.reject(); + await expect(awaitConsentDeferred.promise).rejects.toBe(undefined); + expect(persistentStorage.clear).toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/RulesEngine/utils.spec.js b/vtest/unit/specs/components/RulesEngine/utils.spec.js new file mode 100644 index 000000000..600b63df2 --- /dev/null +++ b/vtest/unit/specs/components/RulesEngine/utils.spec.js @@ -0,0 +1,232 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import { + createInMemoryStorage, + createRestoreStorage, + createSaveStorage, + getActivityId, + getExpirationDate, +} from "../../../../../src/components/RulesEngine/utils.js"; + +describe("RulesEngine:utils", () => { + let storage; + let inMemoryStorage; + beforeEach(() => { + storage = { + getItem: vi.fn(), + setItem: vi.fn(), + clear: vi.fn(), + }; + inMemoryStorage = createInMemoryStorage(); + }); + it("restores from storage", () => { + storage.getItem.mockReturnValue( + '{ "something": true, "color": "orange", "person": { "height": 5.83 } }', + ); + const restore = createRestoreStorage(storage, "zoink"); + expect( + restore({ + good: true, + }), + ).toEqual({ + something: true, + color: "orange", + person: { + height: 5.83, + }, + }); + expect(storage.getItem).toHaveBeenCalledWith("zoink"); + }); + it("uses default value if storage unavailable", () => { + storage.getItem.mockReturnValue(undefined); + const restore = createRestoreStorage(storage, "zoink"); + expect( + restore({ + good: true, + }), + ).toEqual({ + good: true, + }); + expect(storage.getItem).toHaveBeenCalledWith("zoink"); + }); + it("saves to storage", () => { + const mockedTimestamp = new Date(Date.UTC(2023, 8, 2, 13, 34, 56)); + vi.useFakeTimers(); + vi.setSystemTime(mockedTimestamp); + + storage.getItem.mockReturnValue( + '{ "something": true, "color": "orange", "person": { "height": 5.83 } }', + ); + const save = createSaveStorage(storage, "zoink"); + save({ + something: true, + color: "orange", + person: { + height: 5.83, + }, + }); + + vi.advanceTimersByTime(60); + + expect(storage.setItem).toHaveBeenCalledWith( + "zoink", + '{"something":true,"color":"orange","person":{"height":5.83}}', + ); + + vi.useRealTimers(); + }); + it("should return the date of expiration", () => { + const mockedTimestamp = new Date(Date.UTC(2023, 8, 2, 13, 34, 56)); + vi.useFakeTimers(); + vi.setSystemTime(mockedTimestamp); + + const retentionPeriod = 10; + const expectedDate = new Date(mockedTimestamp); + expectedDate.setDate(expectedDate.getDate() - retentionPeriod); + const result = getExpirationDate(retentionPeriod); + expect(result).toEqual(expectedDate); + + vi.useRealTimers(); + }); + it("should return the activityId", () => { + const proposition = { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scope: "web://mywebsite.com", + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + eventToken: "abc", + }, + strategies: [ + { + strategyID: "3VQe3oIqiYq2RAsYzmDTSf", + treatmentID: "yu7rkogezumca7i0i44v", + }, + ], + activity: { + id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", + }, + correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", + }, + items: [ + { + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + schema: "https://ns.adobe.com/personalization/ruleset-item", + data: { + version: 1, + rules: [ + { + condition: { + definition: { + conditions: [ + { + definition: { + conditions: [ + { + definition: { + key: "color", + matcher: "eq", + values: ["orange", "blue"], + }, + type: "matcher", + }, + { + definition: { + key: "action", + matcher: "eq", + values: ["lipstick"], + }, + type: "matcher", + }, + ], + logic: "and", + }, + type: "group", + }, + ], + logic: "and", + }, + type: "group", + }, + consequences: [ + { + type: "schema", + detail: { + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + selector: + "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", + type: "setAttribute", + content: { + src: "img/demo-marketing-offer1-exp-A.png", + }, + prehidingSelector: + "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", + }, + ], + }, + ], + }, + }, + ], + }; + expect(getActivityId(proposition)).toEqual( + "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", + ); + }); + it("should return the activityId as undefined", () => { + const proposition = { + id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", + scope: "web://mywebsite.com", + scopeDetails: { + decisionProvider: "AJO", + characteristics: { + eventToken: "abc", + }, + strategies: [ + { + strategyID: "3VQe3oIqiYq2RAsYzmDTSf", + treatmentID: "yu7rkogezumca7i0i44v", + }, + ], + correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", + }, + }; + expect(getActivityId(proposition)).toEqual(undefined); + }); + it("should set and retrieve an item from in-memory storage", () => { + const key = "testKey"; + const value = "testValue"; + inMemoryStorage.setItem(key, value); + const retrievedValue = inMemoryStorage.getItem(key); + expect(retrievedValue).toEqual(value); + }); + it("should return null for a non-existent item", () => { + const key = "nonExistentKey"; + const retrievedValue = inMemoryStorage.getItem(key); + expect(retrievedValue).toBeNull(); + }); + it("should overwrite the value for an existing key", () => { + const key = "existingKey"; + const originalValue = "originalValue"; + const updatedValue = "updatedValue"; + inMemoryStorage.setItem(key, originalValue); + inMemoryStorage.setItem(key, updatedValue); + const retrievedValue = inMemoryStorage.getItem(key); + expect(retrievedValue).toEqual(updatedValue); + }); +}); diff --git a/vtest/unit/specs/components/StreamingMedia/configValidators.spec.js b/vtest/unit/specs/components/StreamingMedia/configValidators.spec.js new file mode 100644 index 000000000..488a053a6 --- /dev/null +++ b/vtest/unit/specs/components/StreamingMedia/configValidators.spec.js @@ -0,0 +1,75 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import configValidators from "../../../../../src/components/StreamingMedia/configValidators.js"; +import testConfigValidators from "../../../helpers/testConfigValidators.js"; + +describe("Streaming Media config validators", () => { + testConfigValidators({ + configValidators, + validConfigurations: [ + {}, + { + streamingMedia: { + channel: "test-channel", + playerName: "test-player-name", + }, + }, + { + streamingMedia: { + channel: "test-channel", + playerName: "test-player-name", + appVersion: "test-app-version", + }, + }, + { + streamingMedia: { + channel: "test-channel", + playerName: "test-player-name", + appVersion: "test-app-version", + mainPingInterval: 10, + adPingInterval: 1, + }, + }, + ], + invalidConfigurations: [ + { + streamingMedia: "", + }, + { + streamingMedia: {}, + }, + { + streamingMedia: { + channel: "test-channel", + }, + }, + { + streamingMedia: { + playerName: "test-player-name", + }, + }, + ], + defaultValues: {}, + }); + it("provides default values when Streaming media configured", () => { + const config = configValidators({ + streamingMedia: { + channel: "test-channel", + playerName: "test-player-name", + }, + }); + expect(config.streamingMedia.adPingInterval).toBe(10); + expect(config.streamingMedia.mainPingInterval).toBe(10); + }); +}); diff --git a/vtest/unit/specs/components/StreamingMedia/createMediaEventManager.spec.js b/vtest/unit/specs/components/StreamingMedia/createMediaEventManager.spec.js new file mode 100644 index 000000000..5675569c2 --- /dev/null +++ b/vtest/unit/specs/components/StreamingMedia/createMediaEventManager.spec.js @@ -0,0 +1,151 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +// tests for createMediaEventManager.js + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createMediaEventManager from "../../../../../src/components/StreamingMedia/createMediaEventManager.js"; + +describe("StreamingMedia::createMediaEventManager", () => { + let config; + let eventManager; + let consent; + let sendEdgeNetworkRequest; + let mediaEventManager; + let setTimestamp; + beforeEach(() => { + config = { + streamingMedia: { + playerName: "player1", + channel: "channel1", + version: "1.0.0", + }, + }; + eventManager = { + createEvent: vi.fn(), + sendEvent: vi.fn(), + }; + consent = { + awaitConsent: vi.fn(), + }; + sendEdgeNetworkRequest = vi.fn().mockReturnValue(Promise.resolve()); + setTimestamp = vi.fn(); + mediaEventManager = createMediaEventManager({ + config, + eventManager, + consent, + sendEdgeNetworkRequest, + setTimestamp, + }); + }); + it("should create a media event with user xdm", () => { + const options = { + xdm: {}, + }; + const event = { + setUserXdm: vi.fn(), + toJSON: () => ({ + a: 1, + }), + }; + eventManager.createEvent.mockReturnValue(event); + const result = mediaEventManager.createMediaEvent({ + options, + }); + expect(result.toJSON()).toEqual(event.toJSON()); + }); + it("should create a media session with player name, channel, and version", () => { + const options = { + xdm: { + mediaCollection: { + playerName: "player1", + channel: "channel1", + version: "1.0.0", + sessionDetails: {}, + }, + }, + }; + const event = { + setUserXdm: vi.fn(), + mergeXdm: vi.fn(), + toJSON: () => ({ + a: 1, + }), + }; + eventManager.createEvent.mockReturnValue(event); + const result = mediaEventManager.createMediaSession(options); + expect(result.toJSON()).toEqual(event.toJSON()); + }); + it("should augment media event with playhead, qoeDataDetails, and sessionID", () => { + const event = { + mergeXdm: vi.fn(), + }; + const playerId = "player1"; + const getPlayerDetails = vi.fn().mockReturnValue({ + playhead: 10, + qoeDataDetails: { + duration: 60, + }, + }); + const sessionID = "session1"; + const result = mediaEventManager.augmentMediaEvent({ + event, + playerId, + getPlayerDetails, + sessionID, + }); + expect(result).toBe(event); + expect(getPlayerDetails).toHaveBeenCalledWith({ + playerId, + }); + expect(event.mergeXdm).toHaveBeenCalledWith({ + mediaCollection: { + playhead: 10, + qoeDataDetails: { + duration: 60, + }, + sessionID: "session1", + }, + }); + }); + it("should track media session with event, playerId, and getPlayerDetails", () => { + const event = {}; + const playerId = "player1"; + const getPlayerDetails = () => {}; + const mediaOptions = { + playerId, + getPlayerDetails, + legacy: false, + }; + mediaEventManager.trackMediaSession({ + event, + mediaOptions, + }); + expect(eventManager.sendEvent).toHaveBeenCalledWith(event, { + mediaOptions, + edgeConfigOverrides: undefined, + }); + }); + it("should track media event with action and send request to Edge Network", async () => { + const event = { + finalize: vi.fn(), + }; + const action = "play"; + consent.awaitConsent.mockReturnValue(Promise.resolve()); + await mediaEventManager.trackMediaEvent({ + event, + action, + }); + expect(event.finalize).toHaveBeenCalled(); + expect(sendEdgeNetworkRequest).toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/StreamingMedia/createMediaRequest.spec.js b/vtest/unit/specs/components/StreamingMedia/createMediaRequest.spec.js new file mode 100644 index 000000000..403701f5d --- /dev/null +++ b/vtest/unit/specs/components/StreamingMedia/createMediaRequest.spec.js @@ -0,0 +1,29 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import createMediaRequest from "../../../../../src/components/StreamingMedia/createMediaRequest.js"; + +describe("StreamingMedia::createMediaRequest", () => { + it("should call createRequest with correct parameters", () => { + const mediaRequestPayload = {}; // replace with valid payload + const action = "testAction"; + const edgeSubPath = "/va"; + const result = createMediaRequest({ + mediaRequestPayload, + action, + }); + expect(result.getAction()).toEqual(action); + expect(result.getEdgeSubPath()).toEqual(edgeSubPath); + expect(result.getUseSendBeacon()).toEqual(false); + }); +}); diff --git a/vtest/unit/specs/components/StreamingMedia/createMediaResponseHandler.spec.js b/vtest/unit/specs/components/StreamingMedia/createMediaResponseHandler.spec.js new file mode 100644 index 000000000..46bbffaf8 --- /dev/null +++ b/vtest/unit/specs/components/StreamingMedia/createMediaResponseHandler.spec.js @@ -0,0 +1,94 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createMediaResponseHandler from "../../../../../src/components/StreamingMedia/createMediaResponseHandler.js"; + +describe("createMediaResponseHandler", () => { + let trackMediaEvent; + let mediaSessionCacheManager; + let config; + let logger; + let mediaResponseHandler; + let response; + const getPlayerDetails = () => {}; + beforeEach(() => { + response = { + getPayloadsByType: vi.fn(), + }; + mediaSessionCacheManager = { + getSession: vi.fn().mockReturnValue({ + getPlayerDetails: vi.fn(), + sessionPromise: Promise.resolve({ + sessionId: "123", + }), + }), + stopPing: vi.fn(), + savePing: vi.fn(), + }; + config = { + streamingMedia: { + adPingInterval: 5, + mainPingInterval: 10, + }, + }; + logger = { + info: vi.fn(), + }; + trackMediaEvent = vi.fn(); + mediaResponseHandler = createMediaResponseHandler({ + mediaSessionCacheManager, + logger, + config, + trackMediaEvent, + }); + }); + it("should return empty object when no media payload", async () => { + response.getPayloadsByType.mockReturnValue([]); + const result = await mediaResponseHandler({ + response, + playerId: "player1", + getPlayerDetails, + }); + await expect(result).toEqual({}); + await expect(mediaSessionCacheManager.savePing).not.toHaveBeenCalled(); + }); + it("should return session id", async () => { + response.getPayloadsByType.mockReturnValue([ + { + sessionId: "123", + }, + ]); + const result = await mediaResponseHandler({ + response, + playerId: "player1", + getPlayerDetails, + }); + await expect(result).toEqual({ + sessionId: "123", + }); + await expect(mediaSessionCacheManager.savePing).toHaveBeenCalled(); + }); + it("should return sessionId when no player or getPlayerDetails function", async () => { + response.getPayloadsByType.mockReturnValue([ + { + sessionId: "123", + }, + ]); + const result = await mediaResponseHandler({ + response, + }); + await expect(result).toEqual({ + sessionId: "123", + }); + await expect(mediaSessionCacheManager.savePing).not.toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/StreamingMedia/createMediaSessionCacheManager.spec.js b/vtest/unit/specs/components/StreamingMedia/createMediaSessionCacheManager.spec.js new file mode 100644 index 000000000..cf4cb69a0 --- /dev/null +++ b/vtest/unit/specs/components/StreamingMedia/createMediaSessionCacheManager.spec.js @@ -0,0 +1,77 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, describe, it, expect } from "vitest"; +import createMediaSessionCacheManager from "../../../../../src/components/StreamingMedia/createMediaSessionCacheManager.js"; + +describe("StreamingMedia::createMediaSessionCacheManager", () => { + let mediaSessionCacheManager; + beforeEach(() => { + mediaSessionCacheManager = createMediaSessionCacheManager(); + }); + it("getSession should return correct session", () => { + const playerId = "player1"; + const sessionDetails = { + id: "session1", + }; + mediaSessionCacheManager.storeSession({ + playerId, + sessionDetails, + }); + const result = mediaSessionCacheManager.getSession(playerId); + expect(result).toEqual(sessionDetails); + }); + it("stopPing should stop the Ping", () => { + const playerId = "player1"; + const sessionDetails = { + id: "session1", + pingId: 1, + }; + mediaSessionCacheManager.storeSession({ + playerId, + sessionDetails, + }); + const result = mediaSessionCacheManager.getSession(playerId); + mediaSessionCacheManager.stopPing({ + playerId, + }); + expect(result.pingId).toEqual(null); + }); + it("storeSession should store the session", () => { + const playerId = "player1"; + const sessionDetails = { + id: "session1", + }; + mediaSessionCacheManager.storeSession({ + playerId, + sessionDetails, + }); + const session = mediaSessionCacheManager.getSession(playerId); + expect(session).toEqual(sessionDetails); + }); + it("savePing should save the Ping", () => { + const playerId = "player1"; + const sessionDetails = { + id: "session1", + }; + mediaSessionCacheManager.storeSession({ + playerId, + sessionDetails, + }); + mediaSessionCacheManager.savePing({ + playerId, + pingId: 1, + }); + const session = mediaSessionCacheManager.getSession(playerId); + expect(session.pingId).toEqual(1); + }); +}); diff --git a/vtest/unit/specs/components/StreamingMedia/createStreamingMediaComponent.spec.js b/vtest/unit/specs/components/StreamingMedia/createStreamingMediaComponent.spec.js new file mode 100644 index 000000000..b89ecc329 --- /dev/null +++ b/vtest/unit/specs/components/StreamingMedia/createStreamingMediaComponent.spec.js @@ -0,0 +1,75 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createStreamingMediaComponent from "../../../../../src/components/StreamingMedia/createStreamingMediaComponent.js"; + +describe("StreamingMedia::createComponent", () => { + const config = { + streamingMedia: { + channel: "testChannel", + playerName: "testPlayerName", + appVersion: "testAppVersion", + }, + }; + let logger; + let mediaComponent; + let trackMediaEvent; + let mediaResponseHandler; + let trackMediaSession; + const build = (configs) => { + mediaComponent = createStreamingMediaComponent({ + config: configs, + logger, + trackMediaEvent, + mediaResponseHandler, + trackMediaSession, + }); + }; + beforeEach(() => { + logger = { + warn: vi.fn(), + }; + mediaResponseHandler = vi.fn(); + trackMediaEvent = vi.fn(); + trackMediaSession = vi.fn(); + build(config); + }); + it("should call trackSession when with invalid config", async () => { + build({}); + const options = { + playerId: "testPlayerId", + getPlayerDetails: () => {}, + xdm: { + mediaCollection: { + sessionDetails: { + playerName: "testPlayerName", + }, + }, + }, + }; + const createMediaSession = mediaComponent.commands.createMediaSession; + await createMediaSession.run(options); + expect(trackMediaSession).toHaveBeenCalled(); + }); + it("should not send media event if no valid configs", async () => { + build({}); + const options = { + playerId: "testPlayerId", + xdm: { + mediaCollection: {}, + }, + }; + const { sendMediaEvent } = mediaComponent.commands; + return expect(sendMediaEvent.run(options)).rejects.toThrowError(); + }); +}); diff --git a/vtest/unit/specs/components/StreamingMedia/createTrackMediaEvent.spec.js b/vtest/unit/specs/components/StreamingMedia/createTrackMediaEvent.spec.js new file mode 100644 index 000000000..f25094675 --- /dev/null +++ b/vtest/unit/specs/components/StreamingMedia/createTrackMediaEvent.spec.js @@ -0,0 +1,88 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createTrackMediaEvent from "../../../../../src/components/StreamingMedia/createTrackMediaEvent.js"; +import MediaEvents from "../../../../../src/components/StreamingMedia/constants/eventTypes.js"; + +describe("createTrackMediaEvent", () => { + let trackMediaEvent; + let mediaEventManager; + let mediaSessionCacheManager; + let config; + beforeEach(() => { + mediaEventManager = { + createMediaEvent: vi.fn(), + augmentMediaEvent: vi.fn(), + trackMediaEvent: vi.fn().mockReturnValue(Promise.resolve()), + }; + mediaSessionCacheManager = { + getSession: vi.fn().mockReturnValue({ + getPlayerDetails: vi.fn(), + sessionPromise: Promise.resolve({ + sessionId: "123", + }), + }), + stopPing: vi.fn(), + savePing: vi.fn(), + }; + config = { + streamingMedia: { + adPingInterval: 5, + mainPingInterval: 10, + }, + }; + trackMediaEvent = createTrackMediaEvent({ + mediaEventManager, + mediaSessionCacheManager, + config, + }); + }); + it("should send a media event", async () => { + const options = { + playerId: "player1", + xdm: { + eventType: "media.play", + }, + }; + await trackMediaEvent(options); + expect(mediaEventManager.createMediaEvent).toHaveBeenCalledWith({ + options, + }); + expect(mediaSessionCacheManager.getSession).toHaveBeenCalledWith( + options.playerId, + ); + expect(mediaEventManager.augmentMediaEvent).toHaveBeenCalled(); + expect(mediaEventManager.trackMediaEvent).toHaveBeenCalled(); + }); + it("should stop the Ping for session complete event", async () => { + const options = { + playerId: "player1", + xdm: { + eventType: MediaEvents.SESSION_COMPLETE, + }, + }; + await trackMediaEvent(options); + expect(mediaSessionCacheManager.stopPing).toHaveBeenCalledWith({ + playerId: options.playerId, + }); + }); + it("should save the Ping for non-session complete event", async () => { + const options = { + playerId: "player1", + xdm: { + eventType: "media.play", + }, + }; + await trackMediaEvent(options); + expect(mediaSessionCacheManager.savePing).toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/components/StreamingMedia/createTrackMediaSession.spec.js b/vtest/unit/specs/components/StreamingMedia/createTrackMediaSession.spec.js new file mode 100644 index 000000000..0867b2616 --- /dev/null +++ b/vtest/unit/specs/components/StreamingMedia/createTrackMediaSession.spec.js @@ -0,0 +1,125 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createTrackMediaSession from "../../../../../src/components/StreamingMedia/createTrackMediaSession.js"; +import PlaybackState from "../../../../../src/components/StreamingMedia/constants/playbackState.js"; + +describe("createTrackMediaSession", () => { + let trackMediaSession; + let mediaEventManager; + let mediaSessionCacheManager; + let config; + let logger; + beforeEach(() => { + logger = { + warn: vi.fn(), + }; + mediaEventManager = { + createMediaSession: vi.fn(), + augmentMediaEvent: vi.fn(), + trackMediaSession: vi.fn().mockReturnValue(Promise.resolve()), + }; + mediaSessionCacheManager = { + storeSession: vi.fn(), + }; + config = { + streamingMedia: { + playerName: "testPlayerName", + channel: "testChannel", + adPingInterval: 5, + mainPingInterval: 10, + }, + }; + trackMediaSession = createTrackMediaSession({ + config, + logger, + mediaEventManager, + mediaSessionCacheManager, + }); + }); + it("should track a session", async () => { + const sessionPromise = Promise.resolve("123"); + const playerId = "testPlayerId"; + const playerName = "testPlayerName"; + const eventType = "media.sessionStart"; + const event = { + eventType, + }; + const getPlayerDetails = () => {}; + const options = { + playerId, + getPlayerDetails, + xdm: { + mediaCollection: { + sessionDetails: { + playerName, + }, + }, + }, + }; + mediaEventManager.createMediaSession.mockReturnValue({ + eventType, + }); + mediaEventManager.augmentMediaEvent.mockReturnValue({ + eventType, + xdm: { + mediaCollection: { + sessionDetails: { + playerName, + }, + playhead: 0, + }, + }, + }); + mediaEventManager.trackMediaSession.mockReturnValue(sessionPromise); + await trackMediaSession(options); + expect(mediaEventManager.createMediaSession).toHaveBeenCalledWith(options); + expect(mediaEventManager.augmentMediaEvent).toHaveBeenCalledWith({ + event, + playerId, + getPlayerDetails, + }); + expect(mediaEventManager.trackMediaSession).toHaveBeenCalledWith({ + event, + mediaOptions: { + playerId, + getPlayerDetails, + legacy: false, + }, + edgeConfigOverrides: undefined, + }); + expect(mediaSessionCacheManager.storeSession).toHaveBeenCalledWith({ + playerId, + sessionDetails: { + sessionPromise, + getPlayerDetails, + playbackState: PlaybackState.MAIN, + }, + }); + }); + it("should not track session when no valid configs", async () => { + config = {}; + trackMediaSession = createTrackMediaSession({ + config, + mediaEventManager, + mediaSessionCacheManager, + }); + const options = { + playerId: "player1", + xdm: { + eventType: "media.sessionStart", + }, + getPlayerDetails: "", + }; + return expect(trackMediaSession(options)).rejects.toThrowError(); + }); +}); diff --git a/vtest/unit/specs/components/StreamingMedia/validateMediaEventOptions.spec.js b/vtest/unit/specs/components/StreamingMedia/validateMediaEventOptions.spec.js new file mode 100644 index 000000000..7aae1cfc3 --- /dev/null +++ b/vtest/unit/specs/components/StreamingMedia/validateMediaEventOptions.spec.js @@ -0,0 +1,66 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import validateMediaEventOptions from "../../../../../src/components/StreamingMedia/validateMediaEventOptions.js"; + +describe("StreamingMedia::validateMediaEventOptions", () => { + it("should not fail when payerId and xdm are used", () => { + const options = { + playerId: "playerId", + xdm: { + eventType: "media.play", + mediaCollection: { + playhead: 0, + sessionID: "sessionID", + }, + }, + }; + expect(() => { + validateMediaEventOptions({ + options, + }); + }).not.toThrowError(); + }); + it("should not fail when xdm with playhead is used", () => { + const options = { + xdm: { + eventType: "media.play", + mediaCollection: { + playhead: 0, + sessionID: "sessionID", + }, + }, + }; + expect(() => { + validateMediaEventOptions({ + options, + }); + }).not.toThrowError(); + }); + it("should throw an error when invalid options are passed", () => { + const options = { + xdm: { + eventType: "media.play", + mediaCollection: { + playhead: "0", + sessionID: "sessionID", + }, + }, + }; + expect(() => { + validateMediaEventOptions({ + options, + }); + }).toThrowError(); + }); +}); diff --git a/vtest/unit/specs/components/StreamingMedia/validateMediaSessionOptions.spec.js b/vtest/unit/specs/components/StreamingMedia/validateMediaSessionOptions.spec.js new file mode 100644 index 000000000..2d5e2cc60 --- /dev/null +++ b/vtest/unit/specs/components/StreamingMedia/validateMediaSessionOptions.spec.js @@ -0,0 +1,66 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import validateMediaSessionOptions from "../../../../../src/components/StreamingMedia/validateMediaSessionOptions.js"; + +describe("StreamingMedia::validateMediaSessionOptions", () => { + it("should not fail when playerId, callback and xdm are used", () => { + const options = { + playerId: "playerId", + getPlayerDetails: () => {}, + xdm: { + eventType: "eventType", + mediaCollection: { + sessionDetails: {}, + }, + }, + }; + expect(() => { + validateMediaSessionOptions({ + options, + }); + }).not.toThrowError(); + }); + it("should not fail when playerId, callback and xdm are used", () => { + const options = { + xdm: { + eventType: "eventType", + mediaCollection: { + playhead: 0, + sessionDetails: {}, + }, + }, + }; + expect(() => { + validateMediaSessionOptions({ + options, + }); + }).not.toThrowError(); + }); + it("should throw an error when invalid options are passed", () => { + const options = { + xdm: { + eventType: "eventType", + mediaCollection: { + playhead: "0", + sessionID: "sessionID", + }, + }, + }; + expect(() => { + validateMediaSessionOptions({ + options, + }); + }).toThrowError(); + }); +}); diff --git a/vtest/unit/specs/core/buildAndValidateConfig.spec.js b/vtest/unit/specs/core/buildAndValidateConfig.spec.js new file mode 100644 index 000000000..182b23e98 --- /dev/null +++ b/vtest/unit/specs/core/buildAndValidateConfig.spec.js @@ -0,0 +1,118 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import buildAndValidateConfig from "../../../../src/core/buildAndValidateConfig.js"; +import createConfig from "../../../../src/core/config/createConfig.js"; +import { boolean, objectOf } from "../../../../src/utils/validation/index.js"; + +describe("buildAndValidateConfig", () => { + let options; + let componentCreators; + let coreConfigValidators; + let logger; + let setDebugEnabled; + beforeEach(() => { + options = {}; + const componentCreator = () => {}; + componentCreator.configValidators = objectOf({ + idSyncEnabled: boolean().default(true), + }); + componentCreators = [componentCreator]; + coreConfigValidators = objectOf({ + debugEnabled: boolean().default(false), + errorsEnabled: boolean(), + }) + .noUnknownFields() + .required(); + logger = { + enabled: false, + info: vi.fn(), + logOnBeforeCommand: vi.fn(), + logOnInstanceConfigured: vi.fn(), + }; + setDebugEnabled = vi.fn(); + }); + it("adds validators and validates options", () => { + expect(() => { + buildAndValidateConfig({ + options: { + idSyncEnabled: "invalid value", + }, + componentCreators, + coreConfigValidators, + createConfig, + logger, + setDebugEnabled, + }); + }).toThrowError(); + }); + it("sets debug enabled based on config", () => { + options.debugEnabled = true; + buildAndValidateConfig({ + options, + componentCreators, + coreConfigValidators, + createConfig, + logger, + setDebugEnabled, + }); + expect(setDebugEnabled).toHaveBeenCalledWith(true, { + fromConfig: true, + }); + }); + it("logs and returns computed configuration", () => { + logger.enabled = true; + buildAndValidateConfig({ + options, + componentCreators, + coreConfigValidators, + createConfig, + logger, + setDebugEnabled, + }); + expect(logger.logOnInstanceConfigured).toHaveBeenCalledWith({ + config: { + debugEnabled: false, + idSyncEnabled: true, + }, + }); + }); + it("throws an error for unknown fields", () => { + logger.enabled = true; + options.foo = "bar"; + expect(() => + buildAndValidateConfig({ + options, + componentCreators, + coreConfigValidators, + createConfig, + logger, + setDebugEnabled, + }), + ).toThrowError(); + }); + it("returns config", () => { + const result = buildAndValidateConfig({ + options, + componentCreators, + coreConfigValidators, + createConfig, + logger, + setDebugEnabled, + }); + expect(result).toEqual({ + idSyncEnabled: true, + debugEnabled: false, + }); + }); +}); diff --git a/vtest/unit/specs/core/componentCreators.spec.js b/vtest/unit/specs/core/componentCreators.spec.js new file mode 100644 index 000000000..7f916d072 --- /dev/null +++ b/vtest/unit/specs/core/componentCreators.spec.js @@ -0,0 +1,32 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import * as componentCreators from "../../../../src/core/componentCreators.js"; + +describe("componentCreators", () => { + it("is an object of component creators", () => { + const c = Object.keys(componentCreators).reduce((acc, key) => { + acc.push(componentCreators[key]); + return acc; + }, []); + expect(c).toEqual(expect.any(Array)); + c.forEach((componentCreator) => { + expect(componentCreator).toEqual(expect.any(Function)); + expect(componentCreator.namespace).toEqual(expect.any(String)); + if (componentCreator.configValidators) { + // should export a validator function + expect(componentCreator.configValidators).toEqual(expect.any(Function)); + } + }); + }); +}); diff --git a/vtest/unit/specs/core/config/createConfig.spec.js b/vtest/unit/specs/core/config/createConfig.spec.js new file mode 100644 index 000000000..3a6fe2cda --- /dev/null +++ b/vtest/unit/specs/core/config/createConfig.spec.js @@ -0,0 +1,61 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, describe, it, expect } from "vitest"; +import createConfig from "../../../../../src/core/config/createConfig.js"; + +let testConfig = {}; +describe("createConfig", () => { + beforeEach(() => { + testConfig = { + a: 123, + b: "abc", + c: { + a1: "xyz", + }, + neg: { + neg: false, + }, + }; + }); + it("supports being instantiated with a config", () => { + const cfg = createConfig(testConfig); + expect(cfg.a).toEqual(123); + }); + it("supports getting a value assigned to a key", () => { + const cfg = createConfig(testConfig); + expect(cfg.a).toEqual(123); + }); + it("returns undefined when a missing key is requested", () => { + const cfg = createConfig(testConfig); + expect(cfg.missing).toBe(undefined); + }); + it("supports adding a key value mapping", () => { + const cfg = createConfig(testConfig); + cfg.d = "ABC"; + expect(cfg.d).toEqual("ABC"); + }); + describe("changing config", () => { + it("does not change the provided options", () => { + const cfg = createConfig(testConfig); + cfg.d = "NEW VALUE"; + expect(testConfig.d).toBe(undefined); + }); + }); + describe("changing provided options", () => { + it("does not change the config", () => { + const cfg = createConfig(testConfig); + testConfig.a = 456; + expect(cfg.a).toBe(123); + }); + }); +}); diff --git a/vtest/unit/specs/core/config/createCoreConfigs.spec.js b/vtest/unit/specs/core/config/createCoreConfigs.spec.js new file mode 100644 index 000000000..6f1677ee4 --- /dev/null +++ b/vtest/unit/specs/core/config/createCoreConfigs.spec.js @@ -0,0 +1,161 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, describe, it, expect } from "vitest"; +import createCoreConfigs from "../../../../../src/core/config/createCoreConfigs.js"; + +describe("createCoreConfigs", () => { + let validator; + const baseConfig = { + datastreamId: "1234", + orgId: "org1", + }; + beforeEach(() => { + validator = createCoreConfigs(); + }); + describe("debugEnabled", () => { + it("validates debugEnabled=undefined", () => { + const config = validator(baseConfig); + expect(config.debugEnabled).toBe(false); + }); + it("validates debugEnabled=true", () => { + const config = validator({ + debugEnabled: true, + ...baseConfig, + }); + expect(config.debugEnabled).toBe(true); + }); + it("validates debugEnabled=false", () => { + const config = validator({ + debugEnabled: false, + ...baseConfig, + }); + expect(config.debugEnabled).toBe(false); + }); + it("validates debugEnabled=123", () => { + expect(() => { + validator({ + debugEnabled: 123, + ...baseConfig, + }); + }).toThrowError(); + }); + }); + [ + { + datastreamId: "asdfasdf", + orgId: "", + }, + { + datastreamId: "asdfasdf", + orgId: "", + }, + { + datastreamId: "myproperty1", + orgId: "53A16ACB5CC1D3760A495C99@AdobeOrg", + }, + { + datastreamId: "myproperty1", + orgId: "53A16ACB5CC1D3760A495C99@AdobeOrg", + }, + { + datastreamId: "myproperty1", + edgeDomain: "stats.firstparty.com", + orgId: "53A16ACB5CC1D3760A495C99@AdobeOrg", + }, + { + datastreamId: "myproperty1", + edgeDomain: "STATS.FIRSTPARTY.COM", + orgId: "53A16ACB5CC1D3760A495C99@AdobeOrg", + }, + { + datastreamId: "myproperty1", + edgeDomain: "STATS.FIRSTPARTY.COM", + orgId: "53A16ACB5CC1D3760A495C99@AdobeOrg", + }, + { + datastreamId: "myproperty1", + edgeDomain: "STATS.FIRSTPARTY.COM", + orgId: "53A16ACB5CC1D3760A495C99@AdobeOrg", + configurationOverrides: { + experience_platform: { + datasets: { + event: "werewr", + profile: "www", + }, + }, + }, + }, + ].forEach((cfg, i) => { + it(`validates configuration (${i})`, () => { + validator(cfg); + }); + }); + [ + {}, + { + datastreamId: "myproperty1", + edgeDomain: "", + }, + { + datastreamId: "myproperty1", + edgeDomain: "stats firstparty.com", + }, + { + datastreamId: "myproperty1", + edgeDomain: "stats firstparty.com", + prehidingStyle: "", + }, + { + datastreamId: "myproperty1", + edgeBasePath: 123, + }, + ].forEach((cfg, i) => { + it(`invalidates configuration (${i})`, () => { + expect(() => validator(cfg)).toThrowError(); + }); + }); + it("invalidates duplicate configIds", () => { + const config1 = { + datastreamId: "property1", + orgId: "ims1", + }; + const config2 = { + datastreamId: "property2", + orgId: "ims2", + }; + const config3 = { + datastreamId: "property1", + orgId: "ims3", + }; + validator(config1); + validator(config2); + expect(() => validator("", config3)).toThrowError(); + }); + it("invalidates duplicate orgIds", () => { + const config1 = { + datastreamId: "a", + orgId: "a", + }; + const config2 = { + datastreamId: "b", + orgId: "b", + }; + const config3 = { + datastreamId: "c", + orgId: "a", + }; + validator(config1); + validator(config2); + expect(() => validator("", config3)).toThrowError(); + }); +}); diff --git a/vtest/unit/specs/core/consent/createConsent.spec.js b/vtest/unit/specs/core/consent/createConsent.spec.js new file mode 100644 index 000000000..b7d9263aa --- /dev/null +++ b/vtest/unit/specs/core/consent/createConsent.spec.js @@ -0,0 +1,87 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createConsent from "../../../../../src/core/consent/createConsent.js"; + +describe("createConsent", () => { + let state; + let subject; + let logger; + beforeEach(() => { + state = { + in: vi.fn(), + out: vi.fn(), + pending: vi.fn(), + awaitConsent: vi.fn(), + withConsent: vi.fn(), + }; + logger = { + warn: vi.fn(), + }; + subject = createConsent({ + generalConsentState: state, + logger, + }); + }); + it("sets consent to in", () => { + subject.setConsent({ + general: "in", + }); + expect(state.in).toHaveBeenCalled(); + expect(state.out).not.toHaveBeenCalled(); + expect(state.pending).not.toHaveBeenCalled(); + expect(logger.warn).not.toHaveBeenCalled(); + }); + it("sets consent to out", () => { + subject.setConsent({ + general: "out", + }); + expect(state.in).not.toHaveBeenCalled(); + expect(state.out).toHaveBeenCalled(); + expect(state.pending).not.toHaveBeenCalled(); + expect(logger.warn).not.toHaveBeenCalled(); + }); + it("sets consent to pending", () => { + subject.setConsent({ + general: "pending", + }); + expect(state.in).not.toHaveBeenCalled(); + expect(state.out).not.toHaveBeenCalled(); + expect(state.pending).toHaveBeenCalled(); + expect(logger.warn).not.toHaveBeenCalled(); + }); + it("logs unknown consent values", () => { + subject.setConsent({ + general: "foo", + }); + expect(state.in).not.toHaveBeenCalled(); + expect(state.out).not.toHaveBeenCalled(); + expect(state.pending).not.toHaveBeenCalled(); + expect(logger.warn).toHaveBeenCalledWith("Unknown consent value: foo"); + }); + it("suspends", () => { + subject.suspend(); + expect(state.in).not.toHaveBeenCalled(); + expect(state.out).not.toHaveBeenCalled(); + expect(state.pending).toHaveBeenCalled(); + expect(logger.warn).not.toHaveBeenCalled(); + }); + it("calls await consent", () => { + state.awaitConsent.mockReturnValue("mypromise"); + expect(subject.awaitConsent()).toEqual("mypromise"); + }); + it("calls with consent", () => { + state.withConsent.mockReturnValue("mypromise"); + expect(subject.withConsent()).toEqual("mypromise"); + }); +}); diff --git a/vtest/unit/specs/core/consent/createConsentStateMachine.spec.js b/vtest/unit/specs/core/consent/createConsentStateMachine.spec.js new file mode 100644 index 000000000..40199eb07 --- /dev/null +++ b/vtest/unit/specs/core/consent/createConsentStateMachine.spec.js @@ -0,0 +1,182 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createConsentStateMachine from "../../../../../src/core/consent/createConsentStateMachine.js"; +import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; + +const DECLINED_CONSENT_ERROR_CODE = "declinedConsent"; +describe("createConsentStateMachine", () => { + let logger; + let subject; + beforeEach(() => { + logger = { + info: vi.fn(), + warn: vi.fn(), + }; + subject = createConsentStateMachine({ + logger, + }); + }); + it("does not resolve promise if consent is pending", () => { + subject.pending(); + const onFulfilled = vi.fn(); + subject.awaitConsent().then(onFulfilled); + return flushPromiseChains().then(() => { + expect(onFulfilled).not.toHaveBeenCalled(); + }); + }); + it("resolves promise if user consented to all purposes", () => { + subject.in(); + const onFulfilled = vi.fn(); + subject.awaitConsent().then(onFulfilled); + return flushPromiseChains().then(() => { + expect(onFulfilled).toHaveBeenCalled(); + }); + }); + [ + ["default", "No consent preferences have been set."], + ["initial", "The user declined consent."], + ["new", "The user declined consent."], + ].forEach(([source, expectedMessage]) => { + it("rejects promise if user consented to no purposes", () => { + subject.out(source); + const onRejected = vi.fn(); + subject.awaitConsent().catch(onRejected); + return flushPromiseChains().then(() => { + const error = onRejected.mock.calls[0][0]; + expect(error.code).toBe(DECLINED_CONSENT_ERROR_CODE); + expect(error.message).toBe(expectedMessage); + }); + }); + }); + it("resolves queued promises when consent set to in", () => { + subject.pending(); + const onFulfilled = vi.fn(); + subject.awaitConsent().then(onFulfilled); + return flushPromiseChains() + .then(() => { + expect(onFulfilled).not.toHaveBeenCalled(); + subject.in(); + return flushPromiseChains(); + }) + .then(() => { + expect(onFulfilled).toHaveBeenCalled(); + }); + }); + it("rejects queued promises when consent set to out", () => { + subject.pending(); + const onRejected = vi.fn(); + subject.awaitConsent().catch(onRejected); + return flushPromiseChains() + .then(() => { + expect(onRejected).not.toHaveBeenCalled(); + subject.out(); + return flushPromiseChains(); + }) + .then(() => { + const error = onRejected.mock.calls[0][0]; + expect(error.code).toBe(DECLINED_CONSENT_ERROR_CODE); + expect(error.message).toBe("The user declined consent."); + }); + }); + + // This is what would happen when the consent component is not included + it("resolves promises when it is not initialized", () => { + const onFulfilled = vi.fn(); + subject.awaitConsent().then(onFulfilled); + return flushPromiseChains().then(() => { + expect(onFulfilled).toHaveBeenCalled(); + }); + }); + [ + ["in", "default"], + [ + "in", + "initial", + "Loaded user consent preferences. The user previously consented.", + "info", + ], + ["in", "new", "User consented.", "info"], + [ + "out", + "default", + "User consent preferences not found. Default consent of out will be used.", + ], + [ + "out", + "initial", + "Loaded user consent preferences. The user previously declined consent.", + ], + ["out", "new", "User declined consent."], + [ + "pending", + "default", + "User consent preferences not found. Default consent of pending will be used. Some commands may be delayed.", + "info", + ], + ["pending", "initial"], + ["pending", "new"], + ].forEach(([action, source, expectedMessage, logLevel = "warn"]) => { + it(`logs the correct messages when ${action} is called with source ${source}`, () => { + subject[action](source); + if (expectedMessage) { + expect(logger[logLevel]).toHaveBeenCalledWith(expectedMessage); + } else { + expect(logger.warn).not.toHaveBeenCalled(); + } + }); + }); + [ + ["in", "User consented.", "info"], + ["out", "User declined consent.", "warn"], + ].forEach(([action, expectedMessage, logLevel]) => { + ["in", "out", "pending"].forEach((defaultConsent) => { + it(`logs a message when first setting consent (${defaultConsent} => ${action}) using setConsent`, () => { + subject[defaultConsent]("default"); + subject.pending(); + subject[action]("new"); + expect(logger[logLevel]).toHaveBeenCalledWith(expectedMessage); + }); + it(`logs a message when first setting consent (${defaultConsent} => ${action}) using sendEvent`, () => { + subject[defaultConsent]("default"); + subject[action]("new"); + expect(logger[logLevel]).toHaveBeenCalledWith(expectedMessage); + }); + }); + it(`doesn't log a message when a request returns or fails. (${action})`, () => { + subject[action]("initial"); + logger.info.mockReset(); + logger.warn.mockReset(); + subject[action]("new"); + subject[action]("new"); + expect(logger.info).not.toHaveBeenCalled(); + expect(logger.warn).not.toHaveBeenCalled(); + }); + }); + describe("withConsent", () => { + ["default", "initial", "new"].forEach((source) => { + it(`returns immediately when ${source} consent is set to "in"`, () => { + subject.in(source); + return expect(subject.withConsent()).resolves.toBe(); + }); + it(`rejects when ${source} consent is set to "out"`, () => { + subject.out(source); + return expect(subject.withConsent()).rejects.toThrowError(); + }); + it(`rejects when ${source} consent is set to "pending"`, () => { + subject.pending(source); + return expect(subject.withConsent()).rejects.toThrowError(); + }); + }); + }); +}); diff --git a/vtest/unit/specs/core/createComponentRegistry.spec.js b/vtest/unit/specs/core/createComponentRegistry.spec.js new file mode 100644 index 000000000..4db8a84a7 --- /dev/null +++ b/vtest/unit/specs/core/createComponentRegistry.spec.js @@ -0,0 +1,206 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import createComponentRegistry from "../../../../src/core/createComponentRegistry.js"; + +const commandErrorRegex = + /\[CompOne\] An error occurred while executing the perform command./; +const lifecycleErrorRegex = + /\[CompOne\] An error occurred while executing the onBeforeEvent lifecycle hook./; +describe("createComponentRegistry", () => { + describe("register", () => { + it("should not register components with existing commands", () => { + expect(() => { + const registry = createComponentRegistry(); + registry.register("CompOne", { + commands: { + command1() {}, + command2() {}, + command3() {}, + }, + }); + registry.register("CompTwo", { + commands: { + command2() {}, + command3() {}, + command4() {}, + }, + }); + }).toThrowError( + "[ComponentRegistry] Could not register CompTwo because it has existing command(s): command2,command3", + ); + }); + }); + describe("getCommand", () => { + it("handles a command that returns a non-promise", () => { + const registry = createComponentRegistry(); + const component = { + commands: { + perform: vi.fn().mockReturnValue("nonPromiseValue"), + }, + }; + registry.register("CompOne", component); + const command = registry.getCommand("perform"); + const result = command("arg1", "arg2"); + expect(component.commands.perform).toHaveBeenCalledWith("arg1", "arg2"); + expect(result).toBe("nonPromiseValue"); + }); + it("handles a command that returns a promise that gets resolved", () => { + const registry = createComponentRegistry(); + const component = { + commands: { + perform: vi + .fn() + .mockReturnValue(Promise.resolve("resolvedPromiseValue")), + }, + }; + registry.register("CompOne", component); + const command = registry.getCommand("perform"); + const result = command("arg1", "arg2"); + expect(component.commands.perform).toHaveBeenCalledWith("arg1", "arg2"); + return result.then((value) => { + expect(value).toBe("resolvedPromiseValue"); + }); + }); + it("handles a command that throws an error", () => { + const registry = createComponentRegistry(); + const runSpy = vi.fn().mockImplementation(() => { + throw new Error("thrownError"); + }); + const component = { + commands: { + perform: { + run: runSpy, + }, + }, + }; + registry.register("CompOne", component); + const command = registry.getCommand("perform"); + expect(() => { + command.run("arg1", "arg2"); + }).toThrowError(commandErrorRegex); + expect(runSpy).toHaveBeenCalledWith("arg1", "arg2"); + }); + it("handles a command that returns a promise that gets rejected", async () => { + const registry = createComponentRegistry(); + const runSpy = vi + .fn() + .mockReturnValue(Promise.reject(new Error("rejectedPromiseError"))); + const component = { + commands: { + perform: { + run: runSpy, + }, + }, + }; + registry.register("CompOne", component); + const command = registry.getCommand("perform"); + const result = command.run("arg1", "arg2"); + expect(runSpy).toHaveBeenCalledWith("arg1", "arg2"); + await expect(result).rejects.toThrow(commandErrorRegex); + }); + it("should return undefined if command does not exist", () => { + const registry = createComponentRegistry(); + const command = registry.getCommand("bogus"); + expect(command).toBeUndefined(); + }); + }); + describe("getLifecycleCallbacks", () => { + it("handles a callback that returns a non-promise", () => { + const registry = createComponentRegistry(); + const component = { + lifecycle: { + onBeforeEvent: vi.fn().mockReturnValue("nonPromiseValue"), + }, + }; + registry.register("CompOne", component); + const callback = registry.getLifecycleCallbacks("onBeforeEvent")[0]; + const result = callback("arg1", "arg2"); + expect(component.lifecycle.onBeforeEvent).toHaveBeenCalledWith( + "arg1", + "arg2", + ); + expect(result).toBe("nonPromiseValue"); + }); + it("handles a callback that returns a promise that gets resolved", async () => { + const registry = createComponentRegistry(); + const component = { + lifecycle: { + onBeforeEvent: vi + .fn() + .mockReturnValue(Promise.resolve("resolvedPromiseValue")), + }, + }; + registry.register("CompOne", component); + const callback = registry.getLifecycleCallbacks("onBeforeEvent")[0]; + const result = callback("arg1", "arg2"); + expect(component.lifecycle.onBeforeEvent).toHaveBeenCalledWith( + "arg1", + "arg2", + ); + await expect(result).resolves.toBe("resolvedPromiseValue"); + }); + it("handles a callback that throws an error", () => { + const registry = createComponentRegistry(); + const component = { + lifecycle: { + onBeforeEvent: vi.fn().mockImplementation(() => { + throw new Error("thrownError"); + }), + }, + }; + registry.register("CompOne", component); + const callback = registry.getLifecycleCallbacks("onBeforeEvent")[0]; + expect(() => { + callback("arg1", "arg2"); + }).toThrowError(lifecycleErrorRegex); + expect(component.lifecycle.onBeforeEvent).toHaveBeenCalledWith( + "arg1", + "arg2", + ); + }); + it("handles a callback that returns a promise that gets rejected", async () => { + const registry = createComponentRegistry(); + const component = { + lifecycle: { + onBeforeEvent: vi + .fn() + .mockReturnValue(Promise.reject(new Error("rejectedPromiseError"))), + }, + }; + registry.register("CompOne", component); + const callback = registry.getLifecycleCallbacks("onBeforeEvent")[0]; + const result = callback("arg1", "arg2"); + expect(component.lifecycle.onBeforeEvent).toHaveBeenCalledWith( + "arg1", + "arg2", + ); + await expect(result).rejects.toThrow(lifecycleErrorRegex); + }); + it("should return all registered lifecycle callbacks", () => { + const registry = createComponentRegistry(); + registry.register("CompOne", { + lifecycle: { + onBeforeEvent() {}, + }, + }); + registry.register("CompTwo", { + lifecycle: { + onBeforeEvent() {}, + }, + }); + const callbacks = registry.getLifecycleCallbacks("onBeforeEvent"); + expect(callbacks.length).toBe(2); + }); + }); +}); diff --git a/vtest/unit/specs/core/createCookieTransfer.spec.js b/vtest/unit/specs/core/createCookieTransfer.spec.js new file mode 100644 index 000000000..67d9bb25f --- /dev/null +++ b/vtest/unit/specs/core/createCookieTransfer.spec.js @@ -0,0 +1,191 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createCookieTransfer from "../../../../src/core/createCookieTransfer.js"; + +describe("createCookieTransfer", () => { + let apexDomain; + const endpointDomain = "thirdparty.com"; + let shouldTransferCookie; + let payload; + let cookieJar; + let cookieTransfer; + const date = new Date(); + const dateProvider = () => date; + beforeEach(() => { + apexDomain = "example.com"; + shouldTransferCookie = vi.fn(); + shouldTransferCookie.mockReturnValue(false); + payload = { + mergeState: vi.fn(), + }; + cookieJar = { + get: vi.fn(), + set: vi.fn(), + }; + }); + const build = () => { + cookieTransfer = createCookieTransfer({ + cookieJar, + shouldTransferCookie, + apexDomain, + dateProvider, + }); + }; + describe("cookiesToPayload", () => { + it("does not transfer cookies to payload if endpoint is first-party", () => { + build(); + cookieTransfer.cookiesToPayload(payload, "edge.example.com"); + expect(payload.mergeState).toHaveBeenCalledWith({ + domain: apexDomain, + cookiesEnabled: true, + }); + }); + it("does not set state.entries if there are no qualifying cookies", () => { + cookieJar.get.mockReturnValue({}); + build(); + cookieTransfer.cookiesToPayload(payload, endpointDomain); + expect(payload.mergeState).toHaveBeenCalledWith({ + domain: apexDomain, + cookiesEnabled: true, + }); + }); + ["example.com", ""].forEach((domain) => { + it(`transfers eligible cookies to payload with domain ${domain}`, () => { + apexDomain = domain; + build(); + cookieJar.get.mockReturnValue({ + kndctr_ABC_CustomOrg_identity: "XYZ@CustomOrg", + ineligible_cookie: "foo", + kndctr_ABC_CustomOrg_optIn: "all", + at_qa_mode: + '{"token":"QATokenString","listedActivitiesOnly":true,"evaluateAsTrueAudienceIds":["2480042"],"previewIndexes":[{"activityIndex":1,"experienceIndex":1}]}', + }); + shouldTransferCookie + .mockReturnValueOnce(true) + .mockReturnValueOnce(false) + .mockReturnValueOnce(true) + .mockReturnValueOnce(true); + cookieTransfer.cookiesToPayload(payload, endpointDomain); + expect(payload.mergeState).toHaveBeenCalledWith({ + domain: apexDomain, + cookiesEnabled: true, + entries: [ + { + key: "kndctr_ABC_CustomOrg_identity", + value: "XYZ@CustomOrg", + }, + { + key: "kndctr_ABC_CustomOrg_optIn", + value: "all", + }, + { + key: "at_qa_mode", + value: + '{"token":"QATokenString","listedActivitiesOnly":true,"evaluateAsTrueAudienceIds":["2480042"],"previewIndexes":[{"activityIndex":1,"experienceIndex":1}]}', + }, + ], + }); + }); + }); + }); + describe("responseToCookies", () => { + let response; + beforeEach(() => { + response = { + getPayloadsByType: vi.fn(), + }; + }); + it("adds a cookie with the correct domain", () => { + build(); + response.getPayloadsByType.mockReturnValue([ + { + key: "mykey", + value: "myvalue", + }, + ]); + cookieTransfer.responseToCookies(response); + expect(cookieJar.set).toHaveBeenNthCalledWith(1, "mykey", "myvalue", { + domain: "example.com", + }); + }); + it("adds multiple cookies", () => { + build(); + response.getPayloadsByType.mockReturnValue([ + { + key: "mykey1", + value: "myvalue1", + }, + { + key: "mykey2", + value: "myvalue2", + }, + ]); + cookieTransfer.responseToCookies(response); + expect(cookieJar.set).toHaveBeenCalledWith( + "mykey1", + "myvalue1", + expect.any(Object), + ); + expect(cookieJar.set).toHaveBeenCalledWith( + "mykey2", + "myvalue2", + expect.any(Object), + ); + }); + it("sets the expires attribute", () => { + build(); + response.getPayloadsByType.mockReturnValue([ + { + key: "mykey", + value: "myvalue", + maxAge: 172800, // 24 * 60 * 60 * 2 + }, + ]); + cookieTransfer.responseToCookies(response); + expect(cookieJar.set.mock.calls[0][2].expires.getTime()).toEqual( + date.getTime() + 172800 * 1000, + ); + }); + it("adds a sameSite=none cookie with secure attribute", () => { + build(); + response.getPayloadsByType.mockReturnValue([ + { + key: "mykey", + value: "myvalue", + attrs: { + SameSite: "None", + }, + }, + ]); + cookieTransfer.responseToCookies(response); + expect(cookieJar.set.mock.calls[0][2].sameSite).toEqual("none"); + expect(cookieJar.set.mock.calls[0][2].secure).toEqual(true); + }); + it("adds a sameSite=strict cookie", () => { + build(); + response.getPayloadsByType.mockReturnValue([ + { + key: "mykey", + value: "myvalue", + attrs: { + SameSite: "Strict", + }, + }, + ]); + cookieTransfer.responseToCookies(response); + expect(cookieJar.set.mock.calls[0][2].sameSite).toEqual("strict"); + expect(cookieJar.set.mock.calls[0][2].secure).toBeUndefined(); + }); + }); +}); diff --git a/vtest/unit/specs/core/createEvent.spec.js b/vtest/unit/specs/core/createEvent.spec.js new file mode 100644 index 000000000..77a968397 --- /dev/null +++ b/vtest/unit/specs/core/createEvent.spec.js @@ -0,0 +1,493 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, describe, it, expect } from "vitest"; +import createEvent from "../../../../src/core/createEvent.js"; + +describe("createEvent", () => { + let event; + beforeEach(() => { + event = createEvent(); + }); + it("deeply merges XDM with user-provided XDM merged last", () => { + event.setUserXdm({ + fruit: { + type: "apple", + }, + veggie: { + type: "carrot", + }, + }); + event.mergeXdm({ + fruit: { + type: "strawberry", + }, + sport: { + type: "basketball", + }, + }); + event.mergeXdm(); + event.mergeXdm(null); + event.mergeXdm({ + sport: { + type: "football", + }, + game: { + type: "clue", + }, + }); + event.finalize(); + expect(event.toJSON()).toEqual({ + xdm: { + fruit: { + type: "apple", + }, + veggie: { + type: "carrot", + }, + sport: { + type: "football", + }, + game: { + type: "clue", + }, + }, + }); + }); + it("does not modify the original user XDM object", () => { + const dataLayer = { + fruit: { + type: "apple", + }, + veggie: { + type: "carrot", + }, + }; + event.setUserXdm(dataLayer); + event.mergeXdm({ + fruit: { + type: "strawberry", + }, + sport: { + type: "basketball", + }, + }); + expect(dataLayer).toEqual({ + fruit: { + type: "apple", + }, + veggie: { + type: "carrot", + }, + }); + }); + it("handles undefined user XDM", () => { + event.setUserXdm(undefined); + event.mergeXdm({ + fruit: "apple", + }); + event.finalize(); + expect(event.toJSON()).toEqual({ + xdm: { + fruit: "apple", + }, + }); + }); + it("sets user data", () => { + event.setUserData({ + fruit: "apple", + }); + event.setUserData({ + veggie: "carrot", + }); + event.finalize(); + expect(event.toJSON()).toEqual({ + data: { + veggie: "carrot", + }, + }); + }); + it("handles undefined user data", () => { + event.setUserData(undefined); + event.finalize(); + expect(event.toJSON()).toEqual({}); + }); + it("deeply merges meta", () => { + event.mergeMeta({ + fruit: { + type: "strawberry", + }, + sport: { + type: "basketball", + }, + }); + event.mergeMeta({ + sport: { + type: "football", + }, + game: { + type: "clue", + }, + }); + event.mergeMeta(); + event.mergeMeta(null); + event.finalize(); + expect(event.toJSON()).toEqual({ + meta: { + fruit: { + type: "strawberry", + }, + sport: { + type: "football", + }, + game: { + type: "clue", + }, + }, + }); + }); + it("deeply merges query", () => { + event.mergeQuery({ + fruit: { + type: "strawberry", + }, + sport: { + type: "basketball", + }, + }); + event.mergeQuery({ + sport: { + type: "football", + }, + game: { + type: "clue", + }, + }); + event.mergeQuery(); + event.mergeQuery(null); + event.finalize(); + expect(event.toJSON()).toEqual({ + query: { + fruit: { + type: "strawberry", + }, + sport: { + type: "football", + }, + game: { + type: "clue", + }, + }, + }); + }); + it("sets documentUnloading", () => { + expect(event.getDocumentMayUnload()).toBe(false); + event.documentMayUnload(); + expect(event.getDocumentMayUnload()).toBe(true); + }); + it("throws error when mergeXdm called after finalize", () => { + event.setUserXdm({ + web: {}, + }); + event.finalize(); + expect(() => + event.mergeXdm({ + a: "b", + }), + ).toThrowError("mergeXdm cannot be called after event is finalized."); + }); + it("throws error when toJSON called before finalize", () => { + event.setUserXdm({ + web: {}, + }); + expect(() => event.toJSON()).toThrowError("toJSON called before finalize"); + }); + it("reports whether the event is empty", () => { + expect(event.isEmpty()).toBe(true); + event.setUserData({ + foo: "bar", + }); + expect(event.isEmpty()).toBe(false); + }); + it("returns undefined when no viewName exists", () => { + expect(event.getViewName()).toBe(undefined); + event.setUserXdm({ + web: {}, + }); + expect(event.getViewName()).toBe(undefined); + event.setUserXdm({ + web: { + webPageDetails: {}, + }, + }); + expect(event.getViewName()).toBe(undefined); + }); + it("returns viewName when viewName exists", () => { + event.setUserXdm({ + web: { + webPageDetails: { + viewName: "cart", + }, + }, + }); + expect(event.getViewName()).toBe("cart"); + }); + describe("applyCallback", () => { + it("can add fields to empty xdm", () => { + const callback = ({ xdm, data }) => { + xdm.a = "1"; + data.b = "2"; + }; + const subject = createEvent(); + subject.finalize(callback); + expect(subject.toJSON()).toEqual({ + xdm: { + a: "1", + }, + data: { + b: "2", + }, + }); + }); + it("can add fields to an existing xdm", () => { + const callback = ({ xdm, data }) => { + xdm.b = "2"; + data.b = "2"; + }; + const subject = createEvent(); + subject.setUserData({ + a: "1", + }); + subject.setUserXdm({ + a: "1", + }); + subject.finalize(callback); + expect(subject.toJSON()).toEqual({ + xdm: { + a: "1", + b: "2", + }, + data: { + a: "1", + b: "2", + }, + }); + }); + it("can remove fields", () => { + const callback = ({ xdm, data }) => { + delete xdm.a; + delete data.a; + }; + const subject = createEvent(); + subject.setUserXdm({ + a: "1", + b: "2", + }); + subject.setUserData({ + a: "1", + b: "2", + }); + subject.finalize(callback); + expect(subject.toJSON()).toEqual({ + xdm: { + b: "2", + }, + data: { + b: "2", + }, + }); + }); + it("can set xdm or data to empty objects", () => { + const callback = (content) => { + content.xdm = {}; + content.data = {}; + }; + const subject = createEvent(); + subject.setUserXdm({ + a: "1", + b: "2", + }); + subject.setUserData({ + a: "1", + b: "2", + }); + subject.finalize(callback); + expect(subject.toJSON()).toEqual({}); + }); + it("can delete xdm or data objects", () => { + const callback = (content) => { + delete content.xdm; + delete content.data; + }; + const subject = createEvent(); + subject.setUserXdm({ + a: "1", + b: "2", + }); + subject.setUserData({ + a: "1", + b: "2", + }); + subject.finalize(callback); + expect(subject.toJSON()).toEqual({}); + }); + it("event merges when there is an error", () => { + const callback = ({ xdm, data }) => { + delete xdm.a; + xdm.c = "3"; + delete data.a; + data.c = "3"; + throw new Error("Expected Error"); + }; + const subject = createEvent(); + subject.setUserXdm({ + a: "1", + b: "2", + }); + subject.setUserData({ + a: "1", + b: "2", + }); + expect(() => subject.finalize(callback)).toThrowError("Expected Error"); + expect(subject.toJSON()).toEqual({ + xdm: { + b: "2", + c: "3", + }, + data: { + b: "2", + c: "3", + }, + }); + }); + it("event shouldSend should be true when callback returns undefined", () => { + const callback = () => { + return undefined; + }; + const subject = createEvent(); + subject.finalize(callback); + expect(subject.shouldSend()).toBe(true); + }); + it("event shouldSend should be true when callback returns true", () => { + const callback = () => { + return true; + }; + const subject = createEvent(); + subject.finalize(callback); + expect(subject.shouldSend()).toBe(true); + }); + it("event shouldSend should be false when callback throws error", () => { + const callback = () => { + throw new Error("Expected Error"); + }; + const subject = createEvent(); + expect(() => subject.finalize(callback)).toThrowError("Expected Error"); + expect(subject.shouldSend()).toBe(false); + }); + it("event shouldSend should be false when callback returns false", () => { + const callback = () => { + return false; + }; + const subject = createEvent(); + subject.finalize(callback); + expect(subject.shouldSend()).toBe(false); + }); + it("can replace xdm or data", () => { + const callback = (content) => { + content.xdm = { + a: "1", + }; + content.data = { + b: "2", + }; + }; + const subject = createEvent(); + subject.setUserXdm({ + c: "3", + }); + subject.setUserData({ + d: "4", + }); + subject.finalize(callback); + expect(subject.toJSON()).toEqual({ + xdm: { + a: "1", + }, + data: { + b: "2", + }, + }); + }); + }); + it("deduplicates propositions by id", () => { + const subject = createEvent(); + subject.mergeXdm({ + _experience: { + decisioning: { + propositions: [ + { + id: "1", + scope: "a", + }, + { + id: "2", + scope: "a", + }, + ], + }, + }, + }); + subject.setUserXdm({ + _experience: { + decisioning: { + propositions: [ + { + id: "2", + scope: "a", + }, + { + id: "3", + scope: "a", + }, + { + id: "3", + scope: "a", + }, + ], + }, + }, + }); + subject.finalize(); + expect(subject.toJSON()).toEqual({ + xdm: { + _experience: { + decisioning: { + propositions: [ + { + id: "2", + scope: "a", + }, + { + id: "3", + scope: "a", + }, + { + id: "1", + scope: "a", + }, + ], + }, + }, + }, + }); + }); +}); diff --git a/vtest/unit/specs/core/createEventManager.spec.js b/vtest/unit/specs/core/createEventManager.spec.js new file mode 100644 index 000000000..934a3e40b --- /dev/null +++ b/vtest/unit/specs/core/createEventManager.spec.js @@ -0,0 +1,369 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createEventManager from "../../../../src/core/createEventManager.js"; +import createConfig from "../../../../src/core/config/createConfig.js"; +import { defer } from "../../../../src/utils/index.js"; +import flushPromiseChains from "../../helpers/flushPromiseChains.js"; + +const CANCELLATION_MESSAGE_REGEX = /Event was canceled/; +describe("createEventManager", () => { + let config; + let logger; + let lifecycle; + let consent; + let event; + let requestPayload; + let request; + let createDataCollectionRequest; + let sendEdgeNetworkRequest; + let applyResponse; + let onRequestFailureForOnBeforeEvent; + let fakeOnRequestFailure; + let eventManager; + beforeEach(() => { + config = createConfig({ + orgId: "ABC123", + onBeforeEventSend: vi.fn(), + debugEnabled: true, + edgeConfigOverrides: {}, + }); + logger = { + info: vi.fn(), + }; + lifecycle = { + onBeforeEvent: vi.fn().mockReturnValue(Promise.resolve()), + onBeforeDataCollectionRequest: vi.fn().mockReturnValue(Promise.resolve()), + onRequestFailure: vi.fn().mockReturnValue(Promise.resolve()), + }; + consent = { + awaitConsent: vi.fn().mockReturnValue(Promise.resolve()), + }; + event = { + finalize: vi.fn().mockReturnValue(undefined), + shouldSend: vi.fn().mockReturnValue(true), + }; + onRequestFailureForOnBeforeEvent = vi.fn(); + fakeOnRequestFailure = ({ onRequestFailure }) => { + onRequestFailure(onRequestFailureForOnBeforeEvent); + return Promise.resolve(); + }; + const createEvent = () => { + return event; + }; + requestPayload = { + addEvent: vi.fn(), + mergeConfigOverride: vi.fn(), + }; + const createDataCollectionRequestPayload = () => { + return requestPayload; + }; + request = { + getPayload() { + return requestPayload; + }, + }; + createDataCollectionRequest = vi.fn().mockReturnValue(request); + sendEdgeNetworkRequest = vi.fn().mockReturnValue(Promise.resolve()); + applyResponse = vi.fn().mockReturnValue(Promise.resolve()); + eventManager = createEventManager({ + config, + logger, + lifecycle, + consent, + createEvent, + createDataCollectionRequestPayload, + createDataCollectionRequest, + sendEdgeNetworkRequest, + applyResponse, + }); + }); + describe("createEvent", () => { + it("creates an event object", () => { + expect(eventManager.createEvent()).toBe(event); + }); + }); + describe("sendEvent", () => { + it("creates the payload and adds event and meta", () => { + return eventManager.sendEvent(event).then(() => { + expect(requestPayload.addEvent).toHaveBeenCalledWith(event); + }); + }); + it("allows other components to access event and pause the lifecycle", () => { + const deferred = defer(); + const options = { + renderDecisions: true, + }; + lifecycle.onBeforeEvent.mockReturnValue(deferred.promise); + eventManager.sendEvent(event, options); + return flushPromiseChains() + .then(() => { + expect(lifecycle.onBeforeEvent).toHaveBeenCalledWith({ + event, + renderDecisions: true, + onResponse: expect.any(Function), + onRequestFailure: expect.any(Function), + }); + expect(consent.awaitConsent).not.toHaveBeenCalled(); + deferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(sendEdgeNetworkRequest).toHaveBeenCalled(); + }); + }); + it("events call finalize with onBeforeEventSend callback", async () => { + await eventManager.sendEvent(event); + expect(event.finalize).toHaveBeenCalledWith(config.onBeforeEventSend); + }); + it("does not send event when event.shouldSend returns false", () => { + lifecycle.onBeforeEvent.mockImplementation(fakeOnRequestFailure); + event.shouldSend.mockReturnValue(false); + return eventManager.sendEvent(event).then((result) => { + expect(result).toBeUndefined(); + expect(onRequestFailureForOnBeforeEvent).toHaveBeenCalled(); + expect( + onRequestFailureForOnBeforeEvent.mock.calls[0][0].error.message, + ).toMatch(CANCELLATION_MESSAGE_REGEX); + expect(logger.info).toHaveBeenCalledWith( + expect.stringMatching(CANCELLATION_MESSAGE_REGEX), + ); + expect(sendEdgeNetworkRequest).not.toHaveBeenCalled(); + }); + }); + it("sends event when event.shouldSend returns true", () => { + lifecycle.onBeforeEvent.mockImplementation(fakeOnRequestFailure); + return eventManager.sendEvent(event).then((result) => { + expect(result).toBeUndefined(); + expect(onRequestFailureForOnBeforeEvent).not.toHaveBeenCalled(); + expect(sendEdgeNetworkRequest).toHaveBeenCalled(); + }); + }); + it("throws an error on event finalize and event should not be sent", async () => { + lifecycle.onBeforeEvent.mockImplementation(fakeOnRequestFailure); + const errorMsg = "Expected Error"; + const error = new Error(errorMsg); + + event.finalize.mockImplementation(() => { + throw error; + }); + + await expect( + eventManager.sendEvent(event).then(() => { + throw new Error("Should not have resolved."); + }), + ).rejects.toThrowError(errorMsg); + + expect(onRequestFailureForOnBeforeEvent).toHaveBeenCalledWith({ + error, + }); + + expect(sendEdgeNetworkRequest).not.toHaveBeenCalled(); + }); + it("allows components and consent to pause the lifecycle", () => { + const onBeforeEventDeferred = defer(); + const consentDeferred = defer(); + lifecycle.onBeforeEvent.mockReturnValue(onBeforeEventDeferred.promise); + consent.awaitConsent.mockReturnValue(consentDeferred.promise); + eventManager.sendEvent(event); + expect(lifecycle.onBeforeEvent).toHaveBeenCalled(); + return flushPromiseChains() + .then(() => { + expect(consent.awaitConsent).not.toHaveBeenCalled(); + expect(sendEdgeNetworkRequest).not.toHaveBeenCalled(); + onBeforeEventDeferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(consent.awaitConsent).toHaveBeenCalled(); + expect(sendEdgeNetworkRequest).not.toHaveBeenCalled(); + consentDeferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(sendEdgeNetworkRequest).toHaveBeenCalled(); + }); + }); + it("calls onResponse callbacks on response", async () => { + const onResponseForOnBeforeEvent = vi.fn(); + lifecycle.onBeforeEvent.mockImplementation(({ onResponse }) => { + onResponse(onResponseForOnBeforeEvent); + return Promise.resolve(); + }); + const response = { + type: "response", + }; + sendEdgeNetworkRequest.mockImplementation( + ({ runOnResponseCallbacks }) => { + runOnResponseCallbacks({ + response, + }); + return Promise.resolve(); + }, + ); + + await eventManager.sendEvent(event); + expect(onResponseForOnBeforeEvent).toHaveBeenCalledWith({ + response, + }); + }); + it("calls onRequestFailure callbacks on request failure", async () => { + lifecycle.onBeforeEvent.mockImplementation(fakeOnRequestFailure); + const error = new Error(); + sendEdgeNetworkRequest.mockImplementation( + ({ runOnRequestFailureCallbacks }) => { + runOnRequestFailureCallbacks({ error }); + throw error; + }, + ); + + await expect(eventManager.sendEvent(event)).rejects.toThrow(error); + expect(onRequestFailureForOnBeforeEvent).toHaveBeenCalledWith({ error }); + }); + it("sends network request", () => { + return eventManager.sendEvent(event).then(() => { + expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ + request, + runOnResponseCallbacks: expect.any(Function), + runOnRequestFailureCallbacks: expect.any(Function), + }); + }); + }); + it("fails returned promise if request fails", () => { + sendEdgeNetworkRequest.mockReturnValue( + Promise.reject(new Error("no connection")), + ); + return expect(eventManager.sendEvent(event)).rejects.toThrowError( + "no connection", + ); + }); + }); + describe("applyResponse", () => { + const responseHeaders = { + "x-request-id": "474ec8af-6326-4cb5-952a-4b7dc6be5749", + }; + const responseBody = { + requestId: "474ec8af-6326-4cb5-952a-4b7dc6be5749", + handle: [], + }; + const options = { + renderDecisions: false, + responseHeaders, + responseBody, + }; + it("creates the payload and adds event and meta", () => { + return eventManager.applyResponse(event, options).then(() => { + expect(requestPayload.addEvent).toHaveBeenCalledWith(event); + }); + }); + it("events no not call finalize with onBeforeEventSend callback", () => { + return eventManager.applyResponse(event, options).then(() => { + expect(event.finalize).not.toHaveBeenCalled(); + }); + }); + it("calls onResponse callbacks", () => { + const onResponseForOnBeforeEvent = vi.fn(); + lifecycle.onBeforeEvent.mockImplementation(({ onResponse }) => { + onResponse(onResponseForOnBeforeEvent); + return Promise.resolve(); + }); + const response = { + type: "response", + }; + applyResponse.mockImplementation(({ runOnResponseCallbacks }) => { + runOnResponseCallbacks({ + response, + }); + return Promise.resolve(); + }); + return eventManager.applyResponse(event, options).then(() => { + expect(onResponseForOnBeforeEvent).toHaveBeenCalledWith({ + response, + }); + }); + }); + it("applies AEP edge response headers and body and returns result", () => { + const mockResult = { + response: "yep", + }; + applyResponse.mockReturnValue(mockResult); + return eventManager.applyResponse(event, options).then((result) => { + expect(sendEdgeNetworkRequest).not.toHaveBeenCalled(); + expect(applyResponse).toHaveBeenCalledWith({ + request, + responseHeaders, + responseBody, + runOnResponseCallbacks: expect.any(Function), + }); + expect(result).toEqual(mockResult); + }); + }); + it("includes override configuration, if provided", () => { + eventManager + .sendEvent(event, { + edgeConfigOverrides: { + com_adobe_experience_platform: { + event: { + datasetId: "456", + }, + }, + com_adobe_identity: { + idSyncContainerId: "123", + }, + }, + }) + .then(() => { + expect(requestPayload.mergeConfigOverride).toHaveBeenCalledWith({ + com_adobe_identity: { + idSyncContainerId: "123", + }, + com_adobe_experience_platform: { + event: { + datasetId: "456", + }, + }, + }); + }); + }); + it("includes global override configuration, if provided", () => { + config.edgeConfigOverrides.com_adobe_identity = { + idSyncContainerId: "123", + }; + eventManager + .sendEvent(event, { + edgeConfigOverrides: {}, + }) + .then(() => { + expect(requestPayload.mergeConfigOverride).toHaveBeenCalledWith({ + com_adobe_identity: { + idSyncContainerId: "123", + }, + }); + }); + }); + it("includes the datastreamId override, if provided", () => { + eventManager + .sendEvent(event, { + edgeConfigOverrides: { + datastreamId: "456", + }, + }) + .then(() => { + expect(createDataCollectionRequest).toHaveBeenCalledWith({ + payload: expect.any(Object), + datastreamIdOverride: "456", + }); + }); + }); + }); +}); diff --git a/vtest/unit/specs/core/createInstanceFunction.spec.js b/vtest/unit/specs/core/createInstanceFunction.spec.js new file mode 100644 index 000000000..1097727ae --- /dev/null +++ b/vtest/unit/specs/core/createInstanceFunction.spec.js @@ -0,0 +1,68 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import createInstanceFunction from "../../../../src/core/createInstanceFunction.js"; +import flushPromiseChains from "../../helpers/flushPromiseChains.js"; + +describe("createInstance", () => { + it("successfully executes command", () => { + const executeCommand = vi + .fn() + .mockReturnValue(Promise.resolve("commandresult")); + const resolve = vi.fn(); + const reject = vi.fn(); + const instance = createInstanceFunction(executeCommand); + instance([ + resolve, + reject, + [ + "event", + { + foo: "bar", + }, + ], + ]); + expect(executeCommand).toHaveBeenCalledWith("event", { + foo: "bar", + }); + return flushPromiseChains().then(() => { + expect(resolve).toHaveBeenCalledWith("commandresult"); + expect(reject).not.toHaveBeenCalled(); + }); + }); + it("unsuccessfully execute command", () => { + const executeCommand = vi + .fn() + .mockReturnValue(Promise.reject(new Error("error occurred"))); + const resolve = vi.fn(); + const reject = vi.fn(); + const instance = createInstanceFunction(executeCommand); + instance([ + resolve, + reject, + [ + "event", + { + foo: "bar", + }, + ], + ]); + expect(executeCommand).toHaveBeenCalledWith("event", { + foo: "bar", + }); + return flushPromiseChains().then(() => { + expect(resolve).not.toHaveBeenCalled(); + expect(reject).toHaveBeenCalledWith(expect.any(Error)); + }); + }); +}); diff --git a/vtest/unit/specs/core/createLifecycle.spec.js b/vtest/unit/specs/core/createLifecycle.spec.js new file mode 100644 index 000000000..7b1843e4b --- /dev/null +++ b/vtest/unit/specs/core/createLifecycle.spec.js @@ -0,0 +1,91 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import createLifecycle from "../../../../src/core/createLifecycle.js"; + +describe("createLifecycle", () => { + it("exposes all lifecycle methods and they return promises", () => { + const componentRegistry = { + getLifecycleCallbacks() { + return []; + }, + }; + const lifecycle = createLifecycle(componentRegistry); + [ + "onComponentsRegistered", + "onBeforeEvent", + "onBeforeRequest", + "onResponse", + "onRequestFailure", + "onClick", + ].forEach((methodName) => { + expect(lifecycle[methodName]()).toEqual(expect.any(Promise)); + }); + }); + it("calls all callbacks for a given lifecycle method", () => { + const callbacks = [ + vi.fn().mockReturnValue({ + returnValue1: "valueFromCallback1", + }), + vi.fn().mockReturnValue( + Promise.resolve({ + returnValue2: "valueFromCallback2", + }), + ), + ]; + const componentRegistry = { + getLifecycleCallbacks(hookName) { + return hookName === "onBeforeEvent" ? callbacks : []; + }, + }; + const lifecycle = createLifecycle(componentRegistry); + return lifecycle.onBeforeEvent("arg1", "arg2").then((result) => { + callbacks.forEach((callback) => { + expect(callback).toHaveBeenCalledWith("arg1", "arg2"); + }); + expect(result).toBeInstanceOf(Array); + expect(result[0].returnValue1).toEqual("valueFromCallback1"); + expect(result[1].returnValue2).toEqual("valueFromCallback2"); + }); + }); + it("ensures all callbacks for one method are called before any callbacks from a different method", async () => { + let lifecycle; + const callbacksByHookName = { + onComponentsRegistered: [ + vi.fn().mockImplementation(() => { + lifecycle.onBeforeEvent(); + }), + vi.fn(), + ], + onBeforeEvent: [vi.fn()], + }; + const componentRegistry = { + getLifecycleCallbacks(hookName) { + return callbacksByHookName[hookName] || []; + }, + }; + lifecycle = createLifecycle(componentRegistry); + + await lifecycle.onComponentsRegistered(); + + const callOrder1 = + callbacksByHookName.onComponentsRegistered[0].mock.invocationCallOrder[0]; + const callOrder2 = + callbacksByHookName.onComponentsRegistered[1].mock.invocationCallOrder[0]; + const callOrder3 = + callbacksByHookName.onBeforeEvent[0].mock.invocationCallOrder[0]; + + expect(callOrder1).toBeLessThan(callOrder2); + expect(callOrder2).toBeLessThan(callOrder3); + }); +}); diff --git a/vtest/unit/specs/core/createLogController.spec.js b/vtest/unit/specs/core/createLogController.spec.js new file mode 100644 index 000000000..62f8597fc --- /dev/null +++ b/vtest/unit/specs/core/createLogController.spec.js @@ -0,0 +1,240 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createLogController from "../../../../src/core/createLogController.js"; + +const instanceName = "alloy123"; +describe("createLogController", () => { + let console; + let locationSearch; + let logger; + let createLogger; + let getDebugEnabled; + let sessionStorage; + let createNamespacedStorage; + let getMonitors; + beforeEach(() => { + console = { + log() {}, + }; + locationSearch = ""; + logger = { + log() {}, + }; + createLogger = vi + .fn() + .mockImplementation(({ getDebugEnabled: _getDebugEnabled }) => { + getDebugEnabled = _getDebugEnabled; + return logger; + }); + sessionStorage = { + getItem: vi.fn().mockReturnValue(null), + setItem: vi.fn(), + }; + createNamespacedStorage = vi.fn().mockReturnValue({ + session: sessionStorage, + }); + getMonitors = () => []; + }); + it("creates a namespaced storage", () => { + createLogController({ + console, + locationSearch, + createLogger, + instanceName, + createNamespacedStorage, + getMonitors, + }); + expect(createNamespacedStorage).toHaveBeenCalledWith("instance.alloy123."); + }); + it("returns false for getDebugEnabled if storage item is not found", () => { + createLogController({ + console, + locationSearch, + createLogger, + instanceName, + createNamespacedStorage, + getMonitors, + }); + expect(getDebugEnabled()).toBe(false); + }); + it("returns false for getDebugEnabled if storage item is false", () => { + sessionStorage.getItem = () => "false"; + createLogController({ + console, + locationSearch, + createLogger, + instanceName, + createNamespacedStorage, + getMonitors, + }); + expect(getDebugEnabled()).toBe(false); + }); + it("returns true for getDebugEnabled if storage item is true", () => { + sessionStorage.getItem = () => "true"; + createLogController({ + console, + locationSearch, + createLogger, + instanceName, + createNamespacedStorage, + getMonitors, + }); + expect(getDebugEnabled()).toBe(true); + }); + it("persists changes to debugEnabled if not set from config", () => { + const logController = createLogController({ + console, + locationSearch, + createLogger, + instanceName, + createNamespacedStorage, + getMonitors, + }); + logController.setDebugEnabled(true, { + fromConfig: false, + }); + expect(sessionStorage.setItem).toHaveBeenCalledWith("debug", "true"); + expect(getDebugEnabled()).toBe(true); + }); + it("does not persist changes to debugEnabled if set from config", () => { + const logController = createLogController({ + console, + locationSearch, + createLogger, + instanceName, + createNamespacedStorage, + getMonitors, + }); + logController.setDebugEnabled(true, { + fromConfig: true, + }); + expect(sessionStorage.setItem).not.toHaveBeenCalled(); + expect(getDebugEnabled()).toBe(true); + }); + it("does not change debugEnabled from config if previously changed from something other than config on same page load", () => { + const logController = createLogController({ + console, + locationSearch, + createLogger, + instanceName, + createNamespacedStorage, + getMonitors, + }); + logController.setDebugEnabled(true, { + fromConfig: false, + }); + logController.setDebugEnabled(false, { + fromConfig: true, + }); + expect(sessionStorage.setItem).toHaveBeenCalledWith("debug", "true"); + expect(sessionStorage.setItem).not.toHaveBeenCalledWith("debug", "false"); + expect(getDebugEnabled()).toBe(true); + }); + it("does not change debugEnabled from config if previously changed from something other than config on previous page load", () => { + sessionStorage.getItem = () => "true"; + const logController = createLogController({ + console, + locationSearch, + createLogger, + instanceName, + createNamespacedStorage, + getMonitors, + }); + logController.setDebugEnabled(false, { + fromConfig: true, + }); + expect(sessionStorage.setItem).not.toHaveBeenCalled(); + expect(getDebugEnabled()).toBe(true); + }); + it("sets debugEnabled to true if query string parameter set to true", () => { + locationSearch = "?alloy_debug=true"; + const logController = createLogController({ + console, + locationSearch, + createLogger, + instanceName, + createNamespacedStorage, + getMonitors, + }); + + // Make sure setting debugEnabled from config can't override it. + logController.setDebugEnabled(false, { + fromConfig: true, + }); + expect(sessionStorage.setItem).toHaveBeenCalledWith("debug", "true"); + expect(sessionStorage.setItem.mock.calls.length).toBe(1); + expect(getDebugEnabled()).toBe(true); + }); + it("sets debugEnabled to false if query string parameter set to false", () => { + locationSearch = "?alloy_debug=false"; + const logController = createLogController({ + console, + locationSearch, + createLogger, + instanceName, + createNamespacedStorage, + getMonitors, + }); + + // Make sure setting debugEnabled from config can't override it. + logController.setDebugEnabled(true, { + fromConfig: true, + }); + expect(sessionStorage.setItem).toHaveBeenCalledWith("debug", "false"); + expect(sessionStorage.setItem.mock.calls.length).toBe(1); + expect(getDebugEnabled()).toBe(false); + }); + it("creates a logger", () => { + const logController = createLogController({ + console, + locationSearch, + createLogger, + instanceName, + createNamespacedStorage, + getMonitors, + }); + expect(createLogger).toHaveBeenCalledWith({ + getDebugEnabled, + console, + getMonitors, + context: { + instanceName: "alloy123", + }, + }); + expect(logController.logger).toBe(logger); + }); + it("creates a component logger", () => { + const logController = createLogController({ + console, + locationSearch, + createLogger, + instanceName, + createNamespacedStorage, + getMonitors, + }); + const componentLogger = {}; + createLogger.mockReturnValue(componentLogger); + const result = logController.createComponentLogger("Personalization"); + expect(createLogger).toHaveBeenCalledWith({ + getDebugEnabled, + console, + getMonitors, + context: { + instanceName: "alloy123", + componentName: "Personalization", + }, + }); + expect(result).toBe(componentLogger); + }); +}); diff --git a/vtest/unit/specs/core/createLogger.spec.js b/vtest/unit/specs/core/createLogger.spec.js new file mode 100644 index 000000000..d99e6aa42 --- /dev/null +++ b/vtest/unit/specs/core/createLogger.spec.js @@ -0,0 +1,279 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createLogger from "../../../../src/core/createLogger.js"; + +const logMethods = ["info", "warn", "error"]; +const monitorMethods = [ + "onInstanceCreated", + "onInstanceConfigured", + "onBeforeCommand", + "onCommandResolved", + "onCommandRejected", + "onBeforeNetworkRequest", + "onNetworkResponse", + "onNetworkError", +]; +const message = "test message"; +describe("createLogger", () => { + let logEnabled; + let logger; + let console; + let getDebugEnabled; + let context; + let getMonitors; + beforeEach(() => { + console = logMethods.reduce((acc, logMethod) => { + acc[logMethod] = vi.fn(); + return acc; + }, {}); + getDebugEnabled = () => logEnabled; + context = { + instanceName: "myinstance", + }; + getMonitors = () => []; + }); + const build = () => { + logger = createLogger({ + console, + getDebugEnabled, + context, + getMonitors, + }); + }; + logMethods.forEach((logMethod) => { + it(`logs message if debugging is enabled and ${logMethod} is called`, () => { + logEnabled = true; + build(); + logger[logMethod](message); + expect(console[logMethod]).toHaveBeenCalledWith("[myinstance]", message); + }); + it(`does not log a message if debugging is disabled and ${logMethod} is called`, () => { + logEnabled = false; + build(); + logger[logMethod](message); + expect(console[logMethod]).not.toHaveBeenCalled(); + }); + }); + it("is enabled if logEnabled", () => { + logEnabled = true; + getMonitors = () => []; + build(); + expect(logger.enabled).toBe(true); + }); + it("is enabled if there are monitors", () => { + logEnabled = false; + getMonitors = () => [{}]; + build(); + expect(logger.enabled).toBe(true); + }); + it("is not enabled if no logEnabled and no monitors", () => { + logEnabled = false; + getMonitors = () => []; + build(); + expect(logger.enabled).toBe(false); + }); + it("is ok for a monitor to only implement some of the methods", () => { + getMonitors = () => [{}]; + build(); + logger.logOnInstanceCreated({ + b: "2", + }); + }); + monitorMethods.forEach((monitorMethod) => { + it(`calls the monitor method ${monitorMethod}`, () => { + const loggerMethod = `log${monitorMethod.charAt(0).toUpperCase()}${monitorMethod.slice(1)}`; + context = { + a: "1", + }; + const monitor1 = { + [monitorMethod]: vi.fn(), + onBeforeLog: vi.fn(), + }; + + const monitor2 = { + [monitorMethod]: vi.fn(), + }; + getMonitors = () => [monitor1, monitor2]; + build(); + logger[loggerMethod]({ + b: "2", + }); + expect(monitor1[monitorMethod]).toHaveBeenCalledWith({ + a: "1", + b: "2", + }); + expect(monitor1.onBeforeLog).toHaveBeenCalled(); + expect(monitor2[monitorMethod]).toHaveBeenCalledWith({ + a: "1", + b: "2", + }); + }); + }); + it("logs onInstanceCreated", () => { + logEnabled = true; + build(); + logger.logOnInstanceCreated({}); + expect(console.info).toHaveBeenCalledWith( + "[myinstance]", + "Instance initialized.", + ); + }); + it("logs onInstanceConfigured", () => { + logEnabled = true; + build(); + logger.logOnInstanceConfigured({ + config: { + a: "1", + }, + }); + expect(console.info).toHaveBeenCalledWith( + "[myinstance]", + "Instance configured. Computed configuration:", + { + a: "1", + }, + ); + }); + it("logs onBeforeCommand", () => { + logEnabled = true; + build(); + logger.logOnBeforeCommand({ + commandName: "mycommand", + options: { + a: "1", + }, + }); + expect(console.info).toHaveBeenCalledWith( + "[myinstance]", + "Executing mycommand command. Options:", + { + a: "1", + }, + ); + }); + it("logs onCommandResolved", () => { + logEnabled = true; + build(); + logger.logOnCommandResolved({ + commandName: "mycommand", + options: { + a: "1", + }, + result: { + b: "2", + }, + }); + expect(console.info).toHaveBeenCalledWith( + "[myinstance]", + "mycommand command resolved. Result:", + { + b: "2", + }, + ); + }); + it("logs onCommandRejected", () => { + logEnabled = true; + build(); + const error = Error("myerror"); + logger.logOnCommandRejected({ + commandName: "mycommand", + options: { + a: "1", + }, + error, + }); + expect(console.error).toHaveBeenCalledWith( + "[myinstance]", + "mycommand command was rejected. Error:", + error, + ); + }); + it("logs onBeforeNetworkRequest", () => { + logEnabled = true; + build(); + logger.logOnBeforeNetworkRequest({ + requestId: "abc123", + payload: { + a: "1", + }, + }); + expect(console.info).toHaveBeenCalledWith( + "[myinstance]", + "Request abc123: Sending request.", + { + a: "1", + }, + ); + }); + it("logs onNetworkResponse with parsedBody", () => { + logEnabled = true; + build(); + logger.logOnNetworkResponse({ + parsedBody: { + a: "1", + }, + body: "thebody", + requestId: "abc123", + statusCode: 200, + }); + expect(console.info).toHaveBeenCalledWith( + "[myinstance]", + "Request abc123: Received response with status code 200 and response body:", + { + a: "1", + }, + ); + }); + it("logs onNetworkResponse with no parsedBody", () => { + logEnabled = true; + build(); + logger.logOnNetworkResponse({ + body: "thebody", + requestId: "abc123", + statusCode: 200, + }); + expect(console.info).toHaveBeenCalledWith( + "[myinstance]", + "Request abc123: Received response with status code 200 and response body:", + "thebody", + ); + }); + it("logs onNetworkResponse with no response body", () => { + logEnabled = true; + build(); + logger.logOnNetworkResponse({ + body: "", + requestId: "abc123", + statusCode: 200, + }); + expect(console.info).toHaveBeenCalledWith( + "[myinstance]", + "Request abc123: Received response with status code 200 and no response body.", + "", + ); + }); + it("logs onNetworkError", () => { + logEnabled = true; + build(); + logger.logOnNetworkError({ + requestId: "abc123", + error: "myerror", + }); + expect(console.error).toHaveBeenCalledWith( + "[myinstance]", + "Request abc123: Network request failed.", + "myerror", + ); + }); +}); diff --git a/vtest/unit/specs/core/edgeNetwork/handleRequestFailure.spec.js b/vtest/unit/specs/core/edgeNetwork/handleRequestFailure.spec.js new file mode 100644 index 000000000..4da51285c --- /dev/null +++ b/vtest/unit/specs/core/edgeNetwork/handleRequestFailure.spec.js @@ -0,0 +1,32 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, describe, it, expect } from "vitest"; +import handleRequestFailure from "../../../../../src/core/edgeNetwork/handleRequestFailure.js"; + +describe("handleRequestFailure", () => { + it("works", () => { + const onRequestFailureCallbackAggregator = { + add: vi.fn(), + call: vi.fn(), + }; + onRequestFailureCallbackAggregator.call.mockReturnValue(Promise.resolve()); + const error = new Error("woopsie"); + handleRequestFailure(onRequestFailureCallbackAggregator)(error).catch( + (err) => { + expect(onRequestFailureCallbackAggregator.call).toHaveBeenCalledWith({ + error, + }); + expect(err).toEqual(error); + }, + ); + }); +}); diff --git a/vtest/unit/specs/core/edgeNetwork/injectApplyResponse.spec.js b/vtest/unit/specs/core/edgeNetwork/injectApplyResponse.spec.js new file mode 100644 index 000000000..06c071ad1 --- /dev/null +++ b/vtest/unit/specs/core/edgeNetwork/injectApplyResponse.spec.js @@ -0,0 +1,282 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectApplyResponse from "../../../../../src/core/edgeNetwork/injectApplyResponse.js"; +import assertFunctionCallOrder from "../../../helpers/assertFunctionCallOrder.js"; +import { defer } from "../../../../../src/utils/index.js"; +import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; + +describe("injectApplyResponse", () => { + let lifecycle; + let cookieTransfer; + let processWarningsAndErrors; + let createResponse; + let applyResponse; + let request; + let response; + let responseHeaders; + let responseBody; + const testApplyResponseSuccess = ({ + runOnResponseCallbacks, + assertLifecycleCall, + }) => { + const successHandler = vi.fn(); + applyResponse({ + request, + responseHeaders, + responseBody, + runOnResponseCallbacks, + }).then(successHandler); + return flushPromiseChains() + .then(() => { + expect(successHandler).not.toHaveBeenCalled(); + assertLifecycleCall(); + expect(lifecycle.onResponse).toHaveBeenCalledWith({ + response, + }); + return flushPromiseChains(); + }) + .then(() => { + expect(successHandler).toHaveBeenCalled(); + }); + }; + beforeEach(() => { + lifecycle = { + onBeforeRequest: vi.fn().mockReturnValue(Promise.resolve()), + onRequestFailure: vi.fn().mockReturnValue(Promise.resolve()), + onResponse: vi.fn().mockReturnValue(Promise.resolve()), + }; + cookieTransfer = { + cookiesToPayload: vi.fn(), + responseToCookies: vi.fn(), + }; + processWarningsAndErrors = vi.fn(); + request = { + getId: vi.fn().mockReturnValue("RID123"), + getAction: vi.fn().mockReturnValue("test-action"), + getPayload: vi.fn().mockReturnValue({ + type: "payload", + }), + getUseIdThirdPartyDomain: vi.fn().mockReturnValue(false), + getUseSendBeacon: vi.fn().mockReturnValue(false), + }; + responseHeaders = { + "x-hello": "yep", + }; + responseBody = { + handle: [], + }; + response = { + type: "response", + }; + createResponse = vi.fn().mockReturnValue(response); + applyResponse = injectApplyResponse({ + cookieTransfer, + lifecycle, + createResponse, + processWarningsAndErrors, + }); + }); + it("calls lifecycle.onResponse, waits for it to complete, then resolves promise", () => { + const deferred = defer(); + lifecycle.onResponse.mockReturnValue(deferred.promise); + return testApplyResponseSuccess({ + assertLifecycleCall() { + expect(lifecycle.onResponse).toHaveBeenCalledWith({ + response, + }); + deferred.resolve(); + }, + }); + }); + it("calls lifecycle.onBeforeRequest's responseCallback callback, waits for it to complete, then resolves promise", () => { + const deferred = defer(); + const responseCallback = vi.fn().mockReturnValue(deferred.promise); + lifecycle.onBeforeRequest.mockImplementation(({ onResponse }) => { + onResponse(responseCallback); + return Promise.resolve(); + }); + return testApplyResponseSuccess({ + assertLifecycleCall() { + expect(responseCallback).toHaveBeenCalledWith({ + response, + }); + deferred.resolve(); + }, + }); + }); + it("calls runOnResponseCallbacks, waits for it to complete, then resolves promise", () => { + const deferred = defer(); + const runOnResponseCallbacks = vi.fn().mockReturnValue(deferred.promise); + return testApplyResponseSuccess({ + runOnResponseCallbacks, + assertLifecycleCall() { + expect(runOnResponseCallbacks).toHaveBeenCalledWith({ + response, + }); + deferred.resolve(); + }, + }); + }); + it("transfers cookies from response before lifecycle.onResponse", () => { + return applyResponse({ + request, + responseHeaders, + responseBody, + }).then(() => { + expect(cookieTransfer.responseToCookies).toHaveBeenCalledWith(response); + assertFunctionCallOrder([ + cookieTransfer.responseToCookies, + lifecycle.onResponse, + ]); + }); + }); + it("returns the merged object from lifecycle::onResponse and runOnResponseCallbacks", () => { + const runOnResponseCallbacks = vi.fn().mockReturnValue( + Promise.resolve([ + { + c: 2, + }, + { + h: 9, + }, + undefined, + ]), + ); + lifecycle.onResponse.mockReturnValue( + Promise.resolve([ + { + a: 2, + }, + { + b: 8, + }, + undefined, + ]), + ); + return expect( + applyResponse({ + request, + responseHeaders, + responseBody, + runOnResponseCallbacks, + }), + ).resolves.toStrictEqual({ + c: 2, + h: 9, + a: 2, + b: 8, + }); + }); + it("returns the merged object from lifecycle::onBeforeRequest & lifecycle::onResponse", () => { + lifecycle.onBeforeRequest.mockImplementation(({ onResponse }) => { + onResponse(() => ({ + a: 1, + })); + onResponse(() => ({ + b: 1, + })); + onResponse(() => undefined); + return Promise.resolve(); + }); + lifecycle.onResponse.mockReturnValue( + Promise.resolve([ + { + c: 2, + }, + ]), + ); + return expect( + applyResponse({ + request, + responseHeaders, + responseBody, + }), + ).resolves.toStrictEqual({ + a: 1, + b: 1, + c: 2, + }); + }); + it("creates the response with the correct parameters", () => { + return applyResponse({ + request, + responseHeaders, + responseBody, + }).then(() => { + expect(createResponse).toHaveBeenCalledWith({ + content: responseBody, + getHeader: expect.any(Function), + }); + }); + }); + it("catches when warnings and errors in response", () => { + const error = new Error("whoopsie"); + processWarningsAndErrors = vi.fn().mockImplementation(() => { + throw error; + }); + applyResponse = injectApplyResponse({ + cookieTransfer, + lifecycle, + createResponse, + processWarningsAndErrors, + }); + const runOnResponseCallbacks = vi.fn(); + const runOnRequestFailureCallbacks = vi.fn(); + return applyResponse({ + request, + responseHeaders, + responseBody, + runOnResponseCallbacks, + runOnRequestFailureCallbacks, + }) + .catch((err) => { + expect(runOnRequestFailureCallbacks).toHaveBeenCalledWith({ + error, + }); + expect(err).toEqual(error); + }) + .then(() => {}); + }); + it("returns combined result", () => { + const runOnResponseCallbacks = vi.fn().mockReturnValue( + Promise.resolve([ + { + c: 2, + }, + { + h: 9, + }, + undefined, + ]), + ); + return applyResponse({ + request, + responseHeaders, + responseBody, + runOnResponseCallbacks, + }).then((result) => { + expect(runOnResponseCallbacks).toHaveBeenCalledWith({ + response, + }); + assertFunctionCallOrder([ + cookieTransfer.responseToCookies, + lifecycle.onResponse, + ]); + expect(result).not.toBeNull(); + expect(result).toEqual({ + c: 2, + h: 9, + }); + }); + }); +}); diff --git a/vtest/unit/specs/core/edgeNetwork/injectExtractEdgeInfo.spec.js b/vtest/unit/specs/core/edgeNetwork/injectExtractEdgeInfo.spec.js new file mode 100644 index 000000000..0148f5be1 --- /dev/null +++ b/vtest/unit/specs/core/edgeNetwork/injectExtractEdgeInfo.spec.js @@ -0,0 +1,69 @@ +/* +Copyright 2021 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectExtractEdgeInfo from "../../../../../src/core/edgeNetwork/injectExtractEdgeInfo.js"; + +describe("extractEdgeInfo", () => { + let logger; + let extractEdgeInfo; + beforeEach(() => { + logger = { + warn: vi.fn(), + }; + extractEdgeInfo = injectExtractEdgeInfo({ + logger, + }); + }); + [undefined, ""].forEach((input) => { + it(`doesn't log for missing header "${input}"`, () => { + expect(extractEdgeInfo(input)).toEqual({}); + expect(logger.warn).not.toHaveBeenCalled(); + }); + }); + ["OR2", "VA6;", "VA6;bad"].forEach((input) => { + it(`handles invalid header "${input}"`, () => { + expect(extractEdgeInfo(input)).toEqual({}); + expect(logger.warn).toHaveBeenCalled(); + }); + }); + [ + [ + "OR2;9", + { + regionId: 9, + }, + ], + [ + "OR2;9;other info", + { + regionId: 9, + }, + ], + [ + "OR2;011", + { + regionId: 11, + }, + ], + [ + "VA7;-1", + { + regionId: -1, + }, + ], + ].forEach(([input, expectedOutput]) => { + it(`parses "${input}" correctly`, () => { + expect(extractEdgeInfo(input)).toEqual(expectedOutput); + }); + }); +}); diff --git a/vtest/unit/specs/core/edgeNetwork/injectGetLocationHint.spec.js b/vtest/unit/specs/core/edgeNetwork/injectGetLocationHint.spec.js new file mode 100644 index 000000000..bbd426140 --- /dev/null +++ b/vtest/unit/specs/core/edgeNetwork/injectGetLocationHint.spec.js @@ -0,0 +1,55 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectGetLocationHint from "../../../../../src/core/edgeNetwork/injectGetLocationHint.js"; + +describe("injectGetLocationHint", () => { + let cookieJar; + let orgId; + let getLocationHint; + beforeEach(() => { + cookieJar = { + get: vi.fn(), + }; + orgId = "myorg@AdobeOrg"; + getLocationHint = injectGetLocationHint({ + orgId, + cookieJar, + }); + }); + it("returns the cluster cookie", () => { + cookieJar.get.mockReturnValue("mycluster"); + expect(getLocationHint()).toEqual("mycluster"); + }); + it("generates the correct cookie name", () => { + cookieJar.get.mockReturnValue("mycluster"); + getLocationHint(); + expect(cookieJar.get).toHaveBeenNthCalledWith( + 1, + "kndctr_myorg_AdobeOrg_cluster", + ); + }); + it("doesn't cache the result", () => { + cookieJar.get + .mockReturnValueOnce("cluster1") + .mockReturnValueOnce("cluster2"); + expect(getLocationHint()).toEqual("cluster1"); + expect(getLocationHint()).toEqual("cluster2"); + }); + it("returns mbox edge cluster cookie", () => { + cookieJar.get.mockReturnValueOnce(undefined).mockReturnValueOnce("35"); + expect(getLocationHint()).toEqual("t35"); + }); + it("returns undefined", () => { + expect(getLocationHint()).toBeUndefined(); + }); +}); diff --git a/vtest/unit/specs/core/edgeNetwork/injectProcessWarningsAndErrors.spec.js b/vtest/unit/specs/core/edgeNetwork/injectProcessWarningsAndErrors.spec.js new file mode 100644 index 000000000..01002527f --- /dev/null +++ b/vtest/unit/specs/core/edgeNetwork/injectProcessWarningsAndErrors.spec.js @@ -0,0 +1,122 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectProcessWarningsAndErrors from "../../../../../src/core/edgeNetwork/injectProcessWarningsAndErrors.js"; + +describe("processWarningsAndErrors", () => { + let logger; + let processWarningsAndErrors; + beforeEach(() => { + logger = { + warn: vi.fn(), + error: vi.fn(), + }; + processWarningsAndErrors = injectProcessWarningsAndErrors({ + logger, + }); + }); + it("throws error if status code is below 2xx", () => { + expect(() => { + processWarningsAndErrors({ + statusCode: 199, + }); + }).toThrowError( + "The server responded with a status code 199 and no response body.", + ); + }); + it("throws error if status code is above 2xx", () => { + expect(() => { + processWarningsAndErrors({ + statusCode: 300, + }); + }).toThrowError( + "The server responded with a status code 300 and no response body.", + ); + }); + it("throws error if no parsed body and HTTP status code is not 204", () => { + expect(() => { + processWarningsAndErrors({ + statusCode: 200, + }); + }).toThrowError( + "The server responded with a status code 200 and no response body.", + ); + }); + it("throws an error if parsed body does not have handle array", () => { + expect(() => { + processWarningsAndErrors({ + statusCode: 200, + body: '{"foo":"bar"}', + parsedBody: { + foo: "bar", + }, + }); + }).toThrowError( + 'The server responded with a status code 200 and response body:\n{\n "foo": "bar"\n}', + ); + }); + it("logs warnings", () => { + const warnings = [ + { + title: "General warning", + detail: "General warning detail", + }, + { + title: "Personalization warning", + detail: "Personalization warning detail", + }, + ]; + processWarningsAndErrors({ + statusCode: 200, + parsedBody: { + handle: [], + warnings, + }, + }); + expect(logger.warn).toHaveBeenCalledWith( + "The server responded with a warning:", + warnings[0], + ); + expect(logger.warn).toHaveBeenCalledWith( + "The server responded with a warning:", + warnings[1], + ); + }); + it("logs non-fatal errors", () => { + const errors = [ + { + title: "General warning", + detail: "General warning detail", + }, + { + title: "Personalization warning", + detail: "Personalization warning detail", + }, + ]; + processWarningsAndErrors({ + statusCode: 207, + parsedBody: { + handle: [], + errors, + }, + }); + expect(logger.error).toHaveBeenCalledWith( + "The server responded with a non-fatal error:", + errors[0], + ); + expect(logger.error).toHaveBeenCalledWith( + "The server responded with a non-fatal error:", + errors[1], + ); + }); +}); diff --git a/vtest/unit/specs/core/edgeNetwork/injectSendEdgeNetworkRequest.spec.js b/vtest/unit/specs/core/edgeNetwork/injectSendEdgeNetworkRequest.spec.js new file mode 100644 index 000000000..d21f3573f --- /dev/null +++ b/vtest/unit/specs/core/edgeNetwork/injectSendEdgeNetworkRequest.spec.js @@ -0,0 +1,555 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectSendEdgeNetworkRequest from "../../../../../src/core/edgeNetwork/injectSendEdgeNetworkRequest.js"; +import createConfig from "../../../../../src/core/config/createConfig.js"; +import { defer } from "../../../../../src/utils/index.js"; +import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; +import assertFunctionCallOrder from "../../../helpers/assertFunctionCallOrder.js"; + +describe("injectSendEdgeNetworkRequest", () => { + let config; + let logger; + let lifecycle; + let cookieTransfer; + let networkResult; + let sendNetworkRequest; + let response; + let createResponse; + let processWarningsAndErrors; + let getLocationHint; + let getAssuranceValidationTokenParams; + let sendEdgeNetworkRequest; + let payload; + let request; + + // Helper for testing handling of network request failures, particularly + // their interplay with lifecycle hooks. + const testRequestFailureHandling = ({ + runOnRequestFailureCallbacks, + assertLifecycleCall, + }) => { + const error = new Error("no connection"); + sendNetworkRequest.mockReturnValue(Promise.reject(error)); + const errorHandler = vi.fn(); + sendEdgeNetworkRequest({ + request, + runOnRequestFailureCallbacks, + }).catch(errorHandler); + return flushPromiseChains() + .then(() => { + expect(errorHandler).not.toHaveBeenCalled(); + assertLifecycleCall(error); + return flushPromiseChains(); + }) + .then(() => { + expect(errorHandler).toHaveBeenCalledWith(error); + }); + }; + + // Helper for testing handling of fatal error responses from the server, particularly + // their interplay with lifecycle hooks. + + const testResponseFailureHandling = ({ + runOnRequestFailureCallbacks, + assertLifecycleCall, + }) => { + const error = new Error("Unexpected response."); + processWarningsAndErrors.mockImplementation(() => { + throw error; + }); + const errorHandler = vi.fn(); + + sendEdgeNetworkRequest({ + request, + runOnRequestFailureCallbacks, + }).catch(errorHandler); + + return flushPromiseChains() + .then(() => { + expect(errorHandler).not.toHaveBeenCalled(); + assertLifecycleCall(error); + return flushPromiseChains(); + }) + .then(() => { + expect(errorHandler).toHaveBeenCalledWith(error); + }); + }; + + // Helper for testing handling of successful network responses, particularly + // their interplay with lifecycle hooks. + const testResponseSuccessHandling = ({ + runOnResponseCallbacks, + assertLifecycleCall, + }) => { + const successHandler = vi.fn(); + sendEdgeNetworkRequest({ + request, + runOnResponseCallbacks, + }).then(successHandler); + return flushPromiseChains() + .then(() => { + expect(successHandler).not.toHaveBeenCalled(); + assertLifecycleCall(); + expect(lifecycle.onResponse).toHaveBeenCalledWith({ + response, + }); + return flushPromiseChains(); + }) + .then(() => { + expect(successHandler).toHaveBeenCalled(); + }); + }; + beforeEach(() => { + config = createConfig({ + edgeDomain: "edge.example.com", + edgeBasePath: "ee", + datastreamId: "myconfigId", + }); + payload = { + mergeMeta: vi.fn(), + type: vi.fn().mockReturnValue("payload"), + }; + request = { + getId: vi.fn().mockReturnValue("RID123"), + getAction: vi.fn().mockReturnValue("test-action"), + getPayload: vi.fn().mockReturnValue(payload), + getUseIdThirdPartyDomain: vi.fn().mockReturnValue(false), + getUseSendBeacon: vi.fn().mockReturnValue(false), + getDatastreamIdOverride: vi.fn().mockReturnValue(""), + getEdgeSubPath: vi.fn().mockReturnValue(""), + }; + logger = { + info: vi.fn(), + }; + lifecycle = { + onBeforeRequest: vi.fn().mockReturnValue(Promise.resolve()), + onRequestFailure: vi.fn().mockReturnValue(Promise.resolve()), + onResponse: vi.fn().mockReturnValue(Promise.resolve()), + }; + cookieTransfer = { + cookiesToPayload: vi.fn(), + responseToCookies: vi.fn(), + }; + networkResult = { + parsedBody: { + my: "parsedBody", + }, + getHeader: () => "myheader", + }; + sendNetworkRequest = vi + .fn() + .mockReturnValue(Promise.resolve(networkResult)); + response = { + type: "response", + }; + createResponse = vi.fn().mockReturnValue(response); + processWarningsAndErrors = vi.fn(); + getLocationHint = vi.fn(); + getAssuranceValidationTokenParams = vi.fn().mockReturnValue(""); + sendEdgeNetworkRequest = injectSendEdgeNetworkRequest({ + config, + logger, + lifecycle, + cookieTransfer, + sendNetworkRequest, + createResponse, + processWarningsAndErrors, + getLocationHint, + getAssuranceValidationTokenParams, + }); + }); + it("transfers cookies to payload when sending to first-party domain", () => { + return sendEdgeNetworkRequest({ + request, + }).then(() => { + expect(cookieTransfer.cookiesToPayload).toHaveBeenCalledWith( + payload, + "edge.example.com", + ); + }); + }); + it("transfers cookies to payload when sending to third-party domain", () => { + // Ensure that sendEdgeNetworkRequest waits until after + // lifecycle.onBeforeRequest to determine the endpoint domain. + lifecycle.onBeforeRequest.mockImplementation(() => { + request.getUseIdThirdPartyDomain.mockReturnValue(true); + return Promise.resolve(); + }); + return sendEdgeNetworkRequest({ + request, + }).then(() => { + expect(cookieTransfer.cookiesToPayload).toHaveBeenCalledWith( + payload, + "adobedc.demdex.net", + ); + }); + }); + it("sends request to first-party domain", () => { + return sendEdgeNetworkRequest({ + request, + }).then(() => { + expect(sendNetworkRequest).toHaveBeenCalledWith({ + requestId: "RID123", + url: "https://edge.example.com/ee/v1/test-action?configId=myconfigId&requestId=RID123", + payload, + useSendBeacon: false, + }); + }); + }); + it("sends request to third-party domain", () => { + // Ensure that sendEdgeNetworkRequest waits until after + // lifecycle.onBeforeRequest to determine the endpoint domain. + lifecycle.onBeforeRequest.mockImplementation(() => { + request.getUseIdThirdPartyDomain.mockReturnValue(true); + return Promise.resolve(); + }); + return sendEdgeNetworkRequest({ + request, + }).then(() => { + expect(sendNetworkRequest).toHaveBeenCalledWith({ + requestId: "RID123", + url: "https://adobedc.demdex.net/ee/v1/test-action?configId=myconfigId&requestId=RID123", + payload, + useSendBeacon: false, + }); + }); + }); + it("sends request using sendBeacon", () => { + // Ensure that sendEdgeNetworkRequest waits until after + // lifecycle.onBeforeRequest to determine whether to use sendBeacon. + lifecycle.onBeforeRequest.mockImplementation(() => { + request.getUseSendBeacon.mockReturnValue(true); + return Promise.resolve(); + }); + return sendEdgeNetworkRequest({ + request, + }).then(() => { + expect(sendNetworkRequest).toHaveBeenCalledWith({ + requestId: "RID123", + url: "https://edge.example.com/ee/v1/test-action?configId=myconfigId&requestId=RID123", + payload, + useSendBeacon: true, + }); + }); + }); + it("calls lifecycle.onBeforeRequest and waits for it to complete before sending request", () => { + const deferred = defer(); + lifecycle.onBeforeRequest.mockReturnValue(deferred.promise); + const successHandler = vi.fn(); + sendEdgeNetworkRequest({ + request, + }).then(successHandler); + return flushPromiseChains() + .then(() => { + expect(lifecycle.onBeforeRequest).toHaveBeenCalledWith({ + request, + onResponse: expect.any(Function), + onRequestFailure: expect.any(Function), + }); + expect(sendNetworkRequest).not.toHaveBeenCalled(); + deferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(successHandler).toHaveBeenCalled(); + }); + }); + it("when network request fails, calls lifecycle.onRequestFailure, waits for it to complete, then rejects promise", () => { + const deferred = defer(); + lifecycle.onRequestFailure.mockReturnValue(deferred.promise); + return testRequestFailureHandling({ + assertLifecycleCall(error) { + expect(lifecycle.onRequestFailure).toHaveBeenCalledWith({ + error, + }); + // We reject this deferred to simulate a component throwing an error + // during the lifecycle.onRequestFailure hook. This tests that the + // promise from sendEdgeNetworkRequest is still rejected with the + // network error rather than the error coming from a component. + deferred.reject(); + }, + }); + }); + it("when network request fails, calls lifecycle.onBeforeRequest's onRequestFailure callback, waits for it to complete, then rejects promise", () => { + const deferred = defer(); + const requestFailureCallback = vi.fn().mockReturnValue(deferred.promise); + lifecycle.onBeforeRequest.mockImplementation(({ onRequestFailure }) => { + onRequestFailure(requestFailureCallback); + return Promise.resolve(); + }); + return testRequestFailureHandling({ + assertLifecycleCall(error) { + expect(requestFailureCallback).toHaveBeenCalledWith({ + error, + }); + // We reject this deferred to simulate a component throwing an error + // during the lifecycle.onBeforeRequest's onRequestFailure callback. + // This tests that the promise from sendEdgeNetworkRequest is still + // rejected with the network error rather than the error coming from + // a component. + deferred.reject(); + }, + }); + }); + it("when network request fails, calls onRequestFailureCallbacks, waits for it to complete, then rejects promise", () => { + const deferred = defer(); + const runOnRequestFailureCallbacks = vi + .fn() + .mockReturnValue(deferred.promise); + return testRequestFailureHandling({ + runOnRequestFailureCallbacks, + assertLifecycleCall(error) { + expect(runOnRequestFailureCallbacks).toHaveBeenCalledWith({ + error, + }); + // We reject this deferred to simulate a component throwing an error + // during the runOnRequestFailureCallbacks call. This tests that the + // promise from sendEdgeNetworkRequest is still rejected with the + // network error rather than the error coming from a component. + deferred.reject(); + }, + }); + }); + it("when network response is a failure, calls lifecycle.onRequestFailure, waits for it to complete, then rejects promise", () => { + const deferred = defer(); + lifecycle.onRequestFailure.mockReturnValue(deferred.promise); + return testResponseFailureHandling({ + assertLifecycleCall(error) { + expect(lifecycle.onRequestFailure).toHaveBeenCalledWith({ + error, + }); + // We reject this deferred to simulate a component throwing an error + // during the lifecycle.onRequestFailure hook. This tests that the + // promise from sendEdgeNetworkRequest is still rejected with the + // network error rather than the error coming from a component. + deferred.reject(); + }, + }); + }); + it("when network response is a failure, calls lifecycle.onBeforeRequest's onRequestFailure callback, waits for it to complete, then rejects promise", () => { + const deferred = defer(); + const requestFailureCallback = vi.fn().mockReturnValue(deferred.promise); + lifecycle.onBeforeRequest.mockImplementation(({ onRequestFailure }) => { + onRequestFailure(requestFailureCallback); + return Promise.resolve(); + }); + return testResponseFailureHandling({ + assertLifecycleCall(error) { + expect(requestFailureCallback).toHaveBeenCalledWith({ + error, + }); + // We reject this deferred to simulate a component throwing an error + // during the lifecycle.onBeforeRequest's onRequestFailure callback. + // This tests that the promise from sendEdgeNetworkRequest is still + // rejected with the network error rather than the error coming from + // a component. + deferred.reject(); + }, + }); + }); + it("when network response is a failure, calls runOnRequestFailureCallbacks, waits for it to complete, then rejects promise", () => { + const deferred = defer(); + const runOnRequestFailureCallbacks = vi + .fn() + .mockReturnValue(deferred.promise); + return testResponseFailureHandling({ + runOnRequestFailureCallbacks, + assertLifecycleCall(error) { + expect(runOnRequestFailureCallbacks).toHaveBeenCalledWith({ + error, + }); + // We reject this deferred to simulate a component throwing an error + // during the runOnRequestFailureCallbacks call. This tests that the + // promise from sendEdgeNetworkRequest is still rejected with the + // network error rather than the error coming from a component. + deferred.reject(); + }, + }); + }); + it("when network response is a success, calls lifecycle.onResponse, waits for it to complete, then resolves promise", () => { + const deferred = defer(); + lifecycle.onResponse.mockReturnValue(deferred.promise); + return testResponseSuccessHandling({ + assertLifecycleCall() { + expect(lifecycle.onResponse).toHaveBeenCalledWith({ + response, + }); + deferred.resolve(); + }, + }); + }); + it("when network response is a success, calls lifecycle.onBeforeRequest's responseCallback callback, waits for it to complete, then resolves promise", () => { + const deferred = defer(); + const responseCallback = vi.fn().mockReturnValue(deferred.promise); + lifecycle.onBeforeRequest.mockImplementation(({ onResponse }) => { + onResponse(responseCallback); + return Promise.resolve(); + }); + return testResponseSuccessHandling({ + assertLifecycleCall() { + expect(responseCallback).toHaveBeenCalledWith({ + response, + }); + deferred.resolve(); + }, + }); + }); + it("when network response is a success, calls runOnResponseCallbacks, waits for it to complete, then resolves promise", () => { + const deferred = defer(); + const runOnResponseCallbacks = vi.fn().mockReturnValue(deferred.promise); + return testResponseSuccessHandling({ + runOnResponseCallbacks, + assertLifecycleCall() { + expect(runOnResponseCallbacks).toHaveBeenCalledWith({ + response, + }); + deferred.resolve(); + }, + }); + }); + it("transfers cookies from response before lifecycle.onResponse", () => { + return sendEdgeNetworkRequest({ + request, + }).then(() => { + expect(cookieTransfer.responseToCookies).toHaveBeenCalledWith(response); + assertFunctionCallOrder([ + cookieTransfer.responseToCookies, + lifecycle.onResponse, + ]); + }); + }); + it("returns the merged object from lifecycle::onResponse and runOnResponseCallbacks", () => { + const runOnResponseCallbacks = vi.fn().mockReturnValue( + Promise.resolve([ + { + c: 2, + }, + { + h: 9, + }, + undefined, + ]), + ); + lifecycle.onResponse.mockReturnValue( + Promise.resolve([ + { + a: 2, + }, + { + b: 8, + }, + undefined, + ]), + ); + return expect( + sendEdgeNetworkRequest({ + request, + runOnResponseCallbacks, + }), + ).resolves.toStrictEqual({ + c: 2, + h: 9, + a: 2, + b: 8, + }); + }); + it("returns the merged object from lifecycle::onBeforeRequest & lifecycle::onResponse", () => { + lifecycle.onBeforeRequest.mockImplementation(({ onResponse }) => { + onResponse(() => ({ + a: 1, + })); + onResponse(() => ({ + b: 1, + })); + onResponse(() => undefined); + return Promise.resolve(); + }); + lifecycle.onResponse.mockReturnValue( + Promise.resolve([ + { + c: 2, + }, + ]), + ); + return expect( + sendEdgeNetworkRequest({ + request, + }), + ).resolves.toStrictEqual({ + a: 1, + b: 1, + c: 2, + }); + }); + it("creates the response with the correct parameters", () => { + return sendEdgeNetworkRequest({ + request, + }).then(() => { + expect(createResponse).toHaveBeenCalledWith({ + content: { + my: "parsedBody", + }, + getHeader: networkResult.getHeader, + }); + }); + }); + it("uses the cluster cookie location hint", () => { + getLocationHint.mockReturnValue("va6"); + return sendEdgeNetworkRequest({ + request, + }).then(() => { + expect(sendNetworkRequest).toHaveBeenCalledWith({ + requestId: "RID123", + url: "https://edge.example.com/ee/va6/v1/test-action?configId=myconfigId&requestId=RID123", + payload, + useSendBeacon: false, + }); + }); + }); + it("sets validation token params", () => { + getAssuranceValidationTokenParams.mockReturnValue( + "&adobeAepValidationToken=abc-123", + ); + return sendEdgeNetworkRequest({ + request, + }).then(() => { + expect(sendNetworkRequest).toHaveBeenCalledWith({ + requestId: "RID123", + url: "https://edge.example.com/ee/v1/test-action?configId=myconfigId&requestId=RID123&adobeAepValidationToken=abc-123", + payload, + useSendBeacon: false, + }); + }); + }); + it("respects the datastreamIdOverride", () => { + request.getDatastreamIdOverride.mockReturnValue("myconfigIdOverride"); + return sendEdgeNetworkRequest({ + request, + }).then(() => { + expect(payload.mergeMeta).toHaveBeenCalledWith({ + sdkConfig: { + datastream: { + original: "myconfigId", + }, + }, + }); + expect(sendNetworkRequest).toHaveBeenCalledWith({ + payload, + url: "https://edge.example.com/ee/v1/test-action?configId=myconfigIdOverride&requestId=RID123", + requestId: "RID123", + useSendBeacon: false, + }); + }); + }); +}); diff --git a/vtest/unit/specs/core/edgeNetwork/mergeLifecycleResponses.spec.js b/vtest/unit/specs/core/edgeNetwork/mergeLifecycleResponses.spec.js new file mode 100644 index 000000000..19bf5757a --- /dev/null +++ b/vtest/unit/specs/core/edgeNetwork/mergeLifecycleResponses.spec.js @@ -0,0 +1,116 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import mergeLifecycleResponses from "../../../../../src/core/edgeNetwork/mergeLifecycleResponses.js"; + +describe("mergeLifecycleResponses", () => { + it("works", () => { + expect( + mergeLifecycleResponses([ + [ + null, + { + destinations: [], + }, + null, + ], + [ + { + propositions: [], + }, + { + decisions: [ + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNTYzMTcwIiwiZXhwZXJpZW5jZUlkIjoiMCJ9", + scope: "superfluous", + items: [ + { + id: "780724", + schema: + "https://ns.adobe.com/personalization/html-content-item", + data: { + id: "1", + format: "text/html", + content: "
    hi
    ", + }, + }, + ], + }, + ], + propositions: [ + { + renderAttempted: true, + id: "AT:eyJhY3Rpdml0eUlkIjoiNTYzMTY5IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", + scope: "__view__", + items: [ + { + id: "0", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "insertAfter", + format: "application/vnd.adobe.target.dom-action", + content: "
    hai
    ", + selector: "HTML > BODY > H3:nth-of-type(1)", + prehidingSelector: "HTML > BODY > H3:nth-of-type(1)", + }, + }, + ], + }, + ], + }, + { + propositions: [], + }, + ], + ]), + ).toEqual({ + destinations: [], + propositions: [ + { + renderAttempted: true, + id: "AT:eyJhY3Rpdml0eUlkIjoiNTYzMTY5IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", + scope: "__view__", + items: [ + { + id: "0", + schema: "https://ns.adobe.com/personalization/dom-action", + data: { + type: "insertAfter", + format: "application/vnd.adobe.target.dom-action", + content: "
    hai
    ", + selector: "HTML > BODY > H3:nth-of-type(1)", + prehidingSelector: "HTML > BODY > H3:nth-of-type(1)", + }, + }, + ], + }, + ], + decisions: [ + { + id: "AT:eyJhY3Rpdml0eUlkIjoiNTYzMTcwIiwiZXhwZXJpZW5jZUlkIjoiMCJ9", + scope: "superfluous", + items: [ + { + id: "780724", + schema: "https://ns.adobe.com/personalization/html-content-item", + data: { + id: "1", + format: "text/html", + content: "
    hi
    ", + }, + }, + ], + }, + ], + }); + }); +}); diff --git a/vtest/unit/specs/core/initializeComponents.spec.js b/vtest/unit/specs/core/initializeComponents.spec.js new file mode 100644 index 000000000..b18fcc13f --- /dev/null +++ b/vtest/unit/specs/core/initializeComponents.spec.js @@ -0,0 +1,99 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import initializeComponents from "../../../../src/core/initializeComponents.js"; + +describe("initializeComponents", () => { + let lifecycle; + let componentRegistry; + let componentByNamespace; + let componentCreators; + let getImmediatelyAvailableTools; + beforeEach(() => { + lifecycle = { + onComponentsRegistered: vi.fn().mockReturnValue(Promise.resolve()), + }; + componentRegistry = { + register: vi.fn(), + }; + componentByNamespace = { + Comp1: {}, + Comp2: {}, + }; + const componentCreator1 = vi + .fn() + .mockReturnValue(componentByNamespace.Comp1); + componentCreator1.namespace = "Comp1"; + const componentCreator2 = vi + .fn() + .mockReturnValue(componentByNamespace.Comp2); + componentCreator2.namespace = "Comp2"; + componentCreators = [componentCreator1, componentCreator2]; + getImmediatelyAvailableTools = (componentName) => { + return { + tool1: { + name: "tool1", + componentName, + }, + tool2: { + name: "tool2", + componentName, + }, + }; + }; + }); + it("creates and registers components", () => { + const initializeComponentsPromise = initializeComponents({ + componentCreators, + lifecycle, + componentRegistry, + getImmediatelyAvailableTools, + }); + componentCreators.forEach((componentCreator) => { + const { namespace } = componentCreator; + expect(componentCreator).toHaveBeenCalledWith({ + tool1: { + name: "tool1", + componentName: componentCreator.namespace, + }, + tool2: { + name: "tool2", + componentName: componentCreator.namespace, + }, + }); + expect(componentRegistry.register).toHaveBeenCalledWith( + namespace, + componentByNamespace[namespace], + ); + }); + expect(lifecycle.onComponentsRegistered).toHaveBeenCalledWith({ + lifecycle, + }); + return initializeComponentsPromise.then((result) => { + expect(result).toBe(componentRegistry); + }); + }); + it("throws error if component throws error during creation", () => { + componentCreators[1].mockImplementation(() => { + throw new Error("thrownError"); + }); + expect(() => { + initializeComponents({ + componentCreators, + lifecycle, + componentRegistry, + getImmediatelyAvailableTools, + }); + }).toThrowError(/\[Comp2\] An error occurred during component creation./); + }); +}); diff --git a/vtest/unit/specs/core/injectCreateResponse.spec.js b/vtest/unit/specs/core/injectCreateResponse.spec.js new file mode 100644 index 000000000..cf26f8573 --- /dev/null +++ b/vtest/unit/specs/core/injectCreateResponse.spec.js @@ -0,0 +1,152 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectCreateResponse from "../../../../src/core/injectCreateResponse.js"; + +const responseContent = { + requestId: 123, + handle: [ + { + type: "type1", + payload: "payload1a", + }, + { + type: "type2", + payload: "payload2a", + }, + { + type: "type1", + payload: "payload1b", + }, + { + type: "type1", + payload: "payload1c", + }, + ], + errors: [ + { + code: "general:100", + message: "General error occurred.", + }, + { + code: "personalization:204", + message: "Personalization error occurred.", + }, + ], + warnings: [ + { + code: "general:101", + message: "General warning.", + }, + { + code: "personalization:205", + message: "Personalization warning.", + }, + ], +}; +describe("createResponse", () => { + let extractEdgeInfo; + let getHeader; + let createResponse; + let response; + beforeEach(() => { + extractEdgeInfo = vi.fn(); + getHeader = vi.fn(); + createResponse = injectCreateResponse({ + extractEdgeInfo, + }); + response = createResponse({ + content: responseContent, + getHeader, + }); + }); + describe("getPayloadsByType", () => { + it("handles undefined content", () => { + const emptyResponse = createResponse({ + content: undefined, + }); + expect(emptyResponse.getPayloadsByType("type1")).toEqual([]); + }); + it("handles content without handle property", () => { + const emptyResponse = createResponse({ + content: {}, + }); + expect(emptyResponse.getPayloadsByType("type1")).toEqual([]); + }); + it("returns empty array when there are no matching payloads", () => { + expect(response.getPayloadsByType("type3")).toEqual([]); + }); + it("returns one matching payload as an array", () => { + expect(response.getPayloadsByType("type2")).toEqual(["payload2a"]); + }); + it("returns three matching payloads", () => { + expect(response.getPayloadsByType("type1")).toEqual([ + "payload1a", + "payload1b", + "payload1c", + ]); + }); + }); + describe("getErrors", () => { + it("handles undefined content", () => { + const emptyResponse = createResponse({ + content: undefined, + }); + expect(emptyResponse.getErrors()).toEqual([]); + }); + it("handles content without errors property", () => { + const emptyResponse = createResponse({ + content: {}, + }); + expect(emptyResponse.getErrors()).toEqual([]); + }); + it("returns errors", () => { + expect(response.getErrors()).toBe(responseContent.errors); + }); + }); + describe("getWarnings", () => { + it("handles undefined content", () => { + const emptyResponse = createResponse({ + content: undefined, + }); + expect(emptyResponse.getWarnings()).toEqual([]); + }); + it("handles content without warnings property", () => { + const emptyResponse = createResponse({ + content: {}, + }); + expect(emptyResponse.getWarnings()).toEqual([]); + }); + it("returns warnings", () => { + expect(response.getWarnings()).toBe(responseContent.warnings); + }); + }); + describe("getEdge", () => { + it("calls extractEdgeInfo with x-adobe-edge header and returns the result", () => { + extractEdgeInfo.mockReturnValue({ + regionId: 42, + }); + getHeader.mockReturnValue("VA6;42"); + expect(response.getEdge()).toEqual({ + regionId: 42, + }); + expect(extractEdgeInfo).toHaveBeenCalledWith("VA6;42"); + expect(getHeader).toHaveBeenCalledWith("x-adobe-edge"); + }); + }); + describe("toJSON", () => { + it("returns underlying content object", () => { + expect(response.toJSON()).toBe(responseContent); + }); + }); +}); diff --git a/vtest/unit/specs/core/injectExecuteCommand.spec.js b/vtest/unit/specs/core/injectExecuteCommand.spec.js new file mode 100644 index 000000000..8989b8ad0 --- /dev/null +++ b/vtest/unit/specs/core/injectExecuteCommand.spec.js @@ -0,0 +1,325 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectExecuteCommand from "../../../../src/core/injectExecuteCommand.js"; +import flushPromiseChains from "../../helpers/flushPromiseChains.js"; + +describe("injectExecuteCommand", () => { + let logger; + let handleError; + beforeEach(() => { + logger = { + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + logOnBeforeCommand: vi.fn(), + logOnCommandResolved: vi.fn(), + logOnCommandRejected: vi.fn(), + }; + handleError = vi.fn().mockImplementation((error) => { + throw error; + }); + }); + it("rejects promise if configure is not the first command executed", () => { + const executeCommand = injectExecuteCommand({ + logger, + handleError, + }); + return executeCommand("sendEvent").catch((error) => { + expect(error.message).toContain("The library must be configured first"); + expect(handleError).toHaveBeenCalledWith(error, "sendEvent command"); + }); + }); + it("rejects promise if configure command is executed twice", () => { + const configureCommand = () => Promise.resolve(); + const executeCommand = injectExecuteCommand({ + logger, + configureCommand, + handleError, + }); + executeCommand("configure"); + return executeCommand("configure").catch((error) => { + expect(error.message).toContain( + "The library has already been configured", + ); + expect(handleError).toHaveBeenCalledWith(error, "configure command"); + }); + }); + it("rejects promise if command doesn't exist", () => { + const componentRegistry = { + getCommand() {}, + getCommandNames() { + return ["genuine"]; + }, + }; + const configureCommand = () => Promise.resolve(componentRegistry); + const executeCommand = injectExecuteCommand({ + logger, + configureCommand, + handleError, + }); + executeCommand("configure"); + return executeCommand("bogus").catch((error) => { + expect(error.message).toBe( + "The bogus command does not exist. List of available commands: configure, setDebug, genuine.", + ); + expect(handleError).toHaveBeenCalledWith(error, "bogus command"); + }); + }); + it("never resolves/rejects promise to any other command after configure fails", () => { + const configureError = new Error("Test configure command failed"); + const configureCommand = () => Promise.reject(configureError); + const executeCommand = injectExecuteCommand({ + logger, + configureCommand, + handleError, + }); + const configureRejectedSpy = vi.fn(); + executeCommand("configure").catch(configureRejectedSpy); + const sendEventResolvedSpy = vi.fn(); + const sendEventRejectedSpy = vi.fn(); + executeCommand("sendEvent") + .then(sendEventResolvedSpy) + .catch(sendEventRejectedSpy); + return flushPromiseChains().then(() => { + expect(configureRejectedSpy).toHaveBeenCalledWith(configureError); + expect(logger.warn).toHaveBeenCalledWith( + "An error during configuration is preventing the sendEvent command from executing.", + ); + expect(sendEventResolvedSpy).not.toHaveBeenCalled(); + expect(sendEventRejectedSpy).not.toHaveBeenCalled(); + }); + }); + it("reject promise if component command throws error", () => { + const runCommandSpy = vi.fn().mockImplementation(() => { + throw new Error("Unexpected error"); + }); + const testCommand = { + run: runCommandSpy, + }; + const componentRegistry = { + getCommand: () => testCommand, + getCommandNames() { + return ["test"]; + }, + }; + const configureCommand = () => Promise.resolve(componentRegistry); + const executeCommand = injectExecuteCommand({ + logger, + configureCommand, + handleError, + validateCommandOptions: (options) => options, + }); + executeCommand("configure"); + return executeCommand("test", {}).catch(() => { + expect(handleError).toHaveBeenCalledWith( + new Error("Unexpected error"), + "test command", + ); + }); + }); + it("executes component commands", () => { + const validateCommandOptionsSpy = vi + .fn() + .mockReturnValueOnce("with-result-post-validation-options") + .mockReturnValueOnce("without-result-post-validation-options"); + const testCommandWithResult = { + run: vi.fn().mockReturnValue({ + foo: "bar", + }), + }; + const testCommandWithoutResult = { + run: vi.fn().mockReturnValue(undefined), + }; + const componentRegistry = { + getCommand: vi + .fn() + .mockReturnValueOnce(testCommandWithResult) + .mockReturnValueOnce(testCommandWithoutResult), + getCommandNames() { + return ["testCommandWithResult", "testCommandWithoutResult"]; + }, + }; + const configureCommand = () => Promise.resolve(componentRegistry); + const executeCommand = injectExecuteCommand({ + logger, + configureCommand, + handleError, + validateCommandOptions: validateCommandOptionsSpy, + }); + executeCommand("configure"); + return Promise.all([ + executeCommand( + "testCommandWithResult", + "with-result-pre-validation-options", + ), + executeCommand( + "testCommandWithoutResult", + "without-result-pre-validation-options", + ), + ]).then((results) => { + expect(results[0]).toEqual({ + foo: "bar", + }); + expect(results[1]).toEqual({}); + expect(validateCommandOptionsSpy).toHaveBeenCalledWith({ + command: testCommandWithResult, + options: "with-result-pre-validation-options", + }); + expect(validateCommandOptionsSpy).toHaveBeenCalledWith({ + command: testCommandWithoutResult, + options: "without-result-pre-validation-options", + }); + expect(testCommandWithResult.run).toHaveBeenCalledWith( + "with-result-post-validation-options", + ); + expect(testCommandWithoutResult.run).toHaveBeenCalledWith( + "without-result-post-validation-options", + ); + }); + }); + it("executes the core commands", () => { + const componentRegistry = { + getCommand() {}, + }; + const configureCommand = vi + .fn() + .mockReturnValue(Promise.resolve(componentRegistry)); + const setDebugCommand = vi.fn(); + const validateCommandOptions = vi.fn().mockReturnValue({ + enabled: true, + }); + const executeCommand = injectExecuteCommand({ + logger, + configureCommand, + setDebugCommand, + handleError, + validateCommandOptions, + }); + return Promise.all([ + executeCommand("configure", { + foo: "bar", + }), + executeCommand("setDebug", { + baz: "qux", + }), + ]).then(([configureResult, setDebugResult]) => { + expect(configureCommand).toHaveBeenCalledWith({ + foo: "bar", + }); + expect(setDebugCommand).toHaveBeenCalledWith({ + enabled: true, + }); + expect(configureResult).toEqual({}); + expect(setDebugResult).toEqual({}); + }); + }); + const buildWithTestCommand = (runCommand) => { + const testCommand = { + run: runCommand, + }; + const componentRegistry = { + getCommand: () => testCommand, + getCommandNames() { + return ["test"]; + }, + }; + const configureCommand = () => Promise.resolve(componentRegistry); + return injectExecuteCommand({ + logger, + configureCommand, + handleError, + validateCommandOptions: (options) => options, + }); + }; + it("logs onBeforeCommand", () => { + const executeCommand = buildWithTestCommand(() => { + expect(logger.logOnBeforeCommand).toHaveBeenCalledWith({ + commandName: "test", + options: { + my: "options", + }, + }); + }); + executeCommand("configure"); + return executeCommand("test", { + my: "options", + }); + }); + it("logs onCommandResolved", () => { + const executeCommand = buildWithTestCommand(() => { + expect(logger.logOnCommandResolved).not.toHaveBeenCalled(); + return { + go: "bananas", + }; + }); + executeCommand("configure"); + return executeCommand("test", { + my: "options", + }).then((result) => { + expect(result).toEqual({ + go: "bananas", + }); + expect(logger.logOnCommandResolved).toHaveBeenCalledWith({ + commandName: "test", + options: { + my: "options", + }, + result: { + go: "bananas", + }, + }); + }); + }); + it("logs onCommandRejected", () => { + const myerror = Error("bananas"); + const executeCommand = buildWithTestCommand(() => { + expect(logger.logOnCommandRejected).not.toHaveBeenCalled(); + throw myerror; + }); + executeCommand("configure"); + return executeCommand("test", { + my: "options", + }).catch((error) => { + expect(error).toEqual(myerror); + expect(logger.logOnCommandRejected).toHaveBeenCalledWith({ + commandName: "test", + options: { + my: "options", + }, + error: myerror, + }); + }); + }); + it("logs onCommandResolved when handleError swallows the error", () => { + const myerror = Error("bananas"); + handleError.mockReturnValue({}); + const executeCommand = buildWithTestCommand(() => { + throw myerror; + }); + executeCommand("configure"); + return executeCommand("test", { + my: "options", + }).then((result) => { + expect(result).toEqual({}); + expect(logger.logOnCommandResolved).toHaveBeenCalledWith({ + commandName: "test", + options: { + my: "options", + }, + result: {}, + }); + expect(logger.logOnCommandRejected).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/vtest/unit/specs/core/injectHandleError.spec.js b/vtest/unit/specs/core/injectHandleError.spec.js new file mode 100644 index 000000000..2e145d525 --- /dev/null +++ b/vtest/unit/specs/core/injectHandleError.spec.js @@ -0,0 +1,49 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import injectHandleError from "../../../../src/core/injectHandleError.js"; + +const expectedMessage = "[testinstanceName] Bad thing happened."; +describe("injectHandleError", () => { + it("converts non-error to error and throws", () => { + const handleError = injectHandleError({ + errorPrefix: "[testinstanceName]", + }); + expect(() => { + handleError("Bad thing happened.", "myoperation"); + }).toThrowError(expectedMessage); + }); + it("rethrows error with instanceName prepended", () => { + const handleError = injectHandleError({ + errorPrefix: "[testinstanceName]", + }); + expect(() => { + handleError(new Error("Bad thing happened."), "myoperation"); + }).toThrowError(expectedMessage); + }); + it("logs an error and returns empty object if error is due to declined consent", () => { + const logger = { + warn: vi.fn(), + }; + const handleError = injectHandleError({ + errorPrefix: "[testinstanceName]", + logger, + }); + const error = new Error("User declined consent."); + error.code = "declinedConsent"; + expect(handleError(error, "myoperation")).toEqual({}); + expect(logger.warn).toHaveBeenCalledWith( + "The myoperation could not fully complete. User declined consent.", + ); + }); +}); diff --git a/vtest/unit/specs/core/injectShouldTransferCookie.spec.js b/vtest/unit/specs/core/injectShouldTransferCookie.spec.js new file mode 100644 index 000000000..e1d75307a --- /dev/null +++ b/vtest/unit/specs/core/injectShouldTransferCookie.spec.js @@ -0,0 +1,53 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, describe, it, expect } from "vitest"; +import injectShouldTransferCookie from "../../../../src/core/injectShouldTransferCookie.js"; + +describe("shouldTransferCookie", () => { + let targetMigrationEnabled; + let orgId; + let shouldTransferCookie; + beforeEach(() => { + targetMigrationEnabled = false; + orgId = "ABC@CustomOrg"; + shouldTransferCookie = null; + }); + const build = () => { + shouldTransferCookie = injectShouldTransferCookie({ + targetMigrationEnabled, + orgId, + }); + }; + it("returns true if it's at_qa_mode cookie", () => { + build(); + expect(shouldTransferCookie("at_qa_mode")).toBe(true); + }); + it("returns true if it's mbox cookie and targetMigrationEnabled=true", () => { + targetMigrationEnabled = true; + build(); + expect(shouldTransferCookie("mbox")).toBe(true); + }); + it("returns false if it's mbox cookie and targetMigrationEnabled=false", () => { + build(); + expect(shouldTransferCookie("mbox")).toBe(false); + }); + it("returns false if it's not a legacy cookie name", () => { + targetMigrationEnabled = true; + build(); + expect(shouldTransferCookie("foo")).toBe(false); + }); + it("returns true for kndctr cookies", () => { + build(); + expect(shouldTransferCookie("kndctr_ABC_CustomOrg_mynewcookie")).toBe(true); + }); +}); diff --git a/vtest/unit/specs/core/network/getRequestRetryDelay.spec.js b/vtest/unit/specs/core/network/getRequestRetryDelay.spec.js new file mode 100644 index 000000000..800116f45 --- /dev/null +++ b/vtest/unit/specs/core/network/getRequestRetryDelay.spec.js @@ -0,0 +1,99 @@ +/* +Copyright 2021 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, afterEach, describe, it, expect } from "vitest"; +import getRequestRetryDelay from "../../../../../src/core/network/getRequestRetryDelay.js"; + +describe("getRequestRetryDelay", () => { + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(0); + }); + afterEach(() => { + vi.useRealTimers(); + }); + it("returns value derived from retry-after header in delay-seconds format", () => { + const response = { + getHeader: vi.fn().mockReturnValue("123"), + }; + const delay = getRequestRetryDelay({ + response, + retriesAttempted: 0, + }); + expect(delay).toBe(123000); + expect(response.getHeader).toHaveBeenCalledWith("Retry-After"); + }); + it("returns value derived from retry-after header in http-date format in the future", () => { + const response = { + getHeader: vi.fn().mockReturnValue("Thu, 01 Jan 1970 00:00:09 GMT"), + }; + const delay = getRequestRetryDelay({ + response, + retriesAttempted: 0, + }); + expect(delay).toBe(9000); + expect(response.getHeader).toHaveBeenCalledWith("Retry-After"); + }); + it("returns value derived from retry-after header exists in http-date format in the past", () => { + const response = { + getHeader: vi.fn().mockReturnValue("Thu, 01 Jan 1969 11:59:51 GMT"), + }; + const delay = getRequestRetryDelay({ + response, + retriesAttempted: 0, + }); + expect(delay).toBe(0); + expect(response.getHeader).toHaveBeenCalledWith("Retry-After"); + }); + const retriesAttemptedScenarios = [ + { + retriesAttempted: 0, + lowDelay: 700, + highDelay: 1300, + }, + { + retriesAttempted: 1, + lowDelay: 1400, + highDelay: 2600, + }, + { + retriesAttempted: 2, + lowDelay: 2100, + highDelay: 3900, + }, + { + retriesAttempted: 3, + lowDelay: 2800, + highDelay: 5200, + }, + ]; + retriesAttemptedScenarios.forEach((scenario) => { + it(`returns randomized, incremental value based on ${scenario.retriesAttempted} retries attempted`, () => { + vi.spyOn(Math, "random"); + const response = { + getHeader: vi.fn(), + }; + Math.random.mockReturnValue(0); + const lowDelay = getRequestRetryDelay({ + response, + retriesAttempted: scenario.retriesAttempted, + }); + expect(lowDelay).toBe(scenario.lowDelay); + Math.random.mockReturnValue(0.999999999999); + const highDelay = getRequestRetryDelay({ + response, + retriesAttempted: scenario.retriesAttempted, + }); + expect(highDelay).toBe(scenario.highDelay); + }); + }); +}); diff --git a/vtest/unit/specs/core/network/injectSendNetworkRequest.spec.js b/vtest/unit/specs/core/network/injectSendNetworkRequest.spec.js new file mode 100644 index 000000000..ae9d84f55 --- /dev/null +++ b/vtest/unit/specs/core/network/injectSendNetworkRequest.spec.js @@ -0,0 +1,235 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, afterEach, describe, it, expect } from "vitest"; +import injectSendNetworkRequest from "../../../../../src/core/network/injectSendNetworkRequest.js"; +import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; + +describe("injectSendNetworkRequest", () => { + const url = "https://example.com"; + const payload = { + a: "b", + }; + const payloadJson = JSON.stringify(payload); + const requestId = "RID123"; + let logger; + const responseBody = { + requestId: "myrequestid", + handle: [], + }; + const responseBodyJson = JSON.stringify(responseBody); + let sendNetworkRequest; + let sendFetchRequest; + let sendBeaconRequest; + let isRequestRetryable; + let getRequestRetryDelay; + let getHeader; + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date(1998, 11, 19)); + logger = { + logOnBeforeNetworkRequest: vi.fn(), + logOnNetworkResponse: vi.fn(), + logOnNetworkError: vi.fn(), + }; + logger.enabled = true; + getHeader = vi.fn(); + sendFetchRequest = vi.fn().mockReturnValue( + Promise.resolve({ + statusCode: 200, + body: responseBodyJson, + getHeader, + }), + ); + sendBeaconRequest = vi.fn().mockReturnValue( + Promise.resolve({ + statusCode: 204, + body: "", + getHeader: () => undefined, + }), + ); + isRequestRetryable = vi.fn().mockReturnValue(false); + getRequestRetryDelay = vi.fn().mockReturnValue(1000); + sendNetworkRequest = injectSendNetworkRequest({ + logger, + sendFetchRequest, + sendBeaconRequest, + isRequestRetryable, + getRequestRetryDelay, + }); + }); + afterEach(() => { + vi.useRealTimers(); + }); + it("sends the request", () => { + return sendNetworkRequest({ + requestId, + payload, + url, + useSendBeacon: false, + }).then(() => { + expect(logger.logOnBeforeNetworkRequest).toHaveBeenCalledWith({ + requestId, + url, + payload, + }); + expect(sendFetchRequest).toHaveBeenCalledWith(url, payloadJson); + }); + }); + it("handles a response with a JSON body", () => { + return sendNetworkRequest({ + payload, + url, + requestId, + }).then((response) => { + expect(logger.logOnNetworkResponse).toHaveBeenCalledWith({ + requestId, + url, + payload, + statusCode: 200, + body: responseBodyJson, + parsedBody: responseBody, + retriesAttempted: 0, + getHeader, + }); + expect(response).toEqual({ + statusCode: 200, + body: responseBodyJson, + parsedBody: responseBody, + getHeader, + }); + }); + }); + it("handles a response with a non-JSON body", () => { + sendFetchRequest.mockReturnValue( + Promise.resolve({ + statusCode: 200, + body: "non-JSON body", + getHeader, + }), + ); + return sendNetworkRequest({ + payload, + url, + requestId, + }).then((response) => { + expect(logger.logOnNetworkResponse).toHaveBeenCalledWith({ + requestId, + url, + payload: { + a: "b", + }, + statusCode: 200, + body: "non-JSON body", + parsedBody: undefined, + retriesAttempted: 0, + getHeader, + }); + expect(response).toEqual({ + statusCode: 200, + body: "non-JSON body", + parsedBody: undefined, + getHeader, + }); + }); + }); + it("handles a response with an empty body", () => { + sendFetchRequest.mockReturnValue( + Promise.resolve({ + statusCode: 200, + body: "", + getHeader, + }), + ); + return sendNetworkRequest({ + payload, + url, + requestId, + }).then((response) => { + expect(logger.logOnNetworkResponse).toHaveBeenCalledWith({ + requestId, + url, + payload: { + a: "b", + }, + statusCode: 200, + body: "", + parsedBody: undefined, + retriesAttempted: 0, + getHeader, + }); + expect(response).toEqual({ + statusCode: 200, + body: "", + parsedBody: undefined, + getHeader, + }); + }); + }); + it("rejects the promise when a network error occurs", () => { + sendFetchRequest.mockReturnValue(Promise.reject(new Error("networkerror"))); + return sendNetworkRequest({ + payload, + url, + requestId, + }).catch((error) => { + expect(error.message).toEqual( + "Network request failed.\nCaused by: networkerror", + ); + }); + }); + it("resolves the promise for successful status and valid json", () => { + return sendNetworkRequest({ + payload, + url, + requestId, + }).then((response) => { + expect(response).toEqual({ + statusCode: 200, + body: responseBodyJson, + parsedBody: responseBody, + getHeader, + }); + }); + }); + it(`retries requests until request is no longer retryable`, () => { + isRequestRetryable + .mockReturnValueOnce(true) + .mockReturnValueOnce(true) + .mockReturnValueOnce(false); + + sendNetworkRequest({ + payload, + url, + requestId, + }); + expect(sendFetchRequest).toHaveBeenCalledTimes(1); + return flushPromiseChains() + .then(() => { + expect(sendFetchRequest).toHaveBeenCalledTimes(1); + vi.advanceTimersByTime(1000); + expect(sendFetchRequest).toHaveBeenCalledTimes(2); + return flushPromiseChains(); + }) + .then(() => { + expect(sendFetchRequest).toHaveBeenCalledTimes(2); + vi.advanceTimersByTime(1000); + expect(sendFetchRequest).toHaveBeenCalledTimes(3); + return flushPromiseChains(); + }) + .then(() => { + expect(sendFetchRequest).toHaveBeenCalledTimes(3); + vi.advanceTimersByTime(1000); + expect(sendFetchRequest).toHaveBeenCalledTimes(3); + }); + }); +}); diff --git a/vtest/unit/specs/core/network/isRequestRetryable.spec.js b/vtest/unit/specs/core/network/isRequestRetryable.spec.js new file mode 100644 index 000000000..a0699e190 --- /dev/null +++ b/vtest/unit/specs/core/network/isRequestRetryable.spec.js @@ -0,0 +1,48 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isRequestRetryable from "../../../../../src/core/network/isRequestRetryable.js"; + +describe("isRequestRetryable", () => { + [429, 503, 502, 504].forEach((statusCode) => { + it(`returns true for ${statusCode} and retries attempted is under the limit`, () => { + const isRetryable = isRequestRetryable({ + response: { + statusCode, + }, + retriesAttempted: 2, + }); + expect(isRetryable).toBe(true); + }); + it(`returns false for ${statusCode} and retries attempted is over the limit`, () => { + const isRetryable = isRequestRetryable({ + response: { + statusCode, + }, + retriesAttempted: 3, + }); + expect(isRetryable).toBe(false); + }); + }); + [100, 199, 200, 299, 300, 399, 400, 499, 500, 599].forEach((statusCode) => { + it(`returns false for ${statusCode}`, () => { + const isRetryable = isRequestRetryable({ + response: { + statusCode, + }, + retriesAttempted: 0, + }); + expect(isRetryable).toBe(false); + }); + }); +}); diff --git a/vtest/unit/specs/core/network/requestMethods/injectSendBeaconRequest.spec.js b/vtest/unit/specs/core/network/requestMethods/injectSendBeaconRequest.spec.js new file mode 100644 index 000000000..d86dc07d7 --- /dev/null +++ b/vtest/unit/specs/core/network/requestMethods/injectSendBeaconRequest.spec.js @@ -0,0 +1,83 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import injectSendBeaconRequest from "../../../../../../src/core/network/requestMethods/injectSendBeaconRequest.js"; + +// When running these tests in IE 11, they fail because IE doesn't like the +// way the blob is constructed (see +// https://github.com/bpampuch/pdfmake/pull/297/files for a workaround). +// Fortunately, if navigator.sendBeacon doesn't exist (IE 11), injectSendBeaconRequest +// should never be used (see injectNetworkStrategy.js), so we can skip +// these tests altogether. +const guardForSendBeaconAvailability = (spec) => { + return window.navigator.sendBeacon + ? spec + : () => pending("No sendBeacon API available."); +}; +describe("injectSendBeaconRequest", () => { + it( + "falls back to sendFetchRequest if sendBeacon fails", + guardForSendBeaconAvailability(() => { + const sendBeacon = vi.fn().mockReturnValue(false); + const sendFetchRequestPromise = Promise.resolve(); + const sendFetchRequest = vi.fn().mockReturnValue(sendFetchRequestPromise); + const logger = { + info: vi.fn(), + }; + const sendBeaconRequest = injectSendBeaconRequest({ + sendBeacon, + sendFetchRequest, + logger, + }); + const body = { + a: "b", + }; + const result = sendBeaconRequest("https://example.com/endpoint", body); + expect(sendBeacon).toHaveBeenCalledWith( + "https://example.com/endpoint", + expect.any(Object), + ); + expect(sendFetchRequest).toHaveBeenCalledWith( + "https://example.com/endpoint", + body, + ); + expect(logger.info).toHaveBeenCalledWith( + expect.stringMatching("falling back to"), + ); + expect(result).toBe(sendFetchRequestPromise); + }), + ); + it( + "does not fall back to sendFetchRequest if sendBeacon succeeds", + guardForSendBeaconAvailability(() => { + const sendBeacon = vi.fn().mockReturnValue(true); + const body = { + a: "b", + }; + const sendFetchRequest = vi.fn(); + const sendBeaconRequest = injectSendBeaconRequest({ + sendBeacon, + sendFetchRequest, + }); + // eslint-disable-next-line consistent-return + return sendBeaconRequest("https://example.com/endpoint", body).then( + (result) => { + expect(sendFetchRequest).not.toHaveBeenCalled(); + expect(result.statusCode).toBe(204); + expect(result.getHeader("Content-Type")).toBeNull(); + expect(result.body).toBe(""); + }, + ); + }), + ); +}); diff --git a/vtest/unit/specs/core/network/requestMethods/injectSendFetchRequest.spec.js b/vtest/unit/specs/core/network/requestMethods/injectSendFetchRequest.spec.js new file mode 100644 index 000000000..916c3720c --- /dev/null +++ b/vtest/unit/specs/core/network/requestMethods/injectSendFetchRequest.spec.js @@ -0,0 +1,53 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import injectSendFetchRequest from "../../../../../../src/core/network/requestMethods/injectSendFetchRequest.js"; + +describe("injectSendFetchRequest", () => { + it("resolves returned promise upon network success", () => { + const fetchResult = { + status: 999, + headers: { + get: vi.fn().mockReturnValue("headervalue"), + }, + text() { + return Promise.resolve("content"); + }, + }; + const fetch = vi.fn().mockReturnValue(Promise.resolve(fetchResult)); + const sendFetchRequest = injectSendFetchRequest({ + fetch, + }); + return sendFetchRequest("http://example.com/endpoint", { + a: "b", + }).then((result) => { + expect(result.statusCode).toBe(999); + expect(result.getHeader("Content-Type")).toBe("headervalue"); + expect(result.body).toBe("content"); + expect(fetchResult.headers.get).toHaveBeenCalledWith("Content-Type"); + }); + }); + it("rejects returned promise upon network failure", () => { + const fetch = vi + .fn() + .mockReturnValue(Promise.reject(new Error("No connection"))); + const sendFetchRequest = injectSendFetchRequest({ + fetch, + }); + return sendFetchRequest("http://example.com/endpoint", { + a: "b", + }).catch((error) => { + expect(error.message).toBe("No connection"); + }); + }); +}); diff --git a/vtest/unit/specs/core/requiredComponentCreators.spec.js b/vtest/unit/specs/core/requiredComponentCreators.spec.js new file mode 100644 index 000000000..b01bcf17c --- /dev/null +++ b/vtest/unit/specs/core/requiredComponentCreators.spec.js @@ -0,0 +1,32 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import * as requiredComponentCreators from "../../../../src/core/requiredComponentCreators.js"; + +describe("requiredComponentCreators", () => { + it("is an object of component creators", () => { + const c = Object.keys(requiredComponentCreators).reduce((acc, key) => { + acc.push(requiredComponentCreators[key]); + return acc; + }, []); + expect(c).toEqual(expect.any(Array)); + c.forEach((componentCreator) => { + expect(componentCreator).toEqual(expect.any(Function)); + expect(componentCreator.namespace).toEqual(expect.any(String)); + if (componentCreator.configValidators) { + // should export a validator function + expect(componentCreator.configValidators).toEqual(expect.any(Function)); + } + }); + }); +}); diff --git a/vtest/unit/specs/core/validateCommandOptions.spec.js b/vtest/unit/specs/core/validateCommandOptions.spec.js new file mode 100644 index 000000000..56b59b9d9 --- /dev/null +++ b/vtest/unit/specs/core/validateCommandOptions.spec.js @@ -0,0 +1,61 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, describe, it, expect } from "vitest"; +import validateCommandOptions from "../../../../src/core/validateCommandOptions.js"; + +describe("validateCommandOptions", () => { + let command; + let options; + beforeEach(() => { + options = {}; + command = { + commandName: "TEST", + run: () => {}, + }; + }); + it("supports commands not implementing command options validation.", () => { + expect(() => { + validateCommandOptions({ + command, + options, + }); + }).not.toThrowError(); + }); + it("should throw exception if command options validator throws exception.", () => { + command.optionsValidator = () => { + throw new Error("Invalid Options"); + }; + expect(() => { + validateCommandOptions({ + command, + options, + }); + }).toThrowError(); + }); + it("should include custom documentation URI in error message if provided by command options validator.", () => { + command.optionsValidator = () => { + throw new Error("Invalid Options"); + }; + command.documentationUri = "https://example.com"; + let errorMessage; + try { + validateCommandOptions({ + command, + options, + }); + } catch (e) { + errorMessage = e.message; + } + expect(errorMessage).toMatch(/example.com/gm); + }); +}); diff --git a/vtest/unit/specs/utils/assignConcatArrayValues.spec.js b/vtest/unit/specs/utils/assignConcatArrayValues.spec.js new file mode 100644 index 000000000..ab9c38e4c --- /dev/null +++ b/vtest/unit/specs/utils/assignConcatArrayValues.spec.js @@ -0,0 +1,129 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import assignConcatArrayValues from "../../../../src/utils/assignConcatArrayValues.js"; + +describe("assignConcatArrayValues", () => { + it("throws an error if no arguments are passed", () => { + expect(() => assignConcatArrayValues()).toThrowError(); + }); + it("returns an empty array if an empty array is passed", () => { + const obj = []; + expect(assignConcatArrayValues(obj)).toBe(obj); + }); + it("returns the first object if only one argument is passed", () => { + const obj = {}; + expect(assignConcatArrayValues(obj)).toBe(obj); + }); + it("works with two objects with different properties", () => { + const obj1 = { + a: 1, + }; + const obj2 = { + b: 2, + }; + const result = assignConcatArrayValues(obj1, obj2); + expect(result).toEqual({ + a: 1, + b: 2, + }); + expect(result).toBe(obj1); + }); + it("works with two objects with the same property", () => { + expect( + assignConcatArrayValues( + { + a: 1, + }, + { + a: 2, + }, + ), + ).toEqual({ + a: 2, + }); + }); + it("works with two objects with the same property that is an array", () => { + expect( + assignConcatArrayValues( + { + a: [1], + }, + { + a: [2], + }, + ), + ).toEqual({ + a: [1, 2], + }); + }); + it("works with three objects with the same property that is an array", () => { + expect( + assignConcatArrayValues( + { + a: [1], + }, + { + a: [], + }, + { + a: [3], + }, + ), + ).toEqual({ + a: [1, 3], + }); + }); + it("works with three objects with the same property that is an array and different properties", () => { + expect( + assignConcatArrayValues( + { + a: [1], + }, + { + a: [], + c: true, + d: false, + }, + { + a: [3], + b: "2", + e: null, + }, + ), + ).toEqual({ + a: [1, 3], + b: "2", + c: true, + d: false, + e: null, + }); + }); + it("skips non-objects", () => { + expect( + assignConcatArrayValues( + { + a: [1], + }, + null, + { + a: [3], + }, + false, + [], + 5, + ), + ).toEqual({ + a: [1, 3], + }); + }); +}); diff --git a/vtest/unit/specs/utils/clone.spec.js b/vtest/unit/specs/utils/clone.spec.js new file mode 100644 index 000000000..57f236b1a --- /dev/null +++ b/vtest/unit/specs/utils/clone.spec.js @@ -0,0 +1,30 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import clone from "../../../../src/utils/clone.js"; + +describe("clone", () => { + it("clones the object using JSON serialization/deserialization", () => { + const obj = { + toJSON() { + return { + foo: "bar", + }; + }, + }; + const result = clone(obj); + expect(result).toEqual({ + foo: "bar", + }); + }); +}); diff --git a/vtest/unit/specs/utils/crc32.spec.js b/vtest/unit/specs/utils/crc32.spec.js new file mode 100644 index 000000000..146b1b84c --- /dev/null +++ b/vtest/unit/specs/utils/crc32.spec.js @@ -0,0 +1,669 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import crc32 from "../../../../src/utils/crc32.js"; + +const crc32Sample = { + Sanskrit: { + str: "काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥", + crc32Hash: 302792584, + }, + Sanskrit_standard_transcription: { + str: "kācaṃ śaknomyattum; nopahinasti mām.", + crc32Hash: 192677925, + }, + Classical_Greek: { + str: "ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει.", + crc32Hash: 3192255259, + }, + Greek_monotonic: { + str: "Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα.", + crc32Hash: 979070829, + }, + Greek_polytonic: { + str: "Μπορῶ νὰ φάω σπασμένα γυαλιὰ χωρὶς νὰ πάθω τίποτα.", + crc32Hash: 3154458923, + }, + Latin: { + str: "Vitrum edere possum; mihi non nocet.", + crc32Hash: 2428364193, + }, + Old_French: { + str: "Je puis mangier del voirre. Ne me nuit.", + crc32Hash: 766997560, + }, + French: { + str: "Je peux manger du verre, ça ne me fait pas mal.", + crc32Hash: 2691810324, + }, + Provençal_Occitan: { + str: "Pòdi manjar de veire, me nafrariá pas.", + crc32Hash: 1951523735, + }, + Québécois: { + str: "J'peux manger d'la vitre, ça m'fa pas mal.", + crc32Hash: 2390419946, + }, + Walloon: { + str: "Dji pou magnî do vêre, çoula m' freut nén må.", + crc32Hash: 636455546, + }, + Picard: { + str: "Ch'peux mingi du verre, cha m'foé mie n'ma.", + crc32Hash: 2432888641, + }, + Kreyòl_Ayisyen_Haitï: { + str: "Mwen kap manje vè, li pa blese'm.", + crc32Hash: 4249393419, + }, + Basque: { + str: "Kristala jan dezaket, ez dit minik ematen.", + crc32Hash: 402760186, + }, + Catalan_Català: { + str: "Puc menjar vidre, que no em fa mal.", + crc32Hash: 3154625029, + }, + Spanish: { + str: "Puedo comer vidrio, no me hace daño.", + crc32Hash: 1723637111, + }, + Aragonés: { + str: "Puedo minchar beire, no me'n fa mal .", + crc32Hash: 1505826585, + }, + Galician: { + str: "Eu podo xantar cristais e non cortarme.", + crc32Hash: 1109377029, + }, + European_Portuguese: { + str: "Posso comer vidro, não me faz mal.", + crc32Hash: 101185041, + }, + Brazilian_Portuguese_8: { + str: "Posso comer vidro, não me machuca.", + crc32Hash: 883982287, + }, + Caboverdiano_Kabuverdianu_Cape_Verde: { + str: "M' podê cumê vidru, ca ta maguâ-m'.", + crc32Hash: 4279814853, + }, + Papiamentu: { + str: "Ami por kome glas anto e no ta hasimi daño.", + crc32Hash: 2084937203, + }, + Italian: { + str: "Posso mangiare il vetro e non mi fa male.", + crc32Hash: 3047854232, + }, + Milanese: { + str: "Sôn bôn de magnà el véder, el me fa minga mal.", + crc32Hash: 2168316945, + }, + Roman: { + str: "Me posso magna' er vetro, e nun me fa male.", + crc32Hash: 1559137374, + }, + Napoletano: { + str: "M' pozz magna' o'vetr, e nun m' fa mal.", + crc32Hash: 502573463, + }, + Venetian: { + str: "Mi posso magnare el vetro, no'l me fa mae.", + crc32Hash: 616338953, + }, + Zeneise_Genovese: { + str: "Pòsso mangiâ o veddro e o no me fà mâ.", + crc32Hash: 28085942, + }, + Sicilian: { + str: "Puotsu mangiari u vitru, nun mi fa mali.", + crc32Hash: 1960166993, + }, + Romansch_Grischun: { + str: "Jau sai mangiar vaider, senza che quai fa donn a mai.", + crc32Hash: 515085501, + }, + Romanian: { + str: "Pot să mănânc sticlă și ea nu mă rănește.", + crc32Hash: 1879348622, + }, + Esperanto: { + str: "Mi povas manĝi vitron, ĝi ne damaĝas min.", + crc32Hash: 2514278986, + }, + Cornish: { + str: "Mý a yl dybry gwéder hag éf ny wra ow ankenya.", + crc32Hash: 14991194, + }, + Welsh: { + str: "Dw i'n gallu bwyta gwydr, 'dyw e ddim yn gwneud dolur i mi.", + crc32Hash: 1548251343, + }, + Manx_Gaelic: { + str: "Foddym gee glonney agh cha jean eh gortaghey mee.", + crc32Hash: 1378983096, + }, + Old_Irish_Ogham: { + str: "᚛᚛ᚉᚑᚅᚔᚉᚉᚔᚋ ᚔᚈᚔ ᚍᚂᚐᚅᚑ ᚅᚔᚋᚌᚓᚅᚐ᚜", + crc32Hash: 2248868407, + }, + Old_Irish_Latin: { + str: "Con·iccim ithi nglano. Ním·géna.", + crc32Hash: 864598796, + }, + Irish: { + str: "Is féidir liom gloinne a ithe. Ní dhéanann sí dochar ar bith dom.", + crc32Hash: 709577166, + }, + Ulster_Gaelic: { + str: "Ithim-sa gloine agus ní miste damh é.", + crc32Hash: 1206756846, + }, + Scottish_Gaelic: { + str: "S urrainn dhomh gloinne ithe; cha ghoirtich i mi.", + crc32Hash: 3064191386, + }, + "Anglo-Saxon_Runes": { + str: "ᛁᚳ᛫ᛗᚨᚷ᛫ᚷᛚᚨᛋ᛫ᛖᚩᛏᚪᚾ᛫ᚩᚾᛞ᛫ᚻᛁᛏ᛫ᚾᛖ᛫ᚻᛖᚪᚱᛗᛁᚪᚧ᛫ᛗᛖ᛬", + crc32Hash: 2170250096, + }, + "Anglo-Saxon_Latin": { + str: "Ic mæg glæs eotan ond hit ne hearmiað me.", + crc32Hash: 2974948093, + }, + Middle_English: { + str: "Ich canne glas eten and hit hirtiþ me nouȝt.", + crc32Hash: 2519549262, + }, + English: { + str: "I can eat glass and it doesn't hurt me.", + crc32Hash: 3522513655, + }, + English_IPA: { + str: "[aɪ kæn iːt glɑːs ænd ɪt dɐz nɒt hɜːt miː] (Received Pronunciation)", + crc32Hash: 2272956447, + }, + English_Braille: { + str: "⠊⠀⠉⠁⠝⠀⠑⠁⠞⠀⠛⠇⠁⠎⠎⠀⠁⠝⠙⠀⠊⠞⠀⠙⠕⠑⠎⠝⠞⠀⠓⠥⠗⠞⠀⠍⠑", + crc32Hash: 3787140422, + }, + Jamaican: { + str: "Mi kian niam glas han i neba hot mi.", + crc32Hash: 443133101, + }, + Lalland_Scots_Doric: { + str: "Ah can eat gless, it disnae hurt us.", + crc32Hash: 747768810, + }, + Gothic_4: { + str: "ЌЌЌ ЌЌЌЍ Ќ̈ЍЌЌ, ЌЌ ЌЌЍ ЍЌ ЌЌЌЌ ЌЍЌЌЌЌЌ.", + crc32Hash: 3686804294, + }, + Old_Norse_Runes: { + str: "ᛖᚴ ᚷᛖᛏ ᛖᛏᛁ ᚧ ᚷᛚᛖᚱ ᛘᚾ ᚦᛖᛋᛋ ᚨᚧ ᚡᛖ ᚱᚧᚨ ᛋᚨᚱ", + crc32Hash: 2108429530, + }, + Old_Norse_Latin: { + str: "Ek get etið gler án þess að verða sár.", + crc32Hash: 296353268, + }, + Norsk_Norwegian_Nynorsk: { + str: "Eg kan eta glas utan å skada meg.", + crc32Hash: 1577959981, + }, + Norsk_Norwegian_Bokmål: { + str: "Jeg kan spise glass uten å skade meg.", + crc32Hash: 2766158238, + }, + Føroyskt_Faroese: { + str: "Eg kann eta glas, skaðaleysur.", + crc32Hash: 1761767164, + }, + Íslenska_Icelandic: { + str: "Ég get etið gler án þess að meiða mig.", + crc32Hash: 964197683, + }, + Svenska_Swedish: { + str: "Jag kan äta glas utan att skada mig.", + crc32Hash: 2676580541, + }, + Dansk_Danish: { + str: "Jeg kan spise glas, det gør ikke ondt på mig.", + crc32Hash: 1496560676, + }, + Sønderjysk: { + str: "Æ ka æe glass uhen at det go mæ naue.", + crc32Hash: 1426070144, + }, + Frysk_Frisian: { + str: "Ik kin glês ite, it docht me net sear.", + crc32Hash: 1849595261, + }, + Nederlands_Dutch: { + str: "Ik kan glas eten, het doet mij geen kwaad.", + crc32Hash: 4016747965, + }, + Kirchröadsj_Bôchesserplat: { + str: "Iech ken glaas èèse, mer 't deet miech jing pieng.", + crc32Hash: 611147726, + }, + Afrikaans: { + str: "Ek kan glas eet, maar dit doen my nie skade nie.", + crc32Hash: 2665435467, + }, + Lëtzebuergescht_Luxemburgish: { + str: "Ech kan Glas iessen, daat deet mir nët wei.", + crc32Hash: 2414621007, + }, + Deutsch_German: { + str: "Ich kann Glas essen, ohne mir zu schaden.", + crc32Hash: 326341728, + }, + Ruhrdeutsch: { + str: "Ich kann Glas verkasematuckeln, ohne dattet mich wat jucken tut.", + crc32Hash: 390997622, + }, + Langenfelder_Platt: { + str: "Isch kann Jlaas kimmeln, uuhne datt mich datt weh dääd.", + crc32Hash: 172048332, + }, + 'Lausitzer_Mundart_"Lusatian"': { + str: "Ich koann Gloos assn und doas dudd merr ni wii.", + crc32Hash: 3814582543, + }, + Odenwälderisch: { + str: "Iech konn glaasch voschbachteln ohne dass es mir ebbs daun doun dud.", + crc32Hash: 3690429996, + }, + Sächsisch_Saxon: { + str: "'sch kann Glos essn, ohne dass'sch mer wehtue.", + crc32Hash: 1553361449, + }, + Pfälzisch: { + str: "Isch konn Glass fresse ohne dasses mer ebbes ausmache dud.", + crc32Hash: 2890933716, + }, + Schwäbisch_Swabian: { + str: "I kå Glas frässa, ond des macht mr nix!", + crc32Hash: 2705198893, + }, + Deutsch_Voralberg: { + str: "I ka glas eassa, ohne dass mar weh tuat.", + crc32Hash: 1802390038, + }, + Bayrisch_Bavarian: { + str: "I koh Glos esa, und es duard ma ned wei.", + crc32Hash: 1620844699, + }, + Allemannisch: { + str: "I kaun Gloos essen, es tuat ma ned weh.", + crc32Hash: 532444931, + }, + Schwyzerdütsch_Zürich: { + str: "Ich chan Glaas ässe, das schadt mir nöd.", + crc32Hash: 647555846, + }, + Schwyzerdütsch_Luzern: { + str: "Ech cha Glâs ässe, das schadt mer ned.", + crc32Hash: 2888870414, + }, + Hungarian: { + str: "Meg tudom enni az üveget, nem lesz tőle bajom.", + crc32Hash: 798141029, + }, + Suomi_Finnish: { + str: "Voin syödä lasia, se ei vahingoita minua.", + crc32Hash: 854800956, + }, + Sami_Northern: { + str: "Sáhtán borrat lása, dat ii leat bávččas.", + crc32Hash: 2134369501, + }, + Erzian: { + str: "Мон ярсан суликадо, ды зыян эйстэнзэ а ули.", + crc32Hash: 1195119622, + }, + Northern_Karelian: { + str: "Mie voin syvvä lasie ta minla ei ole kipie.", + crc32Hash: 825590056, + }, + Southern_Karelian: { + str: "Minä voin syvvä st'oklua dai minule ei ole kibie.", + crc32Hash: 1454844737, + }, + Estonian: { + str: "Ma võin klaasi süüa, see ei tee mulle midagi.", + crc32Hash: 1872139873, + }, + Latvian: { + str: "Es varu ēst stiklu, tas man nekaitē.", + crc32Hash: 1984101272, + }, + Lithuanian: { + str: "Aš galiu valgyti stiklą ir jis manęs nežeidžia", + crc32Hash: 462644696, + }, + Czech: { + str: "Mohu jíst sklo, neublíží mi.", + crc32Hash: 1941463124, + }, + Slovak: { + str: "Môžem jesť sklo. Nezraní ma.", + crc32Hash: 1401582521, + }, + Polska_Polish: { + str: "Mogę jeść szkło i mi nie szkodzi.", + crc32Hash: 4062381321, + }, + Slovenian: { + str: "Lahko jem steklo, ne da bi mi škodovalo.", + crc32Hash: 3517115573, + }, + "Bosnian,_Croatian,_Montenegrin_and_Serbian_Latin": { + str: "Ja mogu jesti staklo, i to mi ne šteti.", + crc32Hash: 526455862, + }, + "Bosnian,_Montenegrin_and_Serbian_Cyrillic": { + str: "Ја могу јести стакло, и то ми не штети.", + crc32Hash: 1697709634, + }, + Macedonian: { + str: "Можам да јадам стакло, а не ме штета.", + crc32Hash: 1164576497, + }, + Russian: { + str: "Я могу есть стекло, оно мне не вредит.", + crc32Hash: 3819750153, + }, + Belarusian_Cyrillic: { + str: "Я магу есці шкло, яно мне не шкодзіць.", + crc32Hash: 4029978467, + }, + Belarusian_Lacinka: { + str: "Ja mahu jeści škło, jano mne ne škodzić.", + crc32Hash: 1627307525, + }, + Ukrainian: { + str: "Я можу їсти скло, і воно мені не зашкодить.", + crc32Hash: 752793683, + }, + Bulgarian: { + str: "Мога да ям стъкло, то не ми вреди.", + crc32Hash: 3841600447, + }, + Georgian: { + str: "მინას ვჭამ და არა მტკივა.", + crc32Hash: 3209020393, + }, + Armenian: { + str: "Կրնամ ապակի ուտել և ինծի անհանգիստ չըներ։", + crc32Hash: 344432805, + }, + Albanian: { + str: "Unë mund të ha qelq dhe nuk më gjen gjë.", + crc32Hash: 541809055, + }, + Turkish: { + str: "Cam yiyebilirim, bana zararı dokunmaz.", + crc32Hash: 3446658794, + }, + Turkish_Ottoman: { + str: "جام ييه بلورم بڭا ضررى طوقونمز", + crc32Hash: 7569123, + }, + Bangla_Bengali: { + str: "আমি কাঁচ খেতে পারি, তাতে আমার কোনো ক্ষতি হয় না।", + crc32Hash: 3121877246, + }, + Marathi: { + str: "मी काच खाऊ शकतो, मला ते दुखत नाही.", + crc32Hash: 2262144017, + }, + Kannada: { + str: "ನನಗೆ ಹಾನಿ ಆಗದೆ, ನಾನು ಗಜನ್ನು ತಿನಬಹುದು", + crc32Hash: 3783527408, + }, + Hindi: { + str: "मैं काँच खा सकता हूँ और मुझे उससे कोई चोट नहीं पहुंचती.", + crc32Hash: 911621671, + }, + Tamil: { + str: "நான் கண்ணாடி சாப்பிடுவேன், அதனால் எனக்கு ஒரு கேடும் வராது.", + crc32Hash: 2492793518, + }, + Telugu: { + str: "నేను గాజు తినగలను మరియు అలా చేసినా నాకు ఏమి ఇబ్బంది లేదు", + crc32Hash: 1475959036, + }, + Sinhalese: { + str: "මට වීදුරු කෑමට හැකියි. එයින් මට කිසි හානියක් සිදු නොවේ.", + crc32Hash: 2353063362, + }, + Urdu3: { + str: "میں کانچ کھا سکتا ہوں اور مجھے تکلیف نہیں ہوتی ۔", + crc32Hash: 2416692885, + }, + Pashto3: { + str: "زه شيشه خوړلې شم، هغه ما نه خوږوي", + crc32Hash: 2167742720, + }, + Farsi_Persian3: { + str: ".من می توانم بدونِ احساس درد شيشه بخورم", + crc32Hash: 367406354, + }, + Arabic3: { + str: "أنا قادر على أكل الزجاج و هذا لا يؤلمني.", + crc32Hash: 2614809933, + }, + Maltese: { + str: "Nista' niekol il-ħġieġ u ma jagħmilli xejn.", + crc32Hash: 1075553078, + }, + Hebrew3: { + str: "אני יכול לאכול זכוכית וזה לא מזיק לי.", + crc32Hash: 611298455, + }, + Yiddish3: { + str: "איך קען עסן גלאָז און עס טוט מיר נישט װײ.", + crc32Hash: 1278369850, + }, + Twi: { + str: "Metumi awe tumpan, ɜnyɜ me hwee.", + crc32Hash: 1882519302, + }, + Hausa_Latin: { + str: "Inā iya taunar gilāshi kuma in gamā lāfiyā.", + crc32Hash: 3951714594, + }, + Hausa_Ajami_2: { + str: "إِنا إِىَ تَونَر غِلَاشِ كُمَ إِن غَمَا لَافِىَا", + crc32Hash: 2165839937, + }, + Yoruba4: { + str: "Mo lè je̩ dígí, kò ní pa mí lára.", + crc32Hash: 462513830, + }, + Lingala: { + str: "Nakokí kolíya biténi bya milungi, ekosála ngáí mabé tɛ́.", + crc32Hash: 3326201453, + }, + KiSwahili: { + str: "Naweza kula bilauri na sikunyui.", + crc32Hash: 1166963539, + }, + Malay: { + str: "Saya boleh makan kaca dan ia tidak mencederakan saya.", + crc32Hash: 1655550062, + }, + Tagalog: { + str: "Kaya kong kumain nang bubog at hindi ako masaktan.", + crc32Hash: 2604200406, + }, + Chamorro: { + str: "Siña yo' chumocho krestat, ti ha na'lalamen yo'.", + crc32Hash: 2067473716, + }, + Fijian: { + str: "Au rawa ni kana iloilo, ia au sega ni vakacacani kina.", + crc32Hash: 4067250732, + }, + Javanese: { + str: "Aku isa mangan beling tanpa lara.", + crc32Hash: 2987633961, + }, + Burmese: { + str: "က္ယ္ဝန္‌တော္‌၊က္ယ္ဝန္‌မ မ္ယက္‌စားနုိင္‌သည္‌။ ၎က္ရောင္‌့ ထိခုိက္‌မ္ဟု မရ္ဟိပာ။ (9)", + crc32Hash: 699725753, + }, + Vietnamese_quốc_ngữ: { + str: "Tôi có thể ăn thủy tinh mà không hại gì.", + crc32Hash: 2565707606, + }, + Vietnamese_nôm_4: { + str: "些 ࣎ 世 咹 水 晶 ও 空 ࣎ 害 咦", + crc32Hash: 1216417303, + }, + Khmer: { + str: "ខ្ញុំអាចញុំកញ្ចក់បាន ដោយគ្មានបញ្ហារ", + crc32Hash: 2350771137, + }, + Lao: { + str: "ຂອ້ຍກິນແກ້ວໄດ້ໂດຍທີ່ມັນບໍ່ໄດ້ເຮັດໃຫ້ຂອ້ຍເຈັບ.", + crc32Hash: 1733276636, + }, + Thai: { + str: "ฉันกินกระจกได้ แต่มันไม่ทำให้ฉันเจ็บ", + crc32Hash: 227782237, + }, + Mongolian_Cyrillic: { + str: "Би шил идэй чадна, надад хортой биш", + crc32Hash: 2254455591, + }, + Mongolian_Classic_5: { + str: "ᠪᠢ ᠰᠢᠯᠢ ᠢᠳᠡᠶᠦ ᠴᠢᠳᠠᠨᠠ ᠂ ᠨᠠᠳᠤᠷ ᠬᠣᠤᠷᠠᠳᠠᠢ ᠪᠢᠰᠢ", + crc32Hash: 412025584, + }, + Nepali: { + str: "म काँच खान सक्छू र मलाई केहि नी हुन्‍न् ।", + crc32Hash: 92247149, + }, + Tibetan: { + str: "ཤེལ་སྒོ་ཟ་ནས་ང་ན་གི་མ་རེད།", + crc32Hash: 2943025548, + }, + Chinese: { + str: "我能吞下玻璃而不伤身体。", + crc32Hash: 3340264501, + }, + Chinese_Traditional: { + str: "我能吞下玻璃而不傷身體。", + crc32Hash: 617692142, + }, + Taiwanese6: { + str: "Góa ē-tàng chia̍h po-lê, mā bē tio̍h-siong.", + crc32Hash: 3845385888, + }, + Japanese: { + str: "私はガラスを食べられます。それは私を傷つけません。", + crc32Hash: 1060378668, + }, + Korean: { + str: "나는 유리를 먹을 수 있어요. 그래도 아프지 않아요", + crc32Hash: 3701809983, + }, + Bislama: { + str: "Mi save kakae glas, hemi no save katem mi.", + crc32Hash: 1105523983, + }, + Hawaiian: { + str: "Hiki iaʻu ke ʻai i ke aniani; ʻaʻole nō lā au e ʻeha.", + crc32Hash: 3890531562, + }, + Marquesan: { + str: "E koʻana e kai i te karahi, mea ʻā, ʻaʻe hauhau.", + crc32Hash: 1236763367, + }, + Inuktitut_10: { + str: "ᐊᓕᒍᖅ ᓂᕆᔭᕌᖓᒃᑯ ᓱᕋᙱᑦᑐᓐᓇᖅᑐᖓ", + crc32Hash: 557265846, + }, + Chinook_Jargon: { + str: "Naika məkmək kakshət labutay, pi weyk ukuk munk-sik nay.", + crc32Hash: 4059264750, + }, + Navajo: { + str: "Tsésǫʼ yishą́ągo bííníshghah dóó doo shił neezgai da.", + crc32Hash: 3770336662, + }, + Lojban: { + str: "mi kakne le nu citka le blaci .iku'i le se go'i na xrani mi", + crc32Hash: 3008094941, + }, + Nórdicg: { + str: "Ljœr ye caudran créneþ ý jor cẃran.", + crc32Hash: 4211638728, + }, +}; +describe("crc32", () => { + it("should hash a string and return a number ", () => { + const str = "hello"; + const result = crc32(str); + expect(typeof result).toBe("number"); + expect(result).toEqual(907060870); + }); + it("should create same hash every time", () => { + const idsTohash = { + email: { + id: "me@me.com", + authState: 0, + }, + }; + const resultOne = crc32(JSON.stringify(idsTohash)); + const resultTwo = crc32(JSON.stringify(idsTohash)); + expect(typeof resultOne).toBe("number"); + expect(resultOne).toBe(3158443042); + expect(resultTwo).toBe(3158443042); + }); + it("should always return a positive number", () => { + const idOneTohash = "x+x"; + const idTwoTohash = "a*b/100-220"; + const resultOne = crc32(JSON.stringify(idOneTohash)); + const resultTwo = crc32(JSON.stringify(idTwoTohash)); + expect(typeof resultOne).toBe("number"); + expect(resultOne).toBeGreaterThan(0); + expect(typeof resultTwo).toBe("number"); + expect(resultTwo).toBeGreaterThan(0); + }); + it("should hash strings with special characters", () => { + const stringToHash = "hello@#&^hq10"; + const result = crc32(stringToHash); + expect(result).toBe(864118309); + }); + it("should create different hashes for different strings", () => { + const stringOneToHash = "hello@#&^hq10"; + const stringTwoToHash = "hello@#&h^q10"; + const resultOne = crc32(stringOneToHash); + const resultTwo = crc32(stringTwoToHash); + expect(resultOne).not.toEqual(resultTwo); + expect(resultOne).toBe(864118309); + expect(resultTwo).toBe(3365964926); + }); + describe("hashing of various of unicode chars", () => { + Object.keys(crc32Sample).forEach((lang) => { + const sample = crc32Sample[lang]; + it(`should create right hash of a ${lang} string`, () => { + expect(crc32(sample.str)).toBe(sample.crc32Hash); + }); + }); + }); +}); diff --git a/vtest/unit/specs/utils/createCallbackAggregator.spec.js b/vtest/unit/specs/utils/createCallbackAggregator.spec.js new file mode 100644 index 000000000..705654316 --- /dev/null +++ b/vtest/unit/specs/utils/createCallbackAggregator.spec.js @@ -0,0 +1,44 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createCallbackAggregator from "../../../../src/utils/createCallbackAggregator.js"; + +describe("createCallbackAggregator", () => { + let callbackAggregator; + beforeEach(() => { + callbackAggregator = createCallbackAggregator(); + }); + it("calls all added callbacks and returns a combined promise", () => { + const callback1 = vi.fn().mockReturnValue("foo"); + const callback2 = vi.fn().mockReturnValue("bar"); + callbackAggregator.add(callback1); + callbackAggregator.add(callback2); + return callbackAggregator.call("cherry", "tree").then((result) => { + expect(callback1).toHaveBeenCalledWith("cherry", "tree"); + expect(callback2).toHaveBeenCalledWith("cherry", "tree"); + expect(result).toEqual(["foo", "bar"]); + }); + }); + it("doesn't throw errors when there are no callbacks", () => { + return callbackAggregator.call("cherry", "tree").then((result) => { + expect(result).toEqual([]); + }); + }); + it("doesn't throw errors when there are no arguments", () => { + const callback = vi.fn().mockReturnValue("foo"); + callbackAggregator.add(callback); + return callbackAggregator.call().then((result) => { + expect(result).toEqual(["foo"]); + }); + }); +}); diff --git a/vtest/unit/specs/utils/createCollect.spec.js b/vtest/unit/specs/utils/createCollect.spec.js new file mode 100644 index 000000000..a9b379365 --- /dev/null +++ b/vtest/unit/specs/utils/createCollect.spec.js @@ -0,0 +1,55 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createCollect from "../../../../src/utils/createCollect.js"; +import { PropositionEventType } from "../../../../src/constants/propositionEventType.js"; + +describe("Utils::createCollect", () => { + let eventManager; + let mergeDecisionsMeta; + const decisionsMeta = [ + { + id: 1, + decisionId: "foo", + }, + ]; + const event = { + mergeXdm: vi.fn(), + }; + beforeEach(() => { + eventManager = { + sendEvent: vi.fn().mockReturnValue(undefined), + createEvent: vi.fn().mockReturnValue(event), + }; + mergeDecisionsMeta = vi.fn(); + }); + it("collects and sends event with metadata", () => { + const collect = createCollect({ + eventManager, + mergeDecisionsMeta, + }); + collect({ + decisionsMeta, + }); + expect(eventManager.createEvent).toHaveBeenCalled(); + expect(event.mergeXdm).toHaveBeenCalledWith({ + eventType: "decisioning.propositionDisplay", + }); + expect(mergeDecisionsMeta).toHaveBeenCalledWith( + event, + decisionsMeta, + [PropositionEventType.DISPLAY], + undefined, + ); + expect(eventManager.sendEvent).toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/utils/createLoggingCookieJar.spec.js b/vtest/unit/specs/utils/createLoggingCookieJar.spec.js new file mode 100644 index 000000000..64e44476c --- /dev/null +++ b/vtest/unit/specs/utils/createLoggingCookieJar.spec.js @@ -0,0 +1,59 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createLoggingCookieJar from "../../../../src/utils/createLoggingCookieJar.js"; + +describe("loggingCookieJar", () => { + let cookieJar; + let logger; + let loggingCookieJar; + beforeEach(() => { + cookieJar = { + set: vi.fn(), + get: vi.fn(), + }; + logger = { + info: vi.fn(), + }; + loggingCookieJar = createLoggingCookieJar({ + cookieJar, + logger, + }); + }); + it("logs a message", () => { + loggingCookieJar.set("mykey", "myvalue", { + myoption: "myoptionvalue", + }); + expect(logger.info).toHaveBeenNthCalledWith(1, "Setting cookie", { + name: "mykey", + value: "myvalue", + myoption: "myoptionvalue", + }); + }); + it("calls set", () => { + loggingCookieJar.set("mykey", "myvalue", { + myoption: "myoptionvalue", + }); + expect(cookieJar.set).toHaveBeenNthCalledWith(1, "mykey", "myvalue", { + myoption: "myoptionvalue", + }); + }); + it("calls get", () => { + loggingCookieJar.get("mykey"); + expect(cookieJar.get).toHaveBeenNthCalledWith(1, "mykey"); + }); + it("returns the value from get", () => { + cookieJar.get.mockReturnValue("myvalue"); + expect(loggingCookieJar.get("mykey")).toEqual("myvalue"); + }); +}); diff --git a/vtest/unit/specs/utils/createMerger.spec.js b/vtest/unit/specs/utils/createMerger.spec.js new file mode 100644 index 000000000..092efcfa6 --- /dev/null +++ b/vtest/unit/specs/utils/createMerger.spec.js @@ -0,0 +1,73 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import createMerger from "../../../../src/utils/createMerger.js"; + +describe("createMerger", () => { + it("populates key if key doesn't exist", () => { + const content = {}; + createMerger( + content, + "fruit", + )({ + type: "apple", + }); + expect(content).toEqual({ + fruit: { + type: "apple", + }, + }); + }); + it("deeply merges if key does exist", () => { + const content = { + foods: { + fruits: { + apple: { + calories: 95, + }, + banana: { + calories: 105, + }, + }, + }, + }; + createMerger( + content, + "foods", + )({ + fruits: { + banana: { + calories: 110, + }, + cherry: { + calories: 77, + }, + }, + }); + expect(content).toEqual({ + foods: { + fruits: { + apple: { + calories: 95, + }, + banana: { + calories: 110, + }, + cherry: { + calories: 77, + }, + }, + }, + }); + }); +}); diff --git a/vtest/unit/specs/utils/createSubscription.spec.js b/vtest/unit/specs/utils/createSubscription.spec.js new file mode 100644 index 000000000..c53621281 --- /dev/null +++ b/vtest/unit/specs/utils/createSubscription.spec.js @@ -0,0 +1,142 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import createSubscription from "../../../../src/utils/createSubscription.js"; + +describe("createSubscription", () => { + const value = { + something: 42, + }; + let callback1; + let callback2; + let callback3; + beforeEach(() => { + callback1 = vi.fn(); + callback2 = vi.fn(); + callback3 = vi.fn(); + }); + it("supports a single subscription", () => { + const subscription = createSubscription(); + expect(subscription.hasSubscriptions()).toBe(false); + const { unsubscribe } = subscription.add(callback1); + expect(subscription.hasSubscriptions()).toBe(true); + subscription.emit(value); + expect(callback1).toHaveBeenNthCalledWith(1, value); + unsubscribe(); + expect(subscription.hasSubscriptions()).toBe(false); + subscription.emit(value); + expect(callback1).toHaveBeenNthCalledWith(1, value); + }); + it("supports multiple subscriptions", () => { + const subscription = createSubscription(); + expect(subscription.hasSubscriptions()).toBe(false); + const { unsubscribe: unsubscribe1 } = subscription.add(callback1); + const { unsubscribe: unsubscribe2 } = subscription.add(callback2); + const { unsubscribe: unsubscribe3 } = subscription.add(callback3); + expect(subscription.hasSubscriptions()).toBe(true); + subscription.emit(value); + expect(callback1).toHaveBeenNthCalledWith(1, value); + expect(callback2).toHaveBeenNthCalledWith(1, value); + expect(callback3).toHaveBeenNthCalledWith(1, value); + + // unsubscribe the first callback + unsubscribe1(); + expect(subscription.hasSubscriptions()).toBe(true); + subscription.emit(value); + expect(callback1).toHaveBeenCalledTimes(1); + expect(callback2).toHaveBeenCalledTimes(2); + expect(callback3).toHaveBeenCalledTimes(2); + + // unsubscribe the second callback + unsubscribe2(); + expect(subscription.hasSubscriptions()).toBe(true); + subscription.emit(value); + expect(callback1).toHaveBeenCalledTimes(1); + expect(callback2).toHaveBeenCalledTimes(2); + expect(callback3).toHaveBeenCalledTimes(3); + + // unsubscribe the third callback + unsubscribe3(); + expect(subscription.hasSubscriptions()).toBe(false); + subscription.emit(value); + expect(callback1).toHaveBeenCalledTimes(1); + expect(callback2).toHaveBeenCalledTimes(2); + expect(callback3).toHaveBeenCalledTimes(3); + }); + it("emits distinct values for multiple subscriptions", () => { + const subscription = createSubscription(); + subscription.setEmissionPreprocessor((params, basePrice) => { + const { name, profitMargin } = params; + const price = basePrice * profitMargin; + return [`hello ${name}! The price is $${price}`]; + }); + const { unsubscribe: unsubscribe1 } = subscription.add(callback1, { + name: "jim", + profitMargin: 3, + }); + const { unsubscribe: unsubscribe2 } = subscription.add(callback2, { + name: "bob", + profitMargin: 1.8, + }); + const { unsubscribe: unsubscribe3 } = subscription.add(callback3, { + name: "tina", + profitMargin: 1.1, + }); + subscription.emit(10); + expect(callback1).toHaveBeenNthCalledWith(1, "hello jim! The price is $30"); + expect(callback2).toHaveBeenNthCalledWith(1, "hello bob! The price is $18"); + expect(callback3).toHaveBeenNthCalledWith( + 1, + "hello tina! The price is $11", + ); + unsubscribe1(); + unsubscribe2(); + unsubscribe3(); + }); + it("emits distinct values conditionally", () => { + const subscription = createSubscription(); + subscription.setEmissionPreprocessor((params, basePrice) => { + const { name, profitMargin } = params; + const price = basePrice * profitMargin; + return [`hello ${name}! The price is $${price}`]; + }); + subscription.setEmissionCondition((params, result) => { + const price = parseInt( + result.substring(result.length - 2, result.length), + 10, + ); + return price < 20; + }); + const { unsubscribe: unsubscribe1 } = subscription.add(callback1, { + name: "jim", + profitMargin: 3, + }); + const { unsubscribe: unsubscribe2 } = subscription.add(callback2, { + name: "bob", + profitMargin: 1.8, + }); + const { unsubscribe: unsubscribe3 } = subscription.add(callback3, { + name: "tina", + profitMargin: 1.1, + }); + subscription.emit(10); + expect(callback1).not.toHaveBeenCalled(); // price is > 20, so no emission + expect(callback2).toHaveBeenNthCalledWith(1, "hello bob! The price is $18"); + expect(callback3).toHaveBeenNthCalledWith( + 1, + "hello tina! The price is $11", + ); + unsubscribe1(); + unsubscribe2(); + unsubscribe3(); + }); +}); diff --git a/vtest/unit/specs/utils/createTaskQueue.spec.js b/vtest/unit/specs/utils/createTaskQueue.spec.js new file mode 100644 index 000000000..e01244791 --- /dev/null +++ b/vtest/unit/specs/utils/createTaskQueue.spec.js @@ -0,0 +1,131 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import createTaskQueue from "../../../../src/utils/createTaskQueue.js"; +import { defer } from "../../../../src/utils/index.js"; +import flushPromiseChains from "../../helpers/flushPromiseChains.js"; + +describe("createTaskQueue", () => { + it("executes a single task once even when it throws an error", () => { + const queue = createTaskQueue(); + const task1 = vi.fn().mockReturnValue(Promise.reject(Error("myerror"))); + return queue.addTask(task1).catch((e) => { + expect(e.message).toEqual("myerror"); + expect(task1).toHaveBeenCalledTimes(1); + }); + }); + it("executes tasks in sequence when first task succeeds", () => { + const queue = createTaskQueue(); + const task1Deferred = defer(); + const task1 = vi.fn().mockReturnValue(task1Deferred.promise); + const task2Deferred = defer(); + const task2 = vi.fn().mockReturnValue(task2Deferred.promise); + const task1OnFulfilled = vi.fn(); + queue.addTask(task1).then(task1OnFulfilled); + const task2OnFulfilled = vi.fn(); + queue.addTask(task2).then(task2OnFulfilled); + return flushPromiseChains() + .then(() => { + expect(task1).toHaveBeenCalled(); + expect(task2).not.toHaveBeenCalled(); + expect(task1OnFulfilled).not.toHaveBeenCalled(); + expect(task2OnFulfilled).not.toHaveBeenCalled(); + task1Deferred.resolve("task1Result"); + return flushPromiseChains(); + }) + .then(() => { + expect(task2).toHaveBeenCalled(); + expect(task1OnFulfilled).toHaveBeenCalledWith("task1Result"); + expect(task2OnFulfilled).not.toHaveBeenCalled(); + task2Deferred.resolve("task2Result"); + return flushPromiseChains(); + }) + .then(() => { + expect(task2OnFulfilled).toHaveBeenCalledWith("task2Result"); + }); + }); + it("executes tasks in sequence when first task rejects promise", () => { + const queue = createTaskQueue(); + const task1Deferred = defer(); + const task1 = vi.fn().mockReturnValue(task1Deferred.promise); + const task2Deferred = defer(); + const task2 = vi.fn().mockReturnValue(task2Deferred.promise); + const task1OnRejected = vi.fn(); + queue.addTask(task1).catch(task1OnRejected); + const task2OnFulfilled = vi.fn(); + queue.addTask(task2).then(task2OnFulfilled); + return flushPromiseChains() + .then(() => { + expect(task1).toHaveBeenCalled(); + expect(task2).not.toHaveBeenCalled(); + expect(task1OnRejected).not.toHaveBeenCalled(); + expect(task2OnFulfilled).not.toHaveBeenCalled(); + task1Deferred.reject(new Error("task1Error")); + return flushPromiseChains(); + }) + .then(() => { + expect(task2).toHaveBeenCalled(); + expect(task1OnRejected).toHaveBeenCalledWith(new Error("task1Error")); + expect(task2OnFulfilled).not.toHaveBeenCalled(); + task2Deferred.resolve("task2Result"); + return flushPromiseChains(); + }) + .then(() => { + expect(task2OnFulfilled).toHaveBeenCalledWith("task2Result"); + }); + }); + it("executes tasks in sequence when first task throws error", () => { + const queue = createTaskQueue(); + const task1 = vi.fn().mockImplementation(() => { + throw new Error("task1Error"); + }); + const task2Deferred = defer(); + const task2 = vi.fn().mockReturnValue(task2Deferred.promise); + const task1OnRejected = vi.fn(); + queue.addTask(task1).catch(task1OnRejected); + const task2OnFulfilled = vi.fn(); + queue.addTask(task2).then(task2OnFulfilled); + return flushPromiseChains() + .then(() => { + expect(task1).toHaveBeenCalled(); + expect(task2).toHaveBeenCalled(); + expect(task1OnRejected).toHaveBeenCalledWith(new Error("task1Error")); + expect(task2OnFulfilled).not.toHaveBeenCalled(); + task2Deferred.resolve("task2Result"); + return flushPromiseChains(); + }) + .then(() => { + expect(task2OnFulfilled).toHaveBeenCalledWith("task2Result"); + }); + }); + it("accurately reports the size of the queue", () => { + const queue = createTaskQueue(); + const task1Deferred = defer(); + const task2Deferred = defer(); + expect(queue.length).toEqual(0); + queue.addTask(() => task1Deferred.promise); + expect(queue.length).toEqual(1); + queue.addTask(() => task2Deferred.promise); + expect(queue.length).toEqual(2); + task1Deferred.resolve(); + return flushPromiseChains() + .then(() => { + expect(queue.length).toEqual(1); + task2Deferred.resolve(); + return flushPromiseChains(); + }) + .then(() => { + expect(queue.length).toEqual(0); + }); + }); +}); diff --git a/vtest/unit/specs/utils/decodeUriComponentSafely.spec.js b/vtest/unit/specs/utils/decodeUriComponentSafely.spec.js new file mode 100644 index 000000000..018bdabc4 --- /dev/null +++ b/vtest/unit/specs/utils/decodeUriComponentSafely.spec.js @@ -0,0 +1,27 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import decodeUriComponentSafely from "../../../../src/utils/decodeUriComponentSafely.js"; + +describe("decodeUriComponentSafely", () => { + it("decodes a uri encoded string", () => { + expect(decodeUriComponentSafely("%3Fx%3Dtest")).toEqual("?x=test"); + }); + it("returns an empty string when an invalid encoded URI component is provided", () => { + expect( + decodeUriComponentSafely( + "MCORGID%3D5BFE274A5F6980A50A495C08%2540AdobeOrg%ttt", + ), + ).toEqual(""); + }); +}); diff --git a/vtest/unit/specs/utils/deduplicateArray.spec.js b/vtest/unit/specs/utils/deduplicateArray.spec.js new file mode 100644 index 000000000..b59a558cc --- /dev/null +++ b/vtest/unit/specs/utils/deduplicateArray.spec.js @@ -0,0 +1,57 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { deduplicateArray } from "../../../../src/utils/index.js"; + +describe("deduplicateArray", () => { + it("should return an empty array if input is empty", () => { + expect(deduplicateArray([])).toEqual([]); + }); + it("should return an array with one item if input has one item", () => { + const input = [1]; + expect(deduplicateArray(input)).toEqual(input); + }); + it("should return an array with one item if input has two equal items", () => { + const input = [1, 1]; + expect(deduplicateArray(input)).toEqual([1]); + }); + it("should return an array with two items if input has two different items", () => { + const input = [1, 2]; + expect(deduplicateArray(input)).toEqual(input); + }); + it("should return an array with two items if input has three items with two equal items", () => { + const input = [1, 1, 2]; + expect(deduplicateArray(input)).toEqual([1, 2]); + }); + it("should accept a custom equality function", () => { + const input = [ + { + id: 1, + }, + { + id: 1, + }, + { + id: 2, + }, + ]; + const isEqual = (a, b) => a.id === b.id; + expect(deduplicateArray(input, isEqual)).toEqual([ + { + id: 1, + }, + { + id: 2, + }, + ]); + }); +}); diff --git a/vtest/unit/specs/utils/deepAssign.spec.js b/vtest/unit/specs/utils/deepAssign.spec.js new file mode 100644 index 000000000..1d02594d2 --- /dev/null +++ b/vtest/unit/specs/utils/deepAssign.spec.js @@ -0,0 +1,103 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import deepAssign from "../../../../src/utils/deepAssign.js"; + +describe("deepAssign", () => { + it("should throw when target is null or undefined", () => { + expect(() => { + deepAssign(null, { + a: 1, + }); + }).toThrow(); + expect(() => { + deepAssign(undefined, { + a: 1, + }); + }).toThrow(); + }); + it("should assign when target is string", () => { + const result1 = deepAssign("foo", { + a: 1, + }); + const result2 = Object.assign("foo", { + a: 1, + }); + expect(result1).toEqual(result2); + }); + it("should assign when target is number", () => { + const result1 = deepAssign(1, { + a: 1, + }); + const result2 = Object.assign(1, { + a: 1, + }); + expect(result1).toEqual(result2); + }); + it("should assign when target is array", () => { + const result1 = deepAssign([1], { + a: 1, + }); + const result2 = Object.assign([1], { + a: 1, + }); + expect(result1).toEqual(result2); + }); + it("should assign when target is object and source is string", () => { + const result1 = deepAssign({}, "foo"); + const result2 = { + ..."foo", + }; + expect(result1).toEqual(result2); + }); + it("should assign when target is object and source is number", () => { + const result1 = deepAssign({}, 1); + const result2 = { + ...1, + }; + expect(result1).toEqual(result2); + }); + it("should assign when target is object and source is array", () => { + const result1 = deepAssign({}, [1]); + const result2 = { + ...[1], + }; + expect(result1).toEqual(result2); + }); + it("should assign values recursively", () => { + const result = deepAssign( + {}, + { + a: { + c: 1, + }, + }, + { + b: 2, + }, + { + a: { + c: 2, + d: 3, + }, + }, + ); + expect(result).toEqual({ + a: { + c: 2, + d: 3, + }, + b: 2, + }); + }); +}); diff --git a/vtest/unit/specs/utils/defer.spec.js b/vtest/unit/specs/utils/defer.spec.js new file mode 100644 index 000000000..8dfff330f --- /dev/null +++ b/vtest/unit/specs/utils/defer.spec.js @@ -0,0 +1,34 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import defer from "../../../../src/utils/defer.js"; + +describe("defer", () => { + it("resolves an exposed promise", () => { + const deferred = defer(); + deferred.promise.then((value) => { + expect(value).toBe("abc"); + }); + deferred.resolve("abc"); + }); + it("rejects an exposed promise", () => { + const deferred = defer(); + deferred.promise.then( + () => {}, + (value) => { + expect(value).toBe("abc"); + }, + ); + deferred.reject("abc"); + }); +}); diff --git a/vtest/unit/specs/utils/dom/appendNode.spec.js b/vtest/unit/specs/utils/dom/appendNode.spec.js new file mode 100644 index 000000000..d3a903be1 --- /dev/null +++ b/vtest/unit/specs/utils/dom/appendNode.spec.js @@ -0,0 +1,30 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterEach, describe, it, expect } from "vitest"; +import createNode from "../../../../../src/utils/dom/createNode.js"; +import appendNode from "../../../../../src/utils/dom/appendNode.js"; +import selectNodes from "../../../../../src/utils/dom/selectNodes.js"; +import removeNode from "../../../../../src/utils/dom/removeNode.js"; + +describe("DOM::appendNode", () => { + afterEach(() => { + selectNodes("div").forEach(removeNode); + }); + it("should append a node to head tag", () => { + const elem = createNode("div", { + id: "append", + }); + appendNode(document.head, elem); + expect(selectNodes("#append").length).toEqual(1); + }); +}); diff --git a/vtest/unit/specs/utils/dom/awaitSelector.spec.js b/vtest/unit/specs/utils/dom/awaitSelector.spec.js new file mode 100644 index 000000000..a6a108192 --- /dev/null +++ b/vtest/unit/specs/utils/dom/awaitSelector.spec.js @@ -0,0 +1,52 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterAll, describe, it, expect } from "vitest"; +import awaitSelector from "../../../../../src/utils/dom/awaitSelector.js"; + +describe("awaitSelector", () => { + it("await via requestAnimationFrame", (done) => { + // Create test element + const testElement = document.createElement("div"); + testElement.id = "def"; + + // Immediately append element to document + document.body.appendChild(testElement); + + // Now wait for selector + awaitSelector("#def") + .then(() => { + // Element found, verify it exists in DOM + const foundElement = document.querySelector("#def"); + expect(foundElement).toBeTruthy(); + expect(foundElement.id).toBe("def"); + + // Cleanup + document.body.removeChild(testElement); + }) + .catch((error) => { + // Cleanup on error + if (testElement.parentNode) { + document.body.removeChild(testElement); + } + done.fail(error); + }); + }); + + // Ensure cleanup after all tests + afterAll(() => { + const element = document.querySelector("#def"); + if (element) { + element.parentNode.removeChild(element); + } + }); +}); diff --git a/vtest/unit/specs/utils/dom/createNode.spec.js b/vtest/unit/specs/utils/dom/createNode.spec.js new file mode 100644 index 000000000..d24f222f3 --- /dev/null +++ b/vtest/unit/specs/utils/dom/createNode.spec.js @@ -0,0 +1,53 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import createNode from "../../../../../src/utils/dom/createNode.js"; + +describe("DOM::createNode", () => { + it("should createNode with tag only", () => { + const element = createNode("DIV"); + expect(element.tagName).toEqual("DIV"); + }); + it("should createNode with tag and attrs", () => { + const element = createNode("DIV", { + id: "create", + }); + expect(element.tagName).toEqual("DIV"); + expect(element.id).toEqual("create"); + }); + it("should createNode with tag, child", () => { + const element = createNode("DIV", {}, {}, [createNode("p")]); + expect(element.tagName).toEqual("DIV"); + expect(element.firstElementChild.tagName).toEqual("P"); + }); + it("supports style attribute objects", () => { + const element = createNode( + "DIV", + {}, + { + style: { + color: "blue", + }, + }, + ); + expect(element.tagName).toEqual("DIV"); + expect(element.style.color).toEqual("blue"); + }); + it("supports style attribute strings", () => { + const element = createNode("DIV", { + style: "color: blue;", + }); + expect(element.tagName).toEqual("DIV"); + expect(element.style.color).toEqual("blue"); + }); +}); diff --git a/vtest/unit/specs/utils/dom/isShadowSelector.spec.js b/vtest/unit/specs/utils/dom/isShadowSelector.spec.js new file mode 100644 index 000000000..28d6989b7 --- /dev/null +++ b/vtest/unit/specs/utils/dom/isShadowSelector.spec.js @@ -0,0 +1,26 @@ +/* +Copyright 2021 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isShadowSelector from "../../../../../src/utils/dom/isShadowSelector.js"; + +describe("Utils::DOM::isShadowSelector", () => { + it("should detect shadow selectors", () => { + let selector = + "BODY > BUY-NOW-BUTTON:nth-of-type(2):shadow > DIV:nth-of-type(1)"; + let result = isShadowSelector(selector); + expect(result).toBe(true); + selector = "BODY > BUY-NOW-BUTTON:nth-of-type(2) > DIV:nth-of-type(1)"; + result = isShadowSelector(selector); + expect(result).toBe(false); + }); +}); diff --git a/vtest/unit/specs/utils/dom/matchesSelector.spec.js b/vtest/unit/specs/utils/dom/matchesSelector.spec.js new file mode 100644 index 000000000..0562155ba --- /dev/null +++ b/vtest/unit/specs/utils/dom/matchesSelector.spec.js @@ -0,0 +1,23 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import matchesSelector from "../../../../../src/utils/dom/matchesSelector.js"; + +describe("DOM::matchesSelector", () => { + it("should match selector for existing element", () => { + expect(matchesSelector("BODY", document.body)).toEqual(true); + }); + it("should not match selector for non-existing element", () => { + expect(matchesSelector("#bla-bla", document.body)).toEqual(false); + }); +}); diff --git a/vtest/unit/specs/utils/dom/querySelectorAll.spec.js b/vtest/unit/specs/utils/dom/querySelectorAll.spec.js new file mode 100644 index 000000000..ca2e3c036 --- /dev/null +++ b/vtest/unit/specs/utils/dom/querySelectorAll.spec.js @@ -0,0 +1,43 @@ +/* +Copyright 2021 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterEach, describe, it, expect } from "vitest"; +import { + appendNode, + createNode, + removeNode, + selectNodes, +} from "../../../../../src/utils/dom/index.js"; +import querySelectorAll from "../../../../../src/utils/dom/querySelectorAll.js"; + +describe("Personalization::DOM::querySelectorAll", () => { + afterEach(() => { + selectNodes(".qsa").forEach(removeNode); + }); + it("should select with querySelectorAll", () => { + const node = createNode( + "DIV", + { + id: "abc", + class: "qsa", + }, + { + innerHTML: `
    Test
    `, + }, + ); + appendNode(document.body, node); + const selector = ".test"; + const result = querySelectorAll(document, selector); + expect(Array.isArray(result)).toBe(true); + expect(result[0]).toEqual(node.children[0]); + }); +}); diff --git a/vtest/unit/specs/utils/dom/removeNode.spec.js b/vtest/unit/specs/utils/dom/removeNode.spec.js new file mode 100644 index 000000000..83c5106eb --- /dev/null +++ b/vtest/unit/specs/utils/dom/removeNode.spec.js @@ -0,0 +1,30 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { afterEach, describe, it, expect } from "vitest"; +import createNode from "../../../../../src/utils/dom/createNode.js"; +import appendNode from "../../../../../src/utils/dom/appendNode.js"; +import removeNode from "../../../../../src/utils/dom/removeNode.js"; +import selectNodes from "../../../../../src/utils/dom/selectNodes.js"; + +describe("DOM::removeNode", () => { + afterEach(() => { + selectNodes("div").forEach(removeNode); + }); + it("should remove a node from head tag", () => { + const node = createNode("div", { + id: "remove", + }); + removeNode(appendNode(document.head, node)); + expect(selectNodes("#remove").length).toEqual(0); + }); +}); diff --git a/vtest/unit/specs/utils/dom/selectNodes.spec.js b/vtest/unit/specs/utils/dom/selectNodes.spec.js new file mode 100644 index 000000000..af7c6a76f --- /dev/null +++ b/vtest/unit/specs/utils/dom/selectNodes.spec.js @@ -0,0 +1,23 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import selectNodes from "../../../../../src/utils/dom/selectNodes.js"; + +describe("DOM::selectNodes", () => { + it("should return array when nodes are present", () => { + expect(selectNodes("HEAD").length).toEqual(1); + }); + it("should return array when nodes are NOT present", () => { + expect(selectNodes("FOO").length).toEqual(0); + }); +}); diff --git a/vtest/unit/specs/utils/dom/selectNodesWithShadow.spec.js b/vtest/unit/specs/utils/dom/selectNodesWithShadow.spec.js new file mode 100644 index 000000000..1b8cc44af --- /dev/null +++ b/vtest/unit/specs/utils/dom/selectNodesWithShadow.spec.js @@ -0,0 +1,200 @@ +/* +Copyright 2021 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +// eslint-disable-next-line max-classes-per-file +import { afterEach, describe, it, expect } from "vitest"; +import { + createNode, + appendNode, + selectNodes, + removeNode, +} from "../../../../../src/utils/dom/index.js"; +import { selectNodesWithEq } from "../../../../../src/components/Personalization/dom-actions/dom/index.js"; + +const ieDetected = () => !!document.documentMode; +const defineCustomElements = () => { + if (!customElements || customElements.get("buy-now-button")) { + return; + } + const buyNowContent = ` +
    + +
    +
    + +
    +
    +
    + `; + customElements.define( + "buy-now-button", + class extends HTMLElement { + constructor() { + super(); + const shadowRoot = this.attachShadow({ + mode: "open", + }); + shadowRoot.innerHTML = buyNowContent; + } + }, + ); + const productOrderContent = `

    Product order

    Buy
    `; + customElements.define( + "product-order", + class extends HTMLElement { + constructor() { + super(); + const shadowRoot = this.attachShadow({ + mode: "open", + }); + shadowRoot.innerHTML = productOrderContent; + } + }, + ); +}; +describe("Utils::DOM::selectNodesWithShadow", () => { + const CLEANUP_CLASS = "cleanup"; + afterEach(() => { + selectNodes(`.${CLEANUP_CLASS}`).forEach(removeNode); + }); + it("should select when no shadow", () => { + appendNode( + document.body, + createNode("DIV", { + id: "noShadow", + class: CLEANUP_CLASS, + }), + ); + const result = selectNodes("#noShadow"); + expect(result[0].tagName).toEqual("DIV"); + expect(result[0].id).toEqual("noShadow"); + }); + it("should select when one shadow node", () => { + if (ieDetected()) { + return; + } + defineCustomElements(); + const content = ` +
    + FirstButton + SecondButton + +
    `; + appendNode( + document.body, + createNode( + "DIV", + { + id: "abc", + class: CLEANUP_CLASS, + }, + { + innerHTML: content, + }, + ), + ); + const result = selectNodesWithEq( + "#abc:eq(0) > FORM:nth-of-type(1) > BUY-NOW-BUTTON:nth-of-type(2):shadow > DIV:nth-of-type(1) > LABEL:nth-of-type(1)", + ); + expect(result.length).toEqual(1); + expect(result[0].tagName).toEqual("LABEL"); + expect(result[0].textContent).toEqual("Buy Now"); + }); + it("should select when multiple nested shadow nodes", () => { + if (ieDetected()) { + return; + } + defineCustomElements(); + const content = ` +
    + FirstButton + SecondButton + FirstOrder + SecondOrder + +
    `; + appendNode( + document.body, + createNode( + "DIV", + { + id: "abc", + class: CLEANUP_CLASS, + }, + { + innerHTML: content, + }, + ), + ); + const result = selectNodesWithEq( + "#abc:eq(0) > FORM:nth-of-type(1) > PRODUCT-ORDER:nth-of-type(2):shadow > *:eq(0) > BUY-NOW-BUTTON:nth-of-type(1):shadow > DIV:nth-of-type(1) > LABEL:nth-of-type(1)", + ); + expect(result[0].tagName).toEqual("LABEL"); + expect(result[0].textContent).toEqual("Buy Now"); + }); + it("should select when chained :eq:shadow selector", () => { + if (ieDetected()) { + return; + } + defineCustomElements(); + const content = ` +
    + FirstButton + SecondButton + FirstOrder + SecondOrder + +
    `; + appendNode( + document.body, + createNode( + "DIV", + { + id: "abc", + class: CLEANUP_CLASS, + }, + { + innerHTML: content, + }, + ), + ); + const result = selectNodesWithEq( + "#abc:eq(0) > FORM:nth-of-type(1) > PRODUCT-ORDER:eq(1):shadow > *:eq(0) > BUY-NOW-BUTTON:eq(0):shadow > DIV:nth-of-type(1) > LABEL:nth-of-type(1)", + ); + expect(result[0].tagName).toEqual("LABEL"); + expect(result[0].textContent).toEqual("Buy Now"); + }); + it("should respect child selectors", () => { + const content = ` +
    +
    + +
    + +
    + `; + const node = createNode( + "DIV", + { + id: "target", + class: CLEANUP_CLASS, + }, + { + innerHTML: content, + }, + ); + appendNode(document.body, node); + const result = selectNodesWithEq("#target > div:eq(0) > span"); + expect(result[0].tagName).toEqual("SPAN"); + expect(result[0].id).toEqual("right"); + }); +}); diff --git a/vtest/unit/specs/utils/event.spec.js b/vtest/unit/specs/utils/event.spec.js new file mode 100644 index 000000000..05980b441 --- /dev/null +++ b/vtest/unit/specs/utils/event.spec.js @@ -0,0 +1,72 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import { mergeDecisionsMeta, mergeQuery } from "../../../../src/utils/event.js"; +import { PropositionEventType } from "../../../../src/constants/propositionEventType.js"; + +describe("Utils::event", () => { + let event; + beforeEach(() => { + event = { + mergeXdm: vi.fn(), + mergeQuery: vi.fn(), + }; + }); + describe("mergeDecisionsMeta", () => { + it("merges decisions meta", () => { + const decisionsMeta = [ + { + id: "abc", + scope: "home", + }, + { + id: "def", + scope: "cart", + }, + ]; + mergeDecisionsMeta(event, decisionsMeta, [PropositionEventType.DISPLAY]); + expect(event.mergeXdm).toHaveBeenCalledWith({ + _experience: { + decisioning: { + propositions: [ + { + id: "abc", + scope: "home", + }, + { + id: "def", + scope: "cart", + }, + ], + propositionEventType: { + display: 1, + }, + }, + }, + }); + }); + }); + describe("mergeQuery", () => { + it("merges query details", () => { + const details = { + foo: "bar", + }; + mergeQuery(event, details); + expect(event.mergeQuery).toHaveBeenCalledWith({ + personalization: { + foo: "bar", + }, + }); + }); + }); +}); diff --git a/vtest/unit/specs/utils/filterObject.spec.js b/vtest/unit/specs/utils/filterObject.spec.js new file mode 100644 index 000000000..6bd7c7572 --- /dev/null +++ b/vtest/unit/specs/utils/filterObject.spec.js @@ -0,0 +1,63 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import { filterObject } from "../../../../src/utils/index.js"; + +describe("utils:filterObject", () => { + it("should filter out keys with values that do not pass the predicate", () => { + const obj = { + a: 5, + b: 6, + }; + const predicate = (val) => val > 5; + expect(filterObject(obj, predicate)).toEqual({ + b: 6, + }); + }); + it("should filter out nested keys with values that do not pass the predicate", () => { + const obj = { + a: 5, + b: { + c: 6, + }, + }; + const predicate = (val) => val > 5; + expect(filterObject(obj, predicate)).toEqual({ + b: { + c: 6, + }, + }); + }); + it("should filter out deeply nested keys with values that do not pass the predicate", () => { + const obj = { + a: 5, + b: { + c: { + d: 4, + e: 6, + }, + }, + f: { + g: 4, + }, + }; + const predicate = (val) => val > 5; + expect(filterObject(obj, predicate)).toEqual({ + b: { + c: { + e: 6, + }, + }, + }); + }); +}); diff --git a/vtest/unit/specs/utils/flattenArray.spec.js b/vtest/unit/specs/utils/flattenArray.spec.js new file mode 100644 index 000000000..2311e2112 --- /dev/null +++ b/vtest/unit/specs/utils/flattenArray.spec.js @@ -0,0 +1,79 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import flattenArray from "../../../../src/utils/flattenArray.js"; + +describe("flattenArray", () => { + it("recursively flattens an array", () => { + expect( + flattenArray([ + "a", + ["b", "c"], + "d", + ["e"], + "f", + ["g"], + [ + "h", + [ + "i", + ["j"], + "k", + ["l", ["m"], ["n", ["o"], ["p", ["q"], "r"], "s"], "t"], + ], + "u", + ], + "v", + "w", + "x", + "y", + "z", + ]), + ).toEqual([ + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + ]); + }); + it("handles non arrays", () => { + expect( + flattenArray({ + wat: true, + }), + ).toEqual({ + wat: true, + }); + }); +}); diff --git a/vtest/unit/specs/utils/flattenObject.spec.js b/vtest/unit/specs/utils/flattenObject.spec.js new file mode 100644 index 000000000..a3ab6f54a --- /dev/null +++ b/vtest/unit/specs/utils/flattenObject.spec.js @@ -0,0 +1,113 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import flattenObject from "../../../../src/utils/flattenObject.js"; + +describe("flattenObject", () => { + it("flattens event object", () => { + expect( + flattenObject({ + xdm: { + web: { + webPageDetails: { + viewName: "contact", + URL: "https://localhost/aep.html#contact", + }, + webReferrer: { + URL: "https://google.com", + }, + }, + timestamp: "2023-04-12T17:37:56.519Z", + implementationDetails: { + name: "https://ns.adobe.com/experience/alloy", + version: "2.15.0", + environment: "browser", + }, + }, + data: { + moo: "woof", + }, + }), + ).toEqual({ + "xdm.web.webPageDetails.viewName": "contact", + "xdm.web.webPageDetails.URL": "https://localhost/aep.html#contact", + "xdm.web.webReferrer.URL": "https://google.com", + "xdm.timestamp": "2023-04-12T17:37:56.519Z", + "xdm.implementationDetails.name": "https://ns.adobe.com/experience/alloy", + "xdm.implementationDetails.version": "2.15.0", + "xdm.implementationDetails.environment": "browser", + "data.moo": "woof", + }); + }); + it("flattens nested arrays", () => { + expect( + flattenObject({ + pre: true, + a: { + one: 1, + two: 2, + three: { + aa: 2, + bb: 43, + cc: [ + "alf", + "fred", + { + cool: "beans", + lets: "go", + }, + ], + }, + }, + b: { + one: 1, + two: 2, + three: { + poo: true, + }, + }, + c: { + uno: true, + dos: false, + tres: { + value: "yeah ok", + }, + }, + }), + ).toEqual({ + pre: true, + "a.one": 1, + "a.two": 2, + "a.three.aa": 2, + "a.three.bb": 43, + "a.three.cc.0": "alf", + "a.three.cc.1": "fred", + "a.three.cc.2.cool": "beans", + "a.three.cc.2.lets": "go", + "b.one": 1, + "b.two": 2, + "b.three.poo": true, + "c.uno": true, + "c.dos": false, + "c.tres.value": "yeah ok", + }); + }); + it("handles non-objects", () => { + expect(flattenObject(true)).toEqual(true); + expect(flattenObject([1, 2, 3])).toEqual([1, 2, 3]); + expect(flattenObject("hello")).toEqual("hello"); + let obj = new Set(); + expect(obj).toEqual(obj); + obj = () => undefined; + expect(flattenObject(obj)).toEqual(obj); + }); +}); diff --git a/vtest/unit/specs/utils/getApexDomain.spec.js b/vtest/unit/specs/utils/getApexDomain.spec.js new file mode 100644 index 000000000..ba90bb0eb --- /dev/null +++ b/vtest/unit/specs/utils/getApexDomain.spec.js @@ -0,0 +1,67 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import getApexDomain from "../../../../src/utils/getApexDomain.js"; + +const mockWindowWithHostname = (hostname) => { + return { + location: { + hostname, + }, + }; +}; +describe("getTld", () => { + it("returns an empty string when only one host part exists", () => { + const window = mockWindowWithHostname("localhost"); + const cookieJar = { + get() {}, + set() {}, + remove() {}, + }; + expect(getApexDomain(window, cookieJar)).toBe(""); + }); + it("returns the first host that allows a cookie to be set", () => { + const window = mockWindowWithHostname("a.b.c.co.uk"); + let storedValue; + const cookieJar = { + get() { + return storedValue; + }, + set(name, value, options) { + if (options.domain === "c.co.uk") { + storedValue = value; + } + }, + remove: vi.fn(), + }; + expect(getApexDomain(window, cookieJar)).toBe("c.co.uk"); + expect(cookieJar.remove).toHaveBeenCalled(); + }); + it("tries all segments of the hostname if necessary", () => { + const window = mockWindowWithHostname("10.30.34.68"); + let storedValue; + const cookieJar = { + get() { + return storedValue; + }, + set(name, value, options) { + if (options.domain === "10.30.34.68") { + storedValue = value; + } + }, + remove: vi.fn(), + }; + expect(getApexDomain(window, cookieJar)).toBe("10.30.34.68"); + expect(cookieJar.remove).toHaveBeenCalled(); + }); +}); diff --git a/vtest/unit/specs/utils/getLastArrayItems.spec.js b/vtest/unit/specs/utils/getLastArrayItems.spec.js new file mode 100644 index 000000000..0c9f752fb --- /dev/null +++ b/vtest/unit/specs/utils/getLastArrayItems.spec.js @@ -0,0 +1,25 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import getLastArrayItems from "../../../../src/utils/getLastArrayItems.js"; + +describe("getLastArrayItems", () => { + const letters = ["a", "b", "c"]; + it("returns last items from array larger than count", () => { + expect(getLastArrayItems(letters, 2)).toEqual(["b", "c"]); + }); + it("returns all items from array smaller than or equal to count", () => { + expect(getLastArrayItems(letters, 10)).toEqual(["a", "b", "c"]); + expect(getLastArrayItems(letters, 3)).toEqual(["a", "b", "c"]); + }); +}); diff --git a/vtest/unit/specs/utils/getNamespacedCookieName.spec.js b/vtest/unit/specs/utils/getNamespacedCookieName.spec.js new file mode 100644 index 000000000..9aa5e844a --- /dev/null +++ b/vtest/unit/specs/utils/getNamespacedCookieName.spec.js @@ -0,0 +1,21 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import getNamespacedCookieName from "../../../../src/utils/getNamespacedCookieName.js"; + +describe("getNamespacedCookieName", () => { + it("returns namespaced cookie name", () => { + const result = getNamespacedCookieName("ABC@CustomOrg", "foo"); + expect(result).toBe("kndctr_ABC_CustomOrg_foo"); + }); +}); diff --git a/vtest/unit/specs/utils/getNamespacedStorage.spec.js b/vtest/unit/specs/utils/getNamespacedStorage.spec.js new file mode 100644 index 000000000..a170cbeae --- /dev/null +++ b/vtest/unit/specs/utils/getNamespacedStorage.spec.js @@ -0,0 +1,27 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import injectStorage from "../../../../src/utils/injectStorage.js"; + +const getNamespacedStorage = injectStorage(window); +const storage = getNamespacedStorage("namespace"); +describe("getNamespacedStorage", () => { + it("is able to write and read from session storage", () => { + storage.session.setItem("test", "session-storage"); + expect(storage.session.getItem("test")).toBe("session-storage"); + }); + it("is able to write and read from persistent storage", () => { + storage.persistent.setItem("test", "persistent-storage"); + expect(storage.persistent.getItem("test")).toBe("persistent-storage"); + }); +}); diff --git a/vtest/unit/specs/utils/groupBy.spec.js b/vtest/unit/specs/utils/groupBy.spec.js new file mode 100644 index 000000000..116567edf --- /dev/null +++ b/vtest/unit/specs/utils/groupBy.spec.js @@ -0,0 +1,101 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import groupBy from "../../../../src/utils/groupBy.js"; + +describe("groupBy", () => { + it("expects empty obj if array is empty", () => { + const array = []; + expect(groupBy(array, null)).toEqual({}); + }); + it("expects to group by key getter provided", () => { + const array = [ + { + id: 1, + name: "Foo", + }, + { + id: 2, + name: "Foo2", + }, + { + id: 2, + name: "Foo3", + }, + ]; + const map = { + 1: [ + { + id: 1, + name: "Foo", + }, + ], + 2: [ + { + id: 2, + name: "Foo2", + }, + { + id: 2, + name: "Foo3", + }, + ], + }; + expect(groupBy(array, (item) => item.id || "default")).toEqual(map); + }); + it("expects to group by key getter provided or to the default key", () => { + const array = [ + { + id: 1, + name: "Foo", + }, + { + id: 2, + name: "Foo2", + }, + { + id: 2, + name: "Foo3", + }, + { + noId: 2, + name: "Foo3", + }, + ]; + const map = { + 1: [ + { + id: 1, + name: "Foo", + }, + ], + 2: [ + { + id: 2, + name: "Foo2", + }, + { + id: 2, + name: "Foo3", + }, + ], + default: [ + { + noId: 2, + name: "Foo3", + }, + ], + }; + expect(groupBy(array, (item) => item.id || "default")).toEqual(map); + }); +}); diff --git a/vtest/unit/specs/utils/injectAreThirdPartyCookiesSupportedByDefault.spec.js b/vtest/unit/specs/utils/injectAreThirdPartyCookiesSupportedByDefault.spec.js new file mode 100644 index 000000000..8a1eb15ab --- /dev/null +++ b/vtest/unit/specs/utils/injectAreThirdPartyCookiesSupportedByDefault.spec.js @@ -0,0 +1,49 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectAreThirdPartyCookiesSupportedByDefault from "../../../../src/utils/injectAreThirdPartyCookiesSupportedByDefault.js"; +import { + CHROME, + EDGE, + EDGE_CHROMIUM, + FIREFOX, + IE, + SAFARI, + UNKNOWN, +} from "../../../../src/constants/browser.js"; + +const browsersWithSupport = [CHROME, EDGE, EDGE_CHROMIUM, IE, UNKNOWN]; +const browsersWithoutSupport = [FIREFOX, SAFARI]; +describe("areThirdPartyCookiesSupportedByDefault", () => { + let getBrowser; + let areThirdPartyCookiesSupportedByDefault; + beforeEach(() => { + getBrowser = vi.fn(); + areThirdPartyCookiesSupportedByDefault = + injectAreThirdPartyCookiesSupportedByDefault({ + getBrowser, + }); + }); + browsersWithSupport.forEach((browser) => { + it(`reports true for ${browser}`, () => { + getBrowser.mockReturnValue(browser); + expect(areThirdPartyCookiesSupportedByDefault()).toBe(true); + }); + }); + browsersWithoutSupport.forEach((browser) => { + it(`reports false for ${browser}`, () => { + getBrowser.mockReturnValue(browser); + expect(areThirdPartyCookiesSupportedByDefault()).toBe(false); + }); + }); +}); diff --git a/vtest/unit/specs/utils/injectDoesIdentityCookieExist.spec.js b/vtest/unit/specs/utils/injectDoesIdentityCookieExist.spec.js new file mode 100644 index 000000000..4b1ff6d94 --- /dev/null +++ b/vtest/unit/specs/utils/injectDoesIdentityCookieExist.spec.js @@ -0,0 +1,36 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { beforeEach, afterEach, describe, it, expect } from "vitest"; +import { + injectDoesIdentityCookieExist, + cookieJar, +} from "../../../../src/utils/index.js"; +import removeAllCookies from "../../helpers/removeAllCookies.js"; + +describe("Identity::injectDoesIdentityCookieExist", () => { + beforeEach(removeAllCookies); + afterEach(removeAllCookies); + it("returns false if cookie does not exist", () => { + const doesIdentityCookieExist = injectDoesIdentityCookieExist({ + orgId: "org@adobe", + }); + expect(doesIdentityCookieExist()).toBe(false); + }); + it("returns true if cookie exists", () => { + cookieJar.set("kndctr_org_adobe_identity", "user@adobe"); + const doesIdentityCookieExist = injectDoesIdentityCookieExist({ + orgId: "org@adobe", + }); + expect(doesIdentityCookieExist()).toBe(true); + }); +}); diff --git a/vtest/unit/specs/utils/injectFireReferrerHideableImage.spec.js b/vtest/unit/specs/utils/injectFireReferrerHideableImage.spec.js new file mode 100644 index 000000000..91ee993cc --- /dev/null +++ b/vtest/unit/specs/utils/injectFireReferrerHideableImage.spec.js @@ -0,0 +1,78 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import injectFireReferrerHideableImage from "../../../../src/utils/injectFireReferrerHideableImage.js"; + +describe("injectFireReferrerHideableImage", () => { + let appendNodeMock; + let awaitSelectorMock; + let createNodeMock; + let fireImageMock; + let fireReferrerHideableImage; + beforeEach(() => { + appendNodeMock = vi.fn().mockImplementation(() => ({ + contentWindow: { + document: {}, + }, + })); + awaitSelectorMock = vi + .fn() + .mockImplementation(() => Promise.resolve(["body"])); + createNodeMock = vi.fn().mockImplementation(() => ({ + contentWindow: { + document: {}, + }, + })); + fireImageMock = vi.fn().mockImplementation(() => Promise.resolve()); + fireReferrerHideableImage = injectFireReferrerHideableImage({ + appendNode: appendNodeMock, + awaitSelector: awaitSelectorMock, + createNode: createNodeMock, + fireImage: fireImageMock, + }); + }); + it("should create an iframe for a request that hides the referrer", async () => { + const request = { + hideReferrer: true, + url: "https://adobe.com/test-referrer.jpg", + }; + await fireReferrerHideableImage(request); + expect(createNodeMock).toHaveBeenCalled(); + expect(createNodeMock.mock.calls[0]).toContain("IFRAME"); + expect(fireImageMock).toHaveBeenCalled(); + }); + it("should fire the image on the page for a request that does not hide the referrer", async () => { + const request = { + hideReferrer: false, + url: "https://adobe.com/test-referrer.jpg", + }; + await fireReferrerHideableImage(request); + expect(createNodeMock).not.toHaveBeenCalled(); + expect(fireImageMock).toHaveBeenCalled(); + }); + it("should only create one iframe when called multiple times", async () => { + const request = { + hideReferrer: true, + url: "https://adobe.com/test-invalid-referrer.jpg", + }; + const secondRequest = { + hideReferrer: true, + url: "https://adobe.com/test-invalid-referrer2.jpg", + }; + await fireReferrerHideableImage(request); + await fireReferrerHideableImage(secondRequest); + expect(createNodeMock).toHaveBeenCalledTimes(1); + expect(createNodeMock.mock.calls[0]).toContain("IFRAME"); + expect(fireImageMock).toHaveBeenCalledTimes(2); + }); +}); diff --git a/vtest/unit/specs/utils/injectGetBrowser.spec.js b/vtest/unit/specs/utils/injectGetBrowser.spec.js new file mode 100644 index 000000000..56ddc6101 --- /dev/null +++ b/vtest/unit/specs/utils/injectGetBrowser.spec.js @@ -0,0 +1,70 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import injectGetBrowser from "../../../../src/utils/injectGetBrowser.js"; +import { + EDGE, + EDGE_CHROMIUM, + CHROME, + FIREFOX, + IE, + SAFARI, + UNKNOWN, +} from "../../../../src/constants/browser.js"; + +const userAgentsByBrowser = { + [EDGE]: [ + "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10136", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134", + ], + [EDGE_CHROMIUM]: [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3763.0 Safari/537.36 Edg/75.0.131.0", + ], + // Chrome on iOS will not be detected as Chrome. That is because Chrome on + // iOS is just Safari wrapped in Chrome. + [CHROME]: [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36", + "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36", + ], + [FIREFOX]: [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:69.0) Gecko/20100101 Firefox/69.0", + "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0", + ], + [IE]: [ + "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; Tablet PC 2.0; rv:11.0) like Gecko", + "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko", + ], + [SAFARI]: [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15", + "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13 Mobile/15E148 Safari/604.1", + ], + [UNKNOWN]: [ + "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 OPR/43.0.2442.991", + // Opera + "Mozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9", // Konqueror + ], +}; +describe("getBrowser", () => { + Object.keys(userAgentsByBrowser).forEach((browser) => { + const userAgents = userAgentsByBrowser[browser]; + userAgents.forEach((userAgent) => { + it(`reports ${browser} for ${userAgent}`, () => { + expect( + injectGetBrowser({ + userAgent, + })(), + ).toBe(browser); + }); + }); + }); +}); diff --git a/vtest/unit/specs/utils/injectStorage.spec.js b/vtest/unit/specs/utils/injectStorage.spec.js new file mode 100644 index 000000000..999de3dbc --- /dev/null +++ b/vtest/unit/specs/utils/injectStorage.spec.js @@ -0,0 +1,112 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import injectStorage from "../../../../src/utils/injectStorage.js"; + +describe("injectStorage", () => { + [ + { + storageProperty: "session", + windowProperty: "sessionStorage", + }, + { + storageProperty: "persistent", + windowProperty: "localStorage", + }, + ].forEach(({ storageProperty, windowProperty }) => { + describe(storageProperty, () => { + describe("setItem", () => { + it("sets item", () => { + const window = { + [windowProperty]: { + setItem: vi.fn().mockReturnValue(true), + }, + }; + const storage = injectStorage(window)("example."); + const result = storage[storageProperty].setItem("foo", "bar"); + expect(window[windowProperty].setItem).toHaveBeenCalledWith( + "com.adobe.alloy.example.foo", + "bar", + ); + expect(result).toBe(true); + }); + it("returns false if an error occurs setting item", () => { + const window = { + [windowProperty]: { + setItem: vi.fn().mockImplementation(() => { + throw new Error(); + }), + }, + }; + const storage = injectStorage(window)("example."); + const result = storage[storageProperty].setItem("foo", "bar"); + expect(result).toBe(false); + }); + }); + describe("getItem", () => { + it("gets item", () => { + const window = { + [windowProperty]: { + getItem: vi.fn().mockReturnValue("abc"), + }, + }; + const storage = injectStorage(window)("example."); + const result = storage[storageProperty].getItem("foo"); + expect(window[windowProperty].getItem).toHaveBeenCalledWith( + "com.adobe.alloy.example.foo", + ); + expect(result).toBe("abc"); + }); + it("returns null if an error occurs while getting item", () => { + const window = { + [windowProperty]: { + getItem: vi.fn().mockImplementation(() => { + throw new Error(); + }), + }, + }; + const storage = injectStorage(window)("example."); + const result = storage[storageProperty].getItem("foo"); + expect(result).toBeNull(); + }); + }); + describe("clear", () => { + it("clears all with the namespace prefix", () => { + const window = { + [windowProperty]: { + removeItem: vi.fn(), + "com.adobe.alloy.example.a": "1", + "com.adobe.alloy.example.b": "2", + c: "3", + "com.adobe.alloy.d": "4", + }, + }; + const storage = injectStorage(window)("example."); + storage[storageProperty].clear(); + expect(window[windowProperty].removeItem).toHaveBeenCalledWith( + "com.adobe.alloy.example.a", + ); + expect(window[windowProperty].removeItem).toHaveBeenCalledWith( + "com.adobe.alloy.example.b", + ); + expect(window[windowProperty].removeItem).not.toHaveBeenCalledWith( + "c", + ); + expect(window[windowProperty].removeItem).not.toHaveBeenCalledWith( + "com.adobe.alloy.d", + ); + }); + }); + }); + }); +}); diff --git a/vtest/unit/specs/utils/intersection.spec.js b/vtest/unit/specs/utils/intersection.spec.js new file mode 100644 index 000000000..f88f77a58 --- /dev/null +++ b/vtest/unit/specs/utils/intersection.spec.js @@ -0,0 +1,25 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import intersection from "../../../../src/utils/intersection.js"; + +describe("intersection", () => { + it("returns items that are found within both arrays", () => { + const result = intersection(["a", "b", "c", "d"], ["z", "b", "d"]); + expect(result).toEqual(["b", "d"]); + }); + it("returns an empty array if there are no matches", () => { + const result = intersection(["a", "b", "c", "d"], ["e"]); + expect(result).toEqual([]); + }); +}); diff --git a/vtest/unit/specs/utils/isBlankString.spec.js b/vtest/unit/specs/utils/isBlankString.spec.js new file mode 100644 index 000000000..9235fe729 --- /dev/null +++ b/vtest/unit/specs/utils/isBlankString.spec.js @@ -0,0 +1,29 @@ +/* +Copyright 2021 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isBlankString from "../../../../src/utils/isBlankString.js"; + +describe("isBlankString", () => { + it("returns true when null", () => { + expect(isBlankString(null)).toBe(true); + }); + it("returns true when not a string", () => { + expect(isBlankString(42)).toBe(true); + }); + it("returns true for a blank string", () => { + expect(isBlankString("")).toBe(true); + }); + it("returns false for a string", () => { + expect(isBlankString("hi")).toBe(false); + }); +}); diff --git a/vtest/unit/specs/utils/isBoolean.spec.js b/vtest/unit/specs/utils/isBoolean.spec.js new file mode 100644 index 000000000..a5f483f7c --- /dev/null +++ b/vtest/unit/specs/utils/isBoolean.spec.js @@ -0,0 +1,27 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isBoolean from "../../../../src/utils/isBoolean.js"; + +const nonBooleans = [{}, [], new Date(), /abc/, "foo", 123]; +describe("isString", () => { + it("returns true if the value is boolean", () => { + expect(isBoolean(true)).toBe(true); + expect(isBoolean(false)).toBe(true); + }); + it("returns false if the value is not a boolean", () => { + nonBooleans.forEach((value) => { + expect(isBoolean(value)).toBe(false); + }); + }); +}); diff --git a/vtest/unit/specs/utils/isEmptyObject.spec.js b/vtest/unit/specs/utils/isEmptyObject.spec.js new file mode 100644 index 000000000..64277f717 --- /dev/null +++ b/vtest/unit/specs/utils/isEmptyObject.spec.js @@ -0,0 +1,31 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isEmptyObject from "../../../../src/utils/isEmptyObject.js"; + +const nonEmptyObjects = [ + "abc", + { + a: 123, + }, +]; +describe("isEmptyObject", () => { + it("returns true if the value is an empty object", () => { + expect(isEmptyObject({})).toBe(true); + }); + it("returns false if the value is not an empty object", () => { + nonEmptyObjects.forEach((obj) => { + expect(isEmptyObject(obj)).toBe(false); + }); + }); +}); diff --git a/vtest/unit/specs/utils/isFunction.spec.js b/vtest/unit/specs/utils/isFunction.spec.js new file mode 100644 index 000000000..b8312b8c6 --- /dev/null +++ b/vtest/unit/specs/utils/isFunction.spec.js @@ -0,0 +1,26 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isFunction from "../../../../src/utils/isFunction.js"; + +const nonFunctions = [{}, [], new Date(), /abc/, true, false, "text", 123]; +describe("isFunction", () => { + it("returns true if the value is a function", () => { + expect(isFunction(() => {})).toBe(true); + }); + it("returns false if the value is not a function", () => { + nonFunctions.forEach((nonFunction) => { + expect(isFunction(nonFunction)).toBe(false); + }); + }); +}); diff --git a/vtest/unit/specs/utils/isInteger.spec.js b/vtest/unit/specs/utils/isInteger.spec.js new file mode 100644 index 000000000..8239c3c94 --- /dev/null +++ b/vtest/unit/specs/utils/isInteger.spec.js @@ -0,0 +1,27 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isInteger from "../../../../src/utils/isInteger.js"; + +describe("isInteger", () => { + it("returns true if the value is an integer", () => { + [123, -123].forEach((value) => expect(isInteger(value)).toBe(true)); + }); + + // eslint-disable-next-line no-restricted-globals + it("returns false if the value is not an integer", () => { + [null, undefined, NaN, "abc", "123", 123.45, -123.45].forEach((value) => + expect(isInteger(value)).toBe(false), + ); + }); +}); diff --git a/vtest/unit/specs/utils/isNamespacedCookieName.spec.js b/vtest/unit/specs/utils/isNamespacedCookieName.spec.js new file mode 100644 index 000000000..8debf7d7e --- /dev/null +++ b/vtest/unit/specs/utils/isNamespacedCookieName.spec.js @@ -0,0 +1,31 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isNamespacedCookieName from "../../../../src/utils/isNamespacedCookieName.js"; + +describe("isNamespacedCookieName", () => { + it("returns true if it's a namespaced cookie name", () => { + const result = isNamespacedCookieName( + "ABC@CustomOrg", + "kndctr_ABC_CustomOrg_foo", + ); + expect(result).toBe(true); + }); + it("returns false if it's not a namespaced cookie name", () => { + const result = isNamespacedCookieName( + "kndctr_DEF_CustomOrg_foo", + "ABC@CustomOrg", + ); + expect(result).toBe(false); + }); +}); diff --git a/vtest/unit/specs/utils/isNil.spec.js b/vtest/unit/specs/utils/isNil.spec.js new file mode 100644 index 000000000..60c0ec25b --- /dev/null +++ b/vtest/unit/specs/utils/isNil.spec.js @@ -0,0 +1,31 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isNil from "../../../../src/utils/isNil.js"; + +describe("isNil", () => { + it("returns true when null", () => { + expect(isNil(null)).toBe(true); + }); + it("returns true when undefined", () => { + expect(isNil(undefined)).toBe(true); + }); + it("returns false when value", () => { + expect(isNil(1)).toBe(false); + expect(isNil({})).toBe(false); + expect(isNil("aaa")).toBe(false); + expect(isNil(true)).toBe(false); + expect(isNil(false)).toBe(false); + expect(isNil([])).toBe(false); + }); +}); diff --git a/vtest/unit/specs/utils/isNonEmptyArray.spec.js b/vtest/unit/specs/utils/isNonEmptyArray.spec.js new file mode 100644 index 000000000..121ff5dda --- /dev/null +++ b/vtest/unit/specs/utils/isNonEmptyArray.spec.js @@ -0,0 +1,30 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isNonEmptyArray from "../../../../src/utils/isNonEmptyArray.js"; + +describe("isNonEmptyArray", () => { + it("returns true when array with values", () => { + expect(isNonEmptyArray([1, 2, 3])).toBe(true); + }); + it("returns false when array is empty", () => { + expect(isNonEmptyArray([])).toBe(false); + }); + it("returns false when undefined or null", () => { + expect(isNonEmptyArray(undefined)).toBe(false); + expect(isNonEmptyArray(null)).toBe(false); + }); + it("returns false when non array", () => { + expect(isNonEmptyArray("123")).toBe(false); + }); +}); diff --git a/vtest/unit/specs/utils/isNonEmptyString.spec.js b/vtest/unit/specs/utils/isNonEmptyString.spec.js new file mode 100644 index 000000000..a79cec5e3 --- /dev/null +++ b/vtest/unit/specs/utils/isNonEmptyString.spec.js @@ -0,0 +1,26 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isNonEmptyString from "../../../../src/utils/isNonEmptyString.js"; + +describe("isNonEmptyString", () => { + it("returns true when string", () => { + expect(isNonEmptyString("1234")).toBe(true); + }); + it("returns false when empty string", () => { + expect(isNonEmptyString("")).toBe(false); + }); + it("returns false when undefined string", () => { + expect(isNonEmptyString(undefined)).toBe(false); + }); +}); diff --git a/vtest/unit/specs/utils/isNumber.spec.js b/vtest/unit/specs/utils/isNumber.spec.js new file mode 100644 index 000000000..f739a5fdd --- /dev/null +++ b/vtest/unit/specs/utils/isNumber.spec.js @@ -0,0 +1,29 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isNumber from "../../../../src/utils/isNumber.js"; + +describe("isNumber", () => { + it("returns true if the value is a number", () => { + [123, 123.45, -123, -123.45].forEach((value) => + expect(isNumber(value)).toBe(true), + ); + }); + + // eslint-disable-next-line no-restricted-globals + it("returns false if the value is not a number", () => { + [null, undefined, NaN, "abc", "123"].forEach((value) => + expect(isNumber(value)).toBe(false), + ); + }); +}); diff --git a/vtest/unit/specs/utils/isObject.spec.js b/vtest/unit/specs/utils/isObject.spec.js new file mode 100644 index 000000000..3e9f040da --- /dev/null +++ b/vtest/unit/specs/utils/isObject.spec.js @@ -0,0 +1,26 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isObject from "../../../../src/utils/isObject.js"; + +const nonObjects = [[], true, false, 123]; +describe("isObject", () => { + it("returns true if the value is an object", () => { + expect(isObject({})).toBe(true); + }); + it("returns false if the value is not an object", () => { + nonObjects.forEach((obj) => { + expect(isObject(obj)).toBe(false); + }); + }); +}); diff --git a/vtest/unit/specs/utils/isString.spec.js b/vtest/unit/specs/utils/isString.spec.js new file mode 100644 index 000000000..08455bb02 --- /dev/null +++ b/vtest/unit/specs/utils/isString.spec.js @@ -0,0 +1,26 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isString from "../../../../src/utils/isString.js"; + +const nonStrings = [{}, [], new Date(), /abc/, true, false, 123]; +describe("isString", () => { + it("returns true if the value is a string", () => { + expect(isString("123")).toBe(true); + }); + it("returns false if the value is not a string", () => { + nonStrings.forEach((str) => { + expect(isString(str)).toBe(false); + }); + }); +}); diff --git a/vtest/unit/specs/utils/isUnique.spec.js b/vtest/unit/specs/utils/isUnique.spec.js new file mode 100644 index 000000000..3cc0f2959 --- /dev/null +++ b/vtest/unit/specs/utils/isUnique.spec.js @@ -0,0 +1,23 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isUnique from "../../../../src/utils/isUnique.js"; + +describe("isUnique", () => { + it("returns true if array values are unique", () => { + expect(isUnique(["item1", "item2", "item3"])).toBe(true); + }); + it("returns false if array contains duplicate values", () => { + expect(isUnique(["item1", "item1", "item3"])).toBe(false); + }); +}); diff --git a/vtest/unit/specs/utils/isValidRegExp.spec.js b/vtest/unit/specs/utils/isValidRegExp.spec.js new file mode 100644 index 000000000..a857e03db --- /dev/null +++ b/vtest/unit/specs/utils/isValidRegExp.spec.js @@ -0,0 +1,27 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import isValidRegExp from "../../../../src/utils/isValidRegExp.js"; + +describe("isValidRegExp", () => { + ["steel|bronze", "/a/", "/^[a-z0-9+]:///i"].forEach((value) => { + it(`validates ${value}`, () => { + expect(isValidRegExp(value)).toBe(true); + }); + }); + ["[", "*"].forEach((value) => { + it(`rejects ${value}`, () => { + expect(isValidRegExp(value)).toBe(false); + }); + }); +}); diff --git a/vtest/unit/specs/utils/lazy.spec.js b/vtest/unit/specs/utils/lazy.spec.js new file mode 100644 index 000000000..3917eabb6 --- /dev/null +++ b/vtest/unit/specs/utils/lazy.spec.js @@ -0,0 +1,49 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, beforeEach, describe, it, expect } from "vitest"; +import lazy from "../../../../src/utils/lazy.js"; + +describe("lazy", () => { + let factory; + let getter; + beforeEach(() => { + factory = vi.fn(); + factory.mockReturnValue("result"); + getter = lazy(factory); + }); + it("doesn't call the factory function before the first time the getter is called", () => { + expect(factory).not.toHaveBeenCalled(); + }); + it("calls the factory function the first time the getter is called", () => { + getter(); + expect(factory).toHaveBeenCalledTimes(1); + }); + it("doesn't call the factory function the second time the getter is called", () => { + getter(); + getter(); + expect(factory).toHaveBeenCalledTimes(1); + }); + it("returns the result of the factory function", () => { + expect(getter()).toBe("result"); + }); + it("returns the same result the second time the getter is called", () => { + const result = getter(); + expect(getter()).toBe(result); + }); + it("handles factory functions that return undefined", () => { + factory.mockReturnValue(undefined); + getter(); + expect(getter()).toBeUndefined(); + expect(factory).toHaveBeenCalledTimes(1); + }); +}); diff --git a/vtest/unit/specs/utils/networkErrors.spec.js b/vtest/unit/specs/utils/networkErrors.spec.js new file mode 100644 index 000000000..b34f1cd39 --- /dev/null +++ b/vtest/unit/specs/utils/networkErrors.spec.js @@ -0,0 +1,78 @@ +import { describe, it, expect } from "vitest"; +import { + TYPE_ERROR, + NETWORK_ERROR, + isNetworkError, +} from "../../../../src/utils/networkErrors.js"; + +describe("Network Errors", () => { + describe("isNetworkError", () => { + it("returns true for TypeError", () => { + const error = new Error(); + error.name = TYPE_ERROR; + expect(isNetworkError(error)).toBe(true); + }); + it("returns true for NetworkError", () => { + const error = new Error(); + error.name = NETWORK_ERROR; + expect(isNetworkError(error)).toBe(true); + }); + it("returns true for status 0", () => { + const error = { + status: 0, + }; + expect(isNetworkError(error)).toBe(true); + }); + it("returns false for other errors", () => { + const error = new Error(); + error.name = "SyntaxError"; + expect(isNetworkError(error)).toBe(false); + }); + it("returns false for non-zero status", () => { + const error = { + status: 500, + }; + expect(isNetworkError(error)).toBe(false); + }); + it("returns false for undefined status", () => { + const error = new Error(); + expect(isNetworkError(error)).toBe(false); + }); + }); +}); + +describe("Network Errors", () => { + describe("isNetworkError", () => { + it("returns true for TypeError", () => { + const error = new Error(); + error.name = TYPE_ERROR; + expect(isNetworkError(error)).toBe(true); + }); + it("returns true for NetworkError", () => { + const error = new Error(); + error.name = NETWORK_ERROR; + expect(isNetworkError(error)).toBe(true); + }); + it("returns true for status 0", () => { + const error = { + status: 0, + }; + expect(isNetworkError(error)).toBe(true); + }); + it("returns false for other errors", () => { + const error = new Error(); + error.name = "SyntaxError"; + expect(isNetworkError(error)).toBe(false); + }); + it("returns false for non-zero status", () => { + const error = { + status: 500, + }; + expect(isNetworkError(error)).toBe(false); + }); + it("returns false for undefined status", () => { + const error = new Error(); + expect(isNetworkError(error)).toBe(false); + }); + }); +}); diff --git a/vtest/unit/specs/utils/noop.spec.js b/vtest/unit/specs/utils/noop.spec.js new file mode 100644 index 000000000..a24d2b6ad --- /dev/null +++ b/vtest/unit/specs/utils/noop.spec.js @@ -0,0 +1,20 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import noop from "../../../../src/utils/noop.js"; + +describe("noop", () => { + it("accepts any arguments and returns undefined", () => { + expect(noop("foo")).toBeUndefined(); + }); +}); diff --git a/vtest/unit/specs/utils/parseUrl.spec.js b/vtest/unit/specs/utils/parseUrl.spec.js new file mode 100644 index 000000000..2398cec25 --- /dev/null +++ b/vtest/unit/specs/utils/parseUrl.spec.js @@ -0,0 +1,56 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import parseUrl from "../../../../src/utils/parseUrl.js"; + +describe("parseUrl", () => { + it("should parse a valid URL with all components", () => { + const url = "https://example.com/path/to/page?param=value#section"; + const result = parseUrl(url); + expect(result.path).toBe("/path/to/page"); + expect(result.query).toBe("param=value"); + expect(result.fragment).toBe("section"); + expect(result.domain).toBe("example.com"); + expect(result.subdomain).toBe(""); + expect(result.topLevelDomain).toBe("com"); + }); + it("should handle URL without subdomain", () => { + const url = "https://example.com"; + const result = parseUrl(url); + expect(result.path).toBe(""); + expect(result.query).toBe(""); + expect(result.fragment).toBe(""); + expect(result.domain).toBe("example.com"); + expect(result.subdomain).toBe(""); + expect(result.topLevelDomain).toBe("com"); + }); + it("should handle empty URL and return default values", () => { + const url = ""; + const result = parseUrl(url); + expect(result.path).toBe(""); + expect(result.query).toBe(""); + expect(result.fragment).toBe(""); + expect(result.domain).toBe(""); + expect(result.subdomain).toBe(""); + expect(result.topLevelDomain).toBe(""); + }); + it("should handle URL with subdomain", () => { + const url = "https://www.example.com"; + const result = parseUrl(url); + expect(result.path).toBe(""); + expect(result.query).toBe(""); + expect(result.fragment).toBe(""); + expect(result.domain).toBe("www.example.com"); + expect(result.subdomain).toBe(""); + expect(result.topLevelDomain).toBe("com"); + }); +}); diff --git a/vtest/unit/specs/utils/prepareConfigOverridesForEdge.spec.js b/vtest/unit/specs/utils/prepareConfigOverridesForEdge.spec.js new file mode 100644 index 000000000..59b0b38a8 --- /dev/null +++ b/vtest/unit/specs/utils/prepareConfigOverridesForEdge.spec.js @@ -0,0 +1,81 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import { prepareConfigOverridesForEdge } from "../../../../src/utils/index.js"; + +describe("utils:prepareConfigOverridesForEdge", () => { + it("should filter out functions, unused objects and keys, empty arrays, and empty strings", () => { + expect( + prepareConfigOverridesForEdge({ + com_adobe_experience_platform: { + datasets: { + event: { + datasetId: "werewr", + }, + profile: { + datasetId: "", + }, + }, + enabled: false, + }, + com_adobe_analytics: { + reportSuites: [], + }, + com_adobe_identity: {}, + com_adobe_target: { + propertyToken: "rrr", + environmentId: 0, + }, + toString: () => "{ com_adobe_experience_platform: '' }", + }), + ).toEqual({ + com_adobe_experience_platform: { + datasets: { + event: { + datasetId: "werewr", + }, + }, + enabled: false, + }, + com_adobe_target: { + propertyToken: "rrr", + environmentId: 0, + }, + }); + }); + it("should return null for empty config objects", () => { + expect( + prepareConfigOverridesForEdge({ + com_adobe_experience_platform: { + datasets: { + event: { + datasetId: "", + }, + profile: { + datasetId: "", + }, + }, + }, + com_adobe_analytics: { + reportSuites: [], + }, + com_adobe_identity: { + idSyncContainerId: "", + }, + com_adobe_target: { + propertyToken: "", + }, + }), + ).toBeNull(); + }); +}); diff --git a/vtest/unit/specs/utils/request/createAddIdentity.spec.js b/vtest/unit/specs/utils/request/createAddIdentity.spec.js new file mode 100644 index 000000000..cfa56435e --- /dev/null +++ b/vtest/unit/specs/utils/request/createAddIdentity.spec.js @@ -0,0 +1,81 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { createAddIdentity } from "../../../../../src/utils/request/index.js"; + +describe("createAddIdentity", () => { + it("should return a function to add identity", () => { + const content = {}; + const addIdentity = createAddIdentity(content); + expect(typeof addIdentity).toBe("function"); + addIdentity("IDNS", { + id: "ABC123", + }); + expect(content).toEqual({ + xdm: { + identityMap: { + IDNS: [ + { + id: "ABC123", + }, + ], + }, + }, + }); + }); + it("should append identity map if called more than once", () => { + const content = {}; + const addIdentity = createAddIdentity(content); + addIdentity("IDNS", { + id: "ABC123", + }); + addIdentity("IDNS", { + id: "ABC456", + }); + expect(content).toEqual({ + xdm: { + identityMap: { + IDNS: [ + { + id: "ABC123", + }, + { + id: "ABC456", + }, + ], + }, + }, + }); + addIdentity("IDNS2", { + id: "ABC456", + }); + expect(content).toEqual({ + xdm: { + identityMap: { + IDNS: [ + { + id: "ABC123", + }, + { + id: "ABC456", + }, + ], + IDNS2: [ + { + id: "ABC456", + }, + ], + }, + }, + }); + }); +}); diff --git a/vtest/unit/specs/utils/request/createDataCollectionRequest.spec.js b/vtest/unit/specs/utils/request/createDataCollectionRequest.spec.js new file mode 100644 index 000000000..a25e770cb --- /dev/null +++ b/vtest/unit/specs/utils/request/createDataCollectionRequest.spec.js @@ -0,0 +1,78 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import { createDataCollectionRequest } from "../../../../../src/utils/request/index.js"; +import describeRequest from "../../../helpers/describeRequest.js"; + +describe("createDataCollectionRequest", () => { + describeRequest(createDataCollectionRequest); + it("uses collect with sendBeacon if document may unload and identity is established", () => { + const payload = { + getDocumentMayUnload() { + return true; + }, + }; + const request = createDataCollectionRequest({ + payload, + }); + request.setIsIdentityEstablished(); + expect(request.getAction()).toBe("collect"); + expect(request.getUseSendBeacon()).toBe(true); + }); + it("uses interact without sendBeacon if document may unload but identity has not been established", () => { + const payload = { + getDocumentMayUnload() { + return true; + }, + }; + const request = createDataCollectionRequest({ + payload, + }); + expect(request.getAction()).toBe("interact"); + expect(request.getUseSendBeacon()).toBe(false); + }); + it("uses interact without sendBeacon if identity has been established but document will not unload", () => { + const payload = { + getDocumentMayUnload() { + return false; + }, + }; + const request = createDataCollectionRequest({ + payload, + }); + request.setIsIdentityEstablished(); + expect(request.getAction()).toBe("interact"); + expect(request.getUseSendBeacon()).toBe(false); + }); + it("uses interact without sendBeacon if document will not unload and identity has not been established", () => { + const payload = { + getDocumentMayUnload() { + return false; + }, + }; + const request = createDataCollectionRequest({ + payload, + }); + expect(request.getAction()).toBe("interact"); + expect(request.getUseSendBeacon()).toBe(false); + }); + it("passes the datastreamIdOverride to the request", () => { + const payload = {}; + const datastreamIdOverride = "my-edge-config-id-override"; + const request = createDataCollectionRequest({ + payload, + datastreamIdOverride, + }); + expect(request.getDatastreamIdOverride()).toBe(datastreamIdOverride); + }); +}); diff --git a/vtest/unit/specs/utils/request/createDataCollectionRequestPayload.spec.js b/vtest/unit/specs/utils/request/createDataCollectionRequestPayload.spec.js new file mode 100644 index 000000000..7627c6645 --- /dev/null +++ b/vtest/unit/specs/utils/request/createDataCollectionRequestPayload.spec.js @@ -0,0 +1,91 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import { createDataCollectionRequestPayload } from "../../../../../src/utils/request/index.js"; +import createEvent from "../../../../../src/core/createEvent.js"; +import describeRequestPayload from "../../../helpers/describeRequestPayload.js"; + +describe("createDataCollectionRequestPayload", () => { + describeRequestPayload(createDataCollectionRequestPayload); + it("adds an identity", () => { + const payload = createDataCollectionRequestPayload(); + payload.addIdentity("IDNS", { + id: "ABC123", + }); + payload.addIdentity("IDNS", { + id: "DEF456", + }); + expect(JSON.parse(JSON.stringify(payload))).toEqual({ + xdm: { + identityMap: { + IDNS: [ + { + id: "ABC123", + }, + { + id: "DEF456", + }, + ], + }, + }, + }); + }); + it("adds events and serializes them properly", () => { + const payload = createDataCollectionRequestPayload(); + payload.addEvent({ + xdm: { + a: "b", + }, + }); + payload.addEvent({ + xdm: { + c: "d", + }, + }); + expect(JSON.parse(JSON.stringify(payload))).toEqual({ + events: [ + { + xdm: { + a: "b", + }, + }, + { + xdm: { + c: "d", + }, + }, + ], + }); + }); + it("returns that document may unload if any event reports that it may unload", () => { + const payload = createDataCollectionRequestPayload(); + const event1 = createEvent(); + payload.addEvent(event1); + const event2 = createEvent(); + event2.documentMayUnload(); + payload.addEvent(event2); + expect(payload.getDocumentMayUnload()).toBe(true); + }); + it("returns that document will not unload if no event reports that it may unload", () => { + const payload = createDataCollectionRequestPayload(); + const event1 = createEvent(); + payload.addEvent(event1); + const event2 = createEvent(); + payload.addEvent(event2); + expect(payload.getDocumentMayUnload()).toBe(false); + }); + it("returns that document will not unload if the payload contains no events", () => { + const payload = createDataCollectionRequestPayload(); + expect(payload.getDocumentMayUnload()).toBe(false); + }); +}); diff --git a/vtest/unit/specs/utils/request/createGetAssuranceValidationTokenParams.spec.js b/vtest/unit/specs/utils/request/createGetAssuranceValidationTokenParams.spec.js new file mode 100644 index 000000000..eddf483e9 --- /dev/null +++ b/vtest/unit/specs/utils/request/createGetAssuranceValidationTokenParams.spec.js @@ -0,0 +1,62 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import { createGetAssuranceValidationTokenParams } from "../../../../../src/utils/request/index.js"; +import { injectStorage } from "../../../../../src/utils/index.js"; +import uuidV4Regex from "../../../constants/uuidV4Regex.js"; + +const win = { + location: { + search: "", + }, + localStorage: window.localStorage, +}; +describe("createGetAssuranceValidationTokenParams", () => { + it("gets validation token params", () => { + let result; + let token; + let firstClientId; + let clientId; + const getAssuranceValidationTokenParams = + createGetAssuranceValidationTokenParams({ + window: win, + createNamespacedStorage: injectStorage(win), + }); + expect(getAssuranceValidationTokenParams()).toEqual(""); + win.location.search = "?adb_validation_sessionid=abc-123"; + result = getAssuranceValidationTokenParams(); + // eslint-disable-next-line prefer-const + [token, firstClientId] = result.split("%7C"); + expect(token).toEqual("&adobeAepValidationToken=abc-123"); + expect(uuidV4Regex.test(firstClientId)).toBe(true); + expect( + win.localStorage.getItem("com.adobe.alloy.validation.clientId"), + ).toEqual(firstClientId); + win.location.search = "?adb_validation_sessionid=abc-123%20fgh"; + result = getAssuranceValidationTokenParams(); + [token, clientId] = result.split("%7C"); + expect(token).toEqual("&adobeAepValidationToken=abc-123%20fgh"); + expect(clientId).toEqual(firstClientId); + win.location.search = + "?lang=en&sort=relevancy&f:el_product=[Data%20Collection]&adb_validation_sessionid=abc-123"; + result = getAssuranceValidationTokenParams(); + [token, clientId] = result.split("%7C"); + expect(token).toEqual("&adobeAepValidationToken=abc-123"); + expect(clientId).toEqual(firstClientId); + win.location.search = + "?lang=en&sort=relevancy&f:el_product=[Data%20Collection]"; + expect(getAssuranceValidationTokenParams()).toEqual(""); + win.location.search = "?adb_validation_sessionid="; + expect(getAssuranceValidationTokenParams()).toEqual(""); + }); +}); diff --git a/vtest/unit/specs/utils/request/createHasIdentity.spec.js b/vtest/unit/specs/utils/request/createHasIdentity.spec.js new file mode 100644 index 000000000..2c4cbb590 --- /dev/null +++ b/vtest/unit/specs/utils/request/createHasIdentity.spec.js @@ -0,0 +1,42 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { beforeEach, describe, it, expect } from "vitest"; +import { + createHasIdentity, + createAddIdentity, +} from "../../../../../src/utils/request/index.js"; + +describe("createHasIdentity", () => { + let content; + let hasIdentity; + let addIdentity; + beforeEach(() => { + content = {}; + hasIdentity = createHasIdentity(content); + addIdentity = createAddIdentity(content); + }); + it("should return false when no xdm has been set", () => { + expect(hasIdentity("myid")).toBe(false); + }); + it("should return false if no identity has been set", () => { + content.xdm = {}; + expect(hasIdentity("myid")).toBe(false); + }); + it("should return false if there are other identities", () => { + addIdentity("other", "myotherid"); + expect(hasIdentity("myid")).toBe(false); + }); + it("should return true when there already is an identity", () => { + addIdentity("myid", "myidvalue"); + expect(hasIdentity("myid")).toBe(true); + }); +}); diff --git a/vtest/unit/specs/utils/request/createRequestParams.spec.js b/vtest/unit/specs/utils/request/createRequestParams.spec.js new file mode 100644 index 000000000..63fdba73a --- /dev/null +++ b/vtest/unit/specs/utils/request/createRequestParams.spec.js @@ -0,0 +1,65 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, beforeEach, describe, it, expect } from "vitest"; +import { createRequestParams } from "../../../../../src/utils/request/index.js"; + +describe("createRequestParams", () => { + let payload; + let globalConfigOverrides; + let localConfigOverrides; + beforeEach(() => { + payload = { + mergeConfigOverride: vi.fn(), + }; + }); + it("returns the payload and datastreamIdOverride", () => { + const result = createRequestParams({ + payload, + localConfigOverrides: { + datastreamId: "123", + }, + }); + expect(result).toEqual({ + payload, + datastreamIdOverride: "123", + }); + }); + it("works fine without overrides", () => { + const result = createRequestParams({ + payload, + }); + expect(result).toEqual({ + payload, + }); + }); + it("merges the global and local config overrides", () => { + globalConfigOverrides = { + a: "b", + c: "d", + }; + localConfigOverrides = { + a: "e", + }; + createRequestParams({ + payload, + globalConfigOverrides, + localConfigOverrides, + }); + expect(payload.mergeConfigOverride).toHaveBeenCalledWith({ + a: "b", + c: "d", + }); + expect(payload.mergeConfigOverride).toHaveBeenCalledWith({ + a: "e", + }); + }); +}); diff --git a/vtest/unit/specs/utils/sanitizeOrgIdForCookieName.spec.js b/vtest/unit/specs/utils/sanitizeOrgIdForCookieName.spec.js new file mode 100644 index 000000000..d766c0111 --- /dev/null +++ b/vtest/unit/specs/utils/sanitizeOrgIdForCookieName.spec.js @@ -0,0 +1,21 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import sanitizeOrgIdForCookieName from "../../../../src/utils/sanitizeOrgIdForCookieName.js"; + +describe("sanitizeOrgIdForCookieName", () => { + it("replaces @ with _", () => { + const result = sanitizeOrgIdForCookieName("ABC@CustomOrg"); + expect(result).toBe("ABC_CustomOrg"); + }); +}); diff --git a/vtest/unit/specs/utils/stackError.spec.js b/vtest/unit/specs/utils/stackError.spec.js new file mode 100644 index 000000000..fd99341ab --- /dev/null +++ b/vtest/unit/specs/utils/stackError.spec.js @@ -0,0 +1,39 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import stackError from "../../../../src/utils/stackError.js"; + +describe("stackError", () => { + it("stacks message onto error instance", () => { + const error = new Error("Conundrum encountered."); + const result = stackError({ + error, + message: "Predicament discovered.", + }); + expect(result).toEqual(expect.any(Error)); + expect(result.message).toBe( + "Predicament discovered.\nCaused by: Conundrum encountered.", + ); + }); + it("stacks message onto non-error instance", () => { + const error = "Conundrum encountered."; + const result = stackError({ + error, + message: "Predicament discovered.", + }); + expect(result).toEqual(expect.any(Error)); + expect(result.message).toBe( + "Predicament discovered.\nCaused by: Conundrum encountered.", + ); + }); +}); diff --git a/vtest/unit/specs/utils/stringToBoolean.spec.js b/vtest/unit/specs/utils/stringToBoolean.spec.js new file mode 100644 index 000000000..82fcb8e36 --- /dev/null +++ b/vtest/unit/specs/utils/stringToBoolean.spec.js @@ -0,0 +1,26 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { stringToBoolean } from "../../../../src/utils/index.js"; + +describe("stringToBoolean", () => { + ["true", "TRUE", "True"].forEach((str) => { + it(`parses '${str}' as true`, () => { + expect(stringToBoolean(str)).toBe(true); + }); + }); + ["false", "0", "foo", ""].forEach((str) => { + it(`parses '${str}' as false`, () => { + expect(stringToBoolean(str)).toBe(false); + }); + }); +}); diff --git a/vtest/unit/specs/utils/toArray.spec.js b/vtest/unit/specs/utils/toArray.spec.js new file mode 100644 index 000000000..b4108f6b0 --- /dev/null +++ b/vtest/unit/specs/utils/toArray.spec.js @@ -0,0 +1,33 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import toArray from "../../../../src/utils/toArray.js"; + +describe("toArray", () => { + it("does not convert value if already an array", () => { + const value = []; + expect(toArray(value)).toBe(value); + }); + it("converts undefined to empty array", () => { + expect(toArray()).toEqual([]); + }); + it("converts null to empty array", () => { + expect(toArray(null)).toEqual([]); + }); + it("converts array-like value to array", () => { + const result = toArray(document.querySelectorAll("body")); + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBe(1); + expect(result[0]).toBe(document.body); + }); +}); diff --git a/vtest/unit/specs/utils/toError.spec.js b/vtest/unit/specs/utils/toError.spec.js new file mode 100644 index 000000000..ceecc45b6 --- /dev/null +++ b/vtest/unit/specs/utils/toError.spec.js @@ -0,0 +1,28 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import toError from "../../../../src/utils/toError.js"; + +describe("toError", () => { + it("returns an error if value is not an error", () => { + const message = "Conundrum encountered."; + const result = toError(message); + expect(result).toEqual(expect.any(Error)); + expect(result.message).toBe("Conundrum encountered."); + }); + it("returns the value unmodified if value is an error", () => { + const error = new Error("Conundrum encountered."); + const result = toError(error); + expect(result).toBe(error); + }); +}); diff --git a/vtest/unit/specs/utils/toISOStringLocal.spec.js b/vtest/unit/specs/utils/toISOStringLocal.spec.js new file mode 100644 index 000000000..7597bb6ac --- /dev/null +++ b/vtest/unit/specs/utils/toISOStringLocal.spec.js @@ -0,0 +1,36 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { vi, describe, it, expect } from "vitest"; +import { toISOStringLocal } from "../../../../src/utils/index.js"; + +describe("toISOStringLocal", () => { + it("handles a date in Utah", () => { + const date = new Date("August 9, 2019 10:59:42"); + vi.spyOn(date, "getTimezoneOffset").mockReturnValue(7 * 60); + expect(toISOStringLocal(date)).toEqual("2019-08-09T10:59:42.000-07:00"); + }); + it("handles a date in india", () => { + const date = new Date("December 31, 2019 22:36:00"); + vi.spyOn(date, "getTimezoneOffset").mockReturnValue(-5 * 60 - 30); + expect(toISOStringLocal(date)).toEqual("2019-12-31T22:36:00.000+05:30"); + }); + it("handles a weird offset", () => { + const date = new Date("January 01, 2020 00:00:42"); + vi.spyOn(date, "getTimezoneOffset").mockReturnValue(-176); + expect(toISOStringLocal(date)).toEqual("2020-01-01T00:00:42.000+02:56"); + }); + it("handles a UTC timezone", () => { + const date = new Date("December 31, 2019 22:36:00"); + vi.spyOn(date, "getTimezoneOffset").mockReturnValue(0); + expect(toISOStringLocal(date)).toEqual("2019-12-31T22:36:00.000+00:00"); + }); +}); diff --git a/vtest/unit/specs/utils/toInteger.spec.js b/vtest/unit/specs/utils/toInteger.spec.js new file mode 100644 index 000000000..575c5ff25 --- /dev/null +++ b/vtest/unit/specs/utils/toInteger.spec.js @@ -0,0 +1,42 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import toInteger from "../../../../src/utils/toInteger.js"; + +describe("toInteger", () => { + [ + ["0", 0], + ["1", 1], + ["true", undefined], + ["1.1", 1], + ["-4", -4], + ["123abc", undefined], + [-42, -42], + [3.14, 3], + [3.99, 4], + [null, undefined], + [undefined, undefined], + [{}, undefined], + [true, undefined], + [false, undefined], + [() => 42, undefined], + ["1234.5", 1235], + ].forEach(([input, output]) => { + it(`converts "${input}" to ${output}`, () => { + expect(toInteger(input)).toEqual(output); + }); + }); + it("uses the passed value for the default", () => { + expect(toInteger("foo", 0)).toEqual(0); + }); +}); diff --git a/vtest/unit/specs/utils/updateErrorMessage.spec.js b/vtest/unit/specs/utils/updateErrorMessage.spec.js new file mode 100644 index 000000000..1a7161b4a --- /dev/null +++ b/vtest/unit/specs/utils/updateErrorMessage.spec.js @@ -0,0 +1,41 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe, it, expect } from "vitest"; +import updateErrorMessage from "../../../../src/utils/updateErrorMessage.js"; + +describe("updateErrorMessage", () => { + it("updates error message if the message property is writeable", () => { + const error = new Error("Conundrum encountered."); + const message = "Predicament discovered."; + updateErrorMessage({ + error, + message, + }); + expect(error.message).toEqual("Predicament discovered."); + }); + it("does not update error message if the message property is read-only", () => { + let error; + try { + // This will cause a DOMException, which has a read-only message property. + const invalidSelector = "div:foo"; + document.querySelectorAll(invalidSelector); + } catch (e) { + error = e; + } + updateErrorMessage({ + error, + message: "Predicament discovered.", + }); + expect(error.message).not.toContain("Predicament discovered."); + }); +}); diff --git a/vtest/unit/specs/utils/validateConfigOverride.spec.js b/vtest/unit/specs/utils/validateConfigOverride.spec.js new file mode 100644 index 000000000..952a44331 --- /dev/null +++ b/vtest/unit/specs/utils/validateConfigOverride.spec.js @@ -0,0 +1,85 @@ +/* +Copyright 2022 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import validateConfigOverride from "../../../../src/utils/validateConfigOverride.js"; +import describeValidation from "../../helpers/describeValidation.js"; + +describeValidation("utils:validateConfigOverride", validateConfigOverride, [ + // empty configuration + { + value: {}, + }, + // standard configuration + { + value: { + experience_platform: { + datasets: { + event: "werewr", + profile: "www", + }, + }, + analytics: { + reportSuites: ["sdfsfd"], + }, + identity: { + idSyncContainerId: "rrr", + }, + target: { + propertyToken: "rrr", + }, + }, + }, + // arbitrarily nested objects + { + value: { + experience_platform: { + datasets: { + event: { + morning: { + first: { + withoutAction: "222", + }, + }, + }, + }, + }, + }, + }, + // non-object top level keys are valid + { + value: { + foo: "bar", + biz: {}, + }, + }, + // value must be an object + { + value: true, + error: true, + }, + { + value: false, + error: true, + }, + { + value: "", + error: true, + }, + { + value: [], + error: true, + }, + { + value: 123, + error: true, + }, +]); diff --git a/vtest/unit/specs/utils/validateIdentityMap.spec.js b/vtest/unit/specs/utils/validateIdentityMap.spec.js new file mode 100644 index 000000000..f5a118a22 --- /dev/null +++ b/vtest/unit/specs/utils/validateIdentityMap.spec.js @@ -0,0 +1,162 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { validateIdentityMap } from "../../../../src/utils/index.js"; +import describeValidation from "../../helpers/describeValidation.js"; + +describeValidation("utils:validateIdentityMap", validateIdentityMap, [ + { + value: { + a: [ + { + authenticatedState: "unknown", + }, + ], + }, + error: true, + }, + { + value: { + a: [ + { + authenticatedState: "authenticated", + }, + ], + }, + }, + { + value: { + a: [ + { + id: 123, + }, + ], + }, + error: true, + }, + { + value: { + a: [ + { + id: "123", + }, + ], + }, + }, + { + value: { + a: [ + { + namespace: { + unknown: "field", + }, + }, + ], + }, + error: true, + }, + { + value: { + a: [ + { + namespace: { + code: "123", + }, + }, + ], + }, + }, + { + value: { + a: [ + { + primary: 1, + }, + ], + }, + error: true, + }, + { + value: { + a: [ + { + primary: true, + }, + ], + }, + }, + { + value: { + a: [ + { + xid: 123, + }, + ], + }, + error: true, + }, + { + value: { + a: [ + { + xid: "123", + }, + ], + }, + }, + { + value: { + a: [ + { + unknown: "field", + }, + ], + }, + error: true, + }, + { + value: null, + }, + { + value: undefined, + }, + { + value: [], + error: true, + }, + { + value: { + a: [], + }, + }, + { + value: { + a: null, + }, + error: true, + }, + { + value: { + a: undefined, + }, + error: true, + }, + { + value: { + a: "string", + }, + error: true, + }, + { + value: {}, + }, +]); diff --git a/vtest/unit/specs/utils/validation/anythingValidator.spec.js b/vtest/unit/specs/utils/validation/anythingValidator.spec.js new file mode 100644 index 000000000..b510a4a42 --- /dev/null +++ b/vtest/unit/specs/utils/validation/anythingValidator.spec.js @@ -0,0 +1,109 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { anything } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::anything", () => { + describeValidation("optional anything", anything(), [ + { + value: {}, + }, + { + value: { + a: 1, + }, + }, + { + value: [], + }, + { + value: ["hello"], + }, + { + value: 1, + }, + { + value: true, + }, + { + value: undefined, + }, + { + value: null, + }, + { + value: () => undefined, + }, + ]); + describeValidation("required anything", anything().required(), [ + { + value: {}, + }, + { + value: { + a: 1, + }, + }, + { + value: [], + }, + { + value: ["hello"], + }, + { + value: 1, + }, + { + value: true, + }, + { + value: undefined, + error: true, + }, + { + value: null, + error: true, + }, + ]); + describeValidation("default anything", anything().default("foo"), [ + { + value: {}, + }, + { + value: { + a: 1, + }, + }, + { + value: [], + }, + { + value: ["hello"], + }, + { + value: 1, + }, + { + value: true, + }, + { + value: undefined, + expected: "foo", + }, + { + value: null, + expected: "foo", + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/booleanValidator.spec.js b/vtest/unit/specs/utils/validation/booleanValidator.spec.js new file mode 100644 index 000000000..5dba11061 --- /dev/null +++ b/vtest/unit/specs/utils/validation/booleanValidator.spec.js @@ -0,0 +1,88 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { boolean } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::boolean", () => { + describeValidation("optional boolean", boolean(), [ + { + value: "", + error: true, + }, + { + value: "true", + error: true, + }, + { + value: [1], + error: true, + }, + { + value: {}, + error: true, + }, + { + value: 0, + error: true, + }, + { + value: 42, + error: true, + }, + { + value: true, + }, + { + value: false, + }, + { + value: null, + }, + { + value: undefined, + }, + ]); + describeValidation("required boolean", boolean().required(), [ + { + value: true, + }, + { + value: false, + }, + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + ]); + describeValidation("default true boolean", boolean().default(true), [ + { + value: null, + expected: true, + }, + { + value: undefined, + expected: true, + }, + { + value: true, + }, + { + value: false, + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/callbackValidator.spec.js b/vtest/unit/specs/utils/validation/callbackValidator.spec.js new file mode 100644 index 000000000..3ec054d2e --- /dev/null +++ b/vtest/unit/specs/utils/validation/callbackValidator.spec.js @@ -0,0 +1,75 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { callback } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::callback", () => { + describeValidation("optional callback", callback(), [ + { + value: "", + error: true, + }, + { + value: "true", + error: true, + }, + { + value: [1], + error: true, + }, + { + value: {}, + error: true, + }, + { + value: 0, + error: true, + }, + { + value: () => undefined, + }, + { + value: function func() {}, + }, + ]); + describeValidation("required callback", callback().required(), [ + { + value: () => undefined, + }, + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + ]); + const func1 = () => {}; + const func2 = () => {}; + describeValidation("callback with default value", callback().default(func1), [ + { + value: null, + expected: func1, + }, + { + value: undefined, + expected: func1, + }, + { + value: func2, + expected: func2, + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/createAnyOfValidator.spec.js b/vtest/unit/specs/utils/validation/createAnyOfValidator.spec.js new file mode 100644 index 000000000..e012bd1d4 --- /dev/null +++ b/vtest/unit/specs/utils/validation/createAnyOfValidator.spec.js @@ -0,0 +1,112 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { + anyOf, + objectOf, + boolean, + arrayOf, + string, + literal, +} from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation:anyOf", () => { + describeValidation( + "require one or the other", + anyOf( + [ + objectOf({ + renderDecisions: literal(true).required(), + decisionScopes: arrayOf(string()), + }).required(), + objectOf({ + renderDecisions: boolean(), + decisionScopes: arrayOf(string()).nonEmpty().required(), + }).required(), + ], + "either renderDecisions set to true or decisionScopes set to a nonEmpty array", + ), + [ + { + value: undefined, + error: true, + }, + { + value: null, + error: true, + }, + { + value: {}, + error: true, + }, + { + value: { + renderDecisions: true, + }, + error: false, + }, + { + value: { + renderDecisions: false, + }, + error: true, + }, + { + value: { + renderDecisions: "foo", + }, + error: true, + }, + { + value: { + decisionScopes: [], + }, + error: true, + }, + { + value: { + decisionScopes: ["a"], + }, + error: false, + }, + { + value: { + decisionScopes: "bar", + }, + error: true, + }, + { + value: { + renderDecisions: true, + decisionScopes: ["a", "b"], + }, + error: false, + }, + { + value: { + renderDecisions: true, + decisionScopes: "foo", + }, + error: true, + }, + { + value: { + renderDecisions: "foo", + decisionScopes: ["a"], + }, + error: true, + }, + ], + ); +}); diff --git a/vtest/unit/specs/utils/validation/createArrayOfValidator.spec.js b/vtest/unit/specs/utils/validation/createArrayOfValidator.spec.js new file mode 100644 index 000000000..64ea3bbd4 --- /dev/null +++ b/vtest/unit/specs/utils/validation/createArrayOfValidator.spec.js @@ -0,0 +1,78 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { arrayOf, string } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::arrayOf", () => { + describeValidation( + "optional array with required values", + arrayOf(string().required()), + [ + { + value: ["foo", undefined], + error: true, + }, + { + value: [true, "bar"], + error: true, + }, + { + value: "non-array", + error: true, + }, + { + value: ["foo"], + }, + { + value: ["foo", "bar"], + }, + { + value: [], + }, + { + value: null, + }, + { + value: undefined, + }, + ], + ); + describeValidation( + "optional array with optional values", + arrayOf(string().default("hello")), + [ + { + value: ["a", null, undefined, "b"], + expected: ["a", "hello", "hello", "b"], + }, + ], + ); + describeValidation( + "required array with optional values", + arrayOf(string()).required(), + [ + { + value: [null], + }, + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + ], + ); +}); diff --git a/vtest/unit/specs/utils/validation/createDefaultValidator.spec.js b/vtest/unit/specs/utils/validation/createDefaultValidator.spec.js new file mode 100644 index 000000000..adb1e2ed4 --- /dev/null +++ b/vtest/unit/specs/utils/validation/createDefaultValidator.spec.js @@ -0,0 +1,34 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { string } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::default", () => { + describeValidation("default string", string().default("my default"), [ + { + value: null, + expected: "my default", + }, + { + value: undefined, + expected: "my default", + }, + { + value: "", + }, + { + value: "hello", + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/createDeprecatedValidator.spec.js b/vtest/unit/specs/utils/validation/createDeprecatedValidator.spec.js new file mode 100644 index 000000000..d3d2c5896 --- /dev/null +++ b/vtest/unit/specs/utils/validation/createDeprecatedValidator.spec.js @@ -0,0 +1,122 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { + objectOf, + callback, + string, + boolean, +} from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::deprecated", () => { + describeValidation( + "works for a string field", + objectOf({ + old: string().deprecated(), + new: string(), + }), + [ + { + value: { + old: "a", + }, + expected: { + old: "a", + }, + warning: true, + }, + { + value: {}, + expected: {}, + warning: false, + }, + { + value: { + new: "b", + }, + expected: { + new: "b", + }, + warning: false, + }, + ], + ); + describeValidation( + "works for a boolean field", + objectOf({ + old: boolean().deprecated(), + new: boolean(), + }), + [ + { + value: { + old: true, + }, + expected: { + old: true, + }, + warning: true, + }, + { + value: {}, + expected: {}, + warning: false, + }, + { + value: { + new: false, + }, + expected: { + new: false, + }, + warning: false, + }, + ], + ); + const noop = () => undefined; + describeValidation( + "works for a callback field", + objectOf({ + old: callback().deprecated(), + new: callback(), + }), + [ + { + value: { + old: noop, + new: noop, + }, + expected: { + old: noop, + new: noop, + }, + warning: true, + }, + { + value: {}, + expected: {}, + warning: false, + }, + { + value: { + new: noop, + }, + expected: { + new: noop, + }, + warning: false, + }, + ], + ); +}); diff --git a/vtest/unit/specs/utils/validation/createLiteralValidator.spec.js b/vtest/unit/specs/utils/validation/createLiteralValidator.spec.js new file mode 100644 index 000000000..c456d858a --- /dev/null +++ b/vtest/unit/specs/utils/validation/createLiteralValidator.spec.js @@ -0,0 +1,62 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { literal } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation:literal", () => { + describeValidation("literal optional string", literal("hello"), [ + { + value: undefined, + error: false, + }, + { + value: null, + error: false, + }, + { + value: "hello", + error: false, + }, + { + value: {}, + error: true, + }, + { + value: "", + error: true, + }, + { + value: "goodbye", + error: true, + }, + ]); + describeValidation("literal required integer", literal(42).required(), [ + { + value: 42, + error: false, + }, + { + value: 41, + error: true, + }, + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/createMapOfValuesValidator.spec.js b/vtest/unit/specs/utils/validation/createMapOfValuesValidator.spec.js new file mode 100644 index 000000000..75601c39a --- /dev/null +++ b/vtest/unit/specs/utils/validation/createMapOfValuesValidator.spec.js @@ -0,0 +1,99 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { + mapOfValues, + arrayOf, + anything, + string, +} from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::mapOfValues", () => { + describeValidation( + "map of required strings", + mapOfValues(string().required()).required(), + [ + { + value: {}, + }, + { + value: { + a: "1", + }, + }, + { + value: { + a: "1", + b: "2", + c: "3", + }, + }, + { + value: undefined, + error: true, + }, + { + value: null, + error: true, + }, + { + value: { + a: 123, + }, + error: true, + }, + { + value: 123, + error: true, + }, + { + value: { + a: undefined, + }, + error: true, + }, + ], + ); + describeValidation("map of arrays", mapOfValues(arrayOf(anything())), [ + { + value: { + a: [], + b: [true, 1, 0.1, "string", undefined, null], + }, + }, + { + value: { + a: "string", + }, + error: true, + }, + { + value: { + a: undefined, + }, + expected: {}, + }, + { + value: { + a: null, + }, + }, + { + value: undefined, + }, + { + value: null, + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/createMaximumValidator.spec.js b/vtest/unit/specs/utils/validation/createMaximumValidator.spec.js new file mode 100644 index 000000000..78bf704c1 --- /dev/null +++ b/vtest/unit/specs/utils/validation/createMaximumValidator.spec.js @@ -0,0 +1,72 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { number } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::maximum", () => { + describeValidation("optional maximum", number().integer().maximum(4), [ + { + value: 3, + }, + { + value: 5, + error: true, + }, + { + value: null, + }, + { + value: undefined, + }, + ]); + describeValidation( + "required maximum", + number().integer().maximum(2).required(), + [ + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + { + value: 3, + error: true, + }, + { + value: 1, + }, + ], + ); + describeValidation( + "default maximum", + number().integer().maximum(10).default(8), + [ + { + value: null, + expected: 8, + }, + { + value: undefined, + expected: 8, + }, + { + value: 11, + error: true, + }, + ], + ); +}); diff --git a/vtest/unit/specs/utils/validation/createMinimumValidator.spec.js b/vtest/unit/specs/utils/validation/createMinimumValidator.spec.js new file mode 100644 index 000000000..09614fc7c --- /dev/null +++ b/vtest/unit/specs/utils/validation/createMinimumValidator.spec.js @@ -0,0 +1,75 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { number } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::minimum", () => { + describeValidation("optional minimum", number().integer().minimum(4), [ + { + value: 3, + error: true, + }, + { + value: 4, + }, + { + value: 5, + }, + { + value: null, + }, + { + value: undefined, + }, + ]); + describeValidation( + "required minimum", + number().integer().minimum(1).required(), + [ + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + { + value: 0, + error: true, + }, + { + value: 1, + }, + ], + ); + describeValidation( + "default minimum", + number().integer().minimum(10).default(42), + [ + { + value: null, + expected: 42, + }, + { + value: undefined, + expected: 42, + }, + { + value: 0, + error: true, + }, + ], + ); +}); diff --git a/vtest/unit/specs/utils/validation/createNoUnknownFieldsValidator.spec.js b/vtest/unit/specs/utils/validation/createNoUnknownFieldsValidator.spec.js new file mode 100644 index 000000000..639a10fb2 --- /dev/null +++ b/vtest/unit/specs/utils/validation/createNoUnknownFieldsValidator.spec.js @@ -0,0 +1,113 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { objectOf, string } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::noUnknownFields", () => { + describeValidation( + "optional", + objectOf({ + a: string(), + }).noUnknownFields(), + [ + { + value: { + b: "world", + }, + error: true, + }, + { + value: { + a: "hello", + b: "world", + }, + error: true, + }, + { + value: { + a: "hello", + }, + error: false, + }, + { + value: {}, + error: false, + }, + ], + ); + describeValidation( + "required", + objectOf({ + a: string().required(), + }) + .noUnknownFields() + .required(), + [ + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + { + value: {}, + error: true, + }, + { + value: { + a: "Hello", + }, + error: false, + }, + ], + ); + describeValidation( + "default", + objectOf({ + a: string().default("hello"), + }) + .noUnknownFields() + .default({ + a: "world", + }), + [ + { + value: null, + expected: { + a: "world", + }, + }, + { + value: undefined, + expected: { + a: "world", + }, + }, + { + value: {}, + expected: { + a: "hello", + }, + }, + { + value: { + b: "goodbye", + }, + error: true, + }, + ], + ); +}); diff --git a/vtest/unit/specs/utils/validation/createNonEmptyValidator.spec.js b/vtest/unit/specs/utils/validation/createNonEmptyValidator.spec.js new file mode 100644 index 000000000..2bebf0c37 --- /dev/null +++ b/vtest/unit/specs/utils/validation/createNonEmptyValidator.spec.js @@ -0,0 +1,63 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { string } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::nonEmpty", () => { + describeValidation("optional nonEmpty", string().nonEmpty(), [ + { + value: "key", + }, + { + value: "", + error: true, + }, + { + value: null, + }, + { + value: undefined, + }, + ]); + describeValidation("required nonEmpty", string().nonEmpty().required(), [ + { + value: "abc", + }, + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + ]); + describeValidation( + "default nonEmpty", + string().nonEmpty().default("mydefault"), + [ + { + value: null, + expected: "mydefault", + }, + { + value: undefined, + expected: "mydefault", + }, + { + value: "abc", + }, + ], + ); +}); diff --git a/vtest/unit/specs/utils/validation/createObjectOfValidator.spec.js b/vtest/unit/specs/utils/validation/createObjectOfValidator.spec.js new file mode 100644 index 000000000..7afb12459 --- /dev/null +++ b/vtest/unit/specs/utils/validation/createObjectOfValidator.spec.js @@ -0,0 +1,145 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { objectOf, string } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::objectOf", () => { + describeValidation( + "optional object with various values", + objectOf({ + a: string().required(), + b: string().default("b default"), + c: string(), + }), + [ + { + value: {}, + error: true, + }, + { + value: { + a: "1", + }, + expected: { + a: "1", + b: "b default", + }, + }, + { + value: { + a: "1", + b: "2", + c: "3", + }, + }, + { + value: undefined, + }, + { + value: null, + }, + { + value: { + a: 123, + }, + error: true, + }, + { + value: 123, + error: true, + }, + ], + ); + describeValidation( + "nested object", + objectOf({ + a: objectOf({ + aa: string().required(), + }).required(), + }), + [ + { + value: {}, + error: true, + }, + { + value: { + a: {}, + }, + error: true, + }, + { + value: { + a: { + aa: "11", + }, + }, + }, + ], + ); + describeValidation( + "concat", + objectOf({ + a: string().required(), + }) + .concat( + objectOf({ + b: string().default("b default"), + }), + ) + .concat( + objectOf({ + c: string(), + }), + ), + [ + { + value: {}, + error: true, + }, + { + value: { + a: "1", + }, + expected: { + a: "1", + b: "b default", + }, + }, + { + value: { + a: "1", + b: "2", + c: "3", + }, + }, + { + value: undefined, + }, + { + value: null, + }, + { + value: { + a: 123, + }, + error: true, + }, + { + value: 123, + error: true, + }, + ], + ); +}); diff --git a/vtest/unit/specs/utils/validation/createRenamedValidator.spec.js b/vtest/unit/specs/utils/validation/createRenamedValidator.spec.js new file mode 100644 index 000000000..f506217a9 --- /dev/null +++ b/vtest/unit/specs/utils/validation/createRenamedValidator.spec.js @@ -0,0 +1,91 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { objectOf, string } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::renamed", () => { + const testCases = [ + { + value: { + old: "a", + new: "a", + }, + expected: { + new: "a", + }, + warning: true, + }, + { + value: { + old: "a", + }, + expected: { + new: "a", + }, + warning: true, + }, + { + value: { + new: "a", + }, + }, + { + value: { + old: "a", + new: "b", + }, + error: true, + }, + { + value: "foo", + error: true, + }, + { + value: 1, + error: true, + }, + { + value: undefined, + }, + ]; + describeValidation( + "works for a single deprecated field", + objectOf({ + new: string().required(), + }).renamed("old", string(), "new"), + testCases, + ); + describeValidation( + "works for multiple deprecated fields", + objectOf({ + new1: string().required(), + new2: string().required(), + }) + .renamed("old1", string(), "new1") + .renamed("old2", string(), "new2"), + [ + { + value: { + old1: "a", + old2: "b", + }, + expected: { + new1: "a", + new2: "b", + }, + warning: true, + }, + ], + ); +}); diff --git a/vtest/unit/specs/utils/validation/createUniqueItemsValidator.spec.js b/vtest/unit/specs/utils/validation/createUniqueItemsValidator.spec.js new file mode 100644 index 000000000..624914d12 --- /dev/null +++ b/vtest/unit/specs/utils/validation/createUniqueItemsValidator.spec.js @@ -0,0 +1,48 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { + string, + arrayOf, + number, +} from "../../../../../src/utils/validation/index.js"; + +describe("validation::createUniqueItems", () => { + it(`validates an empty array`, () => { + const validator = arrayOf(string()).uniqueItems(); + expect(validator([])).toEqual([]); + }); + it(`validates an array of one item`, () => { + const validator = arrayOf(string()).uniqueItems(); + expect(validator(["a"])).toEqual(["a"]); + }); + it(`throws an error on an array with duplicate (string) items`, () => { + const validator = arrayOf(string()).uniqueItems(); + expect(() => validator(["a", "b", "a", "e"])).toThrowError(); + }); + it(`throws an error on an array with duplicate integers`, () => { + const validator = arrayOf(number()).uniqueItems(); + expect(() => validator([1, 2, 3, 4, 4, 5])).toThrowError(); + }); + it(`validates an array of enums`, () => { + const validator = arrayOf(number()).uniqueItems(); + expect(validator([])).toEqual([]); + }); + it(`validates an array of null or undefined`, () => { + const validator = arrayOf(string()).uniqueItems(); + expect(validator([null, undefined])).toEqual([null, undefined]); + }); + it(`complains about required when null or undefined`, () => { + const validator = arrayOf(string().required()).uniqueItems(); + expect(() => validator([null, undefined])).toThrowError(); + }); +}); diff --git a/vtest/unit/specs/utils/validation/createUniqueValidator.spec.js b/vtest/unit/specs/utils/validation/createUniqueValidator.spec.js new file mode 100644 index 000000000..5b8a6dc6d --- /dev/null +++ b/vtest/unit/specs/utils/validation/createUniqueValidator.spec.js @@ -0,0 +1,46 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe, it, expect } from "vitest"; +import { string } from "../../../../../src/utils/validation/index.js"; + +describe("validation::createUnique", () => { + [["a"], ["a", "b", "c"]].forEach((values) => { + it(`should accept ${JSON.stringify(values)}`, () => { + const validator = string().unique(); + values.forEach((value) => { + expect(validator(value, "mykey")).toEqual(value); + }); + }); + }); + [ + ["a", "a"], + ["a", "b", "a"], + ["a", "b", "b"], + ].forEach((values) => { + it(`should reject ${JSON.stringify(values)}`, () => { + const validator = string().unique(); + values.forEach((value, i) => { + if (i + 1 === values.length) { + expect(() => validator(value, "mykey")).toThrowError(); + } else { + expect(validator(value, "mykey")).toEqual(value); + } + }); + }); + }); + [null, undefined].forEach((value) => { + it(`complains about required when ${JSON.stringify(value)}`, () => { + const validator = string().unique().required(); + expect(() => validator(value, "key")).toThrowError(); + }); + }); +}); diff --git a/vtest/unit/specs/utils/validation/domainValidator.spec.js b/vtest/unit/specs/utils/validation/domainValidator.spec.js new file mode 100644 index 000000000..26fc45d47 --- /dev/null +++ b/vtest/unit/specs/utils/validation/domainValidator.spec.js @@ -0,0 +1,38 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { string } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::domain", () => { + describeValidation("domain", string().domain(), [ + { + value: "stats.adobe.com", + }, + { + value: "stats-edge.adobe.com", + }, + { + value: "https://stats.adobe.com", + error: true, + }, + { + value: "stats.adobe.com\n", + error: true, + }, + { + value: "stats.adobe.com\nbad", + error: true, + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/enumOfValidator.spec.js b/vtest/unit/specs/utils/validation/enumOfValidator.spec.js new file mode 100644 index 000000000..09bac966d --- /dev/null +++ b/vtest/unit/specs/utils/validation/enumOfValidator.spec.js @@ -0,0 +1,102 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { enumOf } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation:enumOf", () => { + describeValidation("optional enum", enumOf("in", 1234, 0.1, false), [ + { + value: undefined, + error: false, + }, + { + value: 1234, + error: false, + }, + { + value: "in", + error: false, + }, + { + value: null, + error: false, + }, + { + value: 0.1, + error: false, + }, + { + value: false, + error: false, + }, + { + value: "out", + error: true, + }, + { + value: "", + error: true, + }, + { + value: {}, + error: true, + }, + { + value: [], + error: true, + }, + ]); + describeValidation("required enum", enumOf("in", "pending").required(), [ + { + value: "in", + error: false, + }, + { + value: "pending", + error: false, + }, + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + { + value: 0.1, + error: true, + }, + { + value: false, + error: true, + }, + { + value: "out", + error: true, + }, + { + value: "", + error: true, + }, + { + value: {}, + error: true, + }, + { + value: [], + error: true, + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/integerValidator.spec.js b/vtest/unit/specs/utils/validation/integerValidator.spec.js new file mode 100644 index 000000000..d09320af3 --- /dev/null +++ b/vtest/unit/specs/utils/validation/integerValidator.spec.js @@ -0,0 +1,68 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { number } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::integer()", () => { + describeValidation("optional integer", number().integer(), [ + { + value: 42.01, + error: true, + }, + { + value: -1.1, + error: true, + }, + { + value: NaN, + error: true, + }, + { + value: 0, + }, + { + value: 42, + }, + { + value: -1, + }, + ]); + describeValidation("required integer", number().integer().required(), [ + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + { + value: 10, + }, + ]); + describeValidation("default integer", number().integer().default(12345), [ + { + value: null, + expected: 12345, + }, + { + value: undefined, + expected: 12345, + }, + { + value: 10, + expected: 10, + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/matchesRegexpValidator.spec.js b/vtest/unit/specs/utils/validation/matchesRegexpValidator.spec.js new file mode 100644 index 000000000..6d4857e39 --- /dev/null +++ b/vtest/unit/specs/utils/validation/matchesRegexpValidator.spec.js @@ -0,0 +1,67 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { string } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +const regexp = /^[A-z]+$/; +describe("validation::matchesRegexp", () => { + describeValidation("optional matchesRegexp", string().matches(regexp), [ + { + value: "abc", + }, + { + value: "ABCD", + }, + { + value: "*", + error: true, + }, + { + value: "123", + error: true, + }, + { + value: null, + }, + { + value: undefined, + }, + ]); + describeValidation("required regexp", string().regexp(regexp).required(), [ + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + { + value: "", + }, + ]); + describeValidation("default regexp", string().regexp(regexp).default("abc"), [ + { + value: null, + expected: "abc", + }, + { + value: undefined, + expected: "abc", + }, + { + value: "a", + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/numberValidator.spec.js b/vtest/unit/specs/utils/validation/numberValidator.spec.js new file mode 100644 index 000000000..9ee87928a --- /dev/null +++ b/vtest/unit/specs/utils/validation/numberValidator.spec.js @@ -0,0 +1,79 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { number } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::number", () => { + describeValidation("optional number", number(), [ + { + value: true, + error: true, + }, + { + value: "", + error: true, + }, + { + value: "42", + error: true, + }, + { + value: [1], + error: true, + }, + { + value: {}, + error: true, + }, + { + value: NaN, + error: true, + }, + { + value: 0, + }, + { + value: 0.01, + }, + { + value: Infinity, + }, + ]); + describeValidation("required number", number().required(), [ + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + { + value: 123, + }, + ]); + describeValidation("default number", number().default(-1), [ + { + value: null, + expected: -1, + }, + { + value: undefined, + expected: -1, + }, + { + value: 123, + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/regexpValidator.spec.js b/vtest/unit/specs/utils/validation/regexpValidator.spec.js new file mode 100644 index 000000000..f3c88f9e3 --- /dev/null +++ b/vtest/unit/specs/utils/validation/regexpValidator.spec.js @@ -0,0 +1,69 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { string } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::regexp", () => { + describeValidation("optional regexp", string().regexp(), [ + { + value: "steel|bronze", + }, + { + value: "/a/", + }, + { + value: "/^[a-z0-9+]:///i", + }, + { + value: "[", + error: true, + }, + { + value: "*", + error: true, + }, + { + value: null, + }, + { + value: undefined, + }, + ]); + describeValidation("required regexp", string().regexp().required(), [ + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + { + value: "", + }, + ]); + describeValidation("default regexp", string().regexp().default("/default/"), [ + { + value: null, + expected: "/default/", + }, + { + value: undefined, + expected: "/default/", + }, + { + value: "a", + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/requiredValidator.spec.js b/vtest/unit/specs/utils/validation/requiredValidator.spec.js new file mode 100644 index 000000000..668e29676 --- /dev/null +++ b/vtest/unit/specs/utils/validation/requiredValidator.spec.js @@ -0,0 +1,34 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { describe } from "vitest"; +import { string } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::required", () => { + describeValidation("required string", string().required(), [ + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + { + value: "", + }, + { + value: "hello", + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/stringValidator.spec.js b/vtest/unit/specs/utils/validation/stringValidator.spec.js new file mode 100644 index 000000000..f0d358514 --- /dev/null +++ b/vtest/unit/specs/utils/validation/stringValidator.spec.js @@ -0,0 +1,70 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { describe } from "vitest"; +import { string } from "../../../../../src/utils/validation/index.js"; +import describeValidation from "../../../helpers/describeValidation.js"; + +describe("validation::string", () => { + describeValidation("optional string", string(), [ + { + value: false, + error: true, + }, + { + value: 0, + error: true, + }, + { + value: [], + error: true, + }, + { + value: () => {}, + error: true, + }, + { + value: null, + }, + { + value: undefined, + }, + ]); + describeValidation("required string", string().required(), [ + { + value: null, + error: true, + }, + { + value: undefined, + error: true, + }, + { + value: "", + }, + { + value: "hello", + }, + ]); + describeValidation("default string", string().default("default"), [ + { + value: null, + expected: "default", + }, + { + value: undefined, + expected: "default", + }, + { + value: "hello", + }, + ]); +}); diff --git a/vtest/unit/specs/utils/validation/utils.spec.js b/vtest/unit/specs/utils/validation/utils.spec.js new file mode 100644 index 000000000..0ddb45bfe --- /dev/null +++ b/vtest/unit/specs/utils/validation/utils.spec.js @@ -0,0 +1,83 @@ +/* +Copyright 2019 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { vi, describe, it, expect } from "vitest"; +import { + chain, + nullSafeChain, + assertValid, +} from "../../../../../src/utils/validation/utils.js"; + +describe("validation::utils", () => { + describe("chain", () => { + it("calls the validators with the correct params", () => { + const validator1 = vi.fn(); + const validator2 = vi.fn(); + const validator3 = vi.fn(); + validator1.mockReturnValue("validator1return"); + validator2.mockReturnValue("validator2return"); + validator3.mockReturnValue("validator3return"); + const subject = chain(chain(validator1, validator2), validator3); + expect(subject("myCurrentValue", "myKey")).toEqual("validator3return"); + expect(validator1).toHaveBeenCalledTimes(1); + expect(validator1).toHaveBeenCalledWith("myCurrentValue", "myKey"); + expect(validator2).toHaveBeenCalledTimes(1); + expect(validator2).toHaveBeenCalledWith("validator1return", "myKey"); + expect(validator3).toHaveBeenCalledTimes(1); + expect(validator3).toHaveBeenCalledWith("validator2return", "myKey"); + }); + it("short circuits evaluation", () => { + const validator1 = vi.fn(); + const validator2 = vi.fn(); + const validator3 = vi.fn(); + validator1.mockReturnValue("validator1return"); + validator2.mockImplementation(() => { + throw new Error("My Error!"); + }); + validator3.mockReturnValue("validator3return"); + const subject = chain(chain(validator1, validator2), validator3); + expect(() => subject("myCurrentValue", "myKey")).toThrow( + Error("My Error!"), + ); + expect(validator3).not.toHaveBeenCalled(); + }); + }); + describe("nullSafeChain", () => { + it("doesn't call the underlying validators when null is passed in", () => { + const validator1 = vi.fn(); + const validator2 = vi.fn(); + const validator3 = vi.fn(); + validator1.mockReturnValue(null); + const subject = nullSafeChain( + nullSafeChain(validator1, validator2), + validator3, + ); + expect(subject(null, "myKey")).toEqual(null); + expect(validator1).toHaveBeenCalledTimes(1); + expect(validator1).toHaveBeenCalledWith(null, "myKey"); + expect(validator2).toHaveBeenCalledTimes(0); + expect(validator3).toHaveBeenCalledTimes(0); + }); + }); + describe("assertValid", () => { + it("throws an error when it is invalid", () => { + expect(() => + assertValid(false, "myValue", "myPath", "myMessage"), + ).toThrowError(/'myPath': Expected myMessage, but got "myValue"\./); + }); + it("does not throw an error when it is valid", () => { + expect( + assertValid(true, "myValue", "myPath", "myMessage"), + ).toBeUndefined(); + }); + }); +}); From d29e2017ffa5c62d593d8149a4268edb9c40cd89 Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Mon, 6 Jan 2025 13:35:54 -0700 Subject: [PATCH 03/15] Add playwright dependencies. --- package-lock.json | 5010 ++++++++++++++++++++++++--------------------- package.json | 3 +- 2 files changed, 2719 insertions(+), 2294 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6ce63c27f..ef72d6eb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "@adobe/alloy", "version": "2.24.0", + "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@adobe/aep-rules-engine": "^2.0.2", @@ -99,9 +100,9 @@ } }, "node_modules/@adobe/aep-rules-engine": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@adobe/aep-rules-engine/-/aep-rules-engine-2.0.2.tgz", - "integrity": "sha512-y5B1LcLo1xbUtRZLe4FRGiburzLu6kgY2VgLutgjoz0bpsKFxb21mqJ1axemsTfpJawYEvuP23+No1ud1ZsP2A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@adobe/aep-rules-engine/-/aep-rules-engine-2.0.3.tgz", + "integrity": "sha512-9znsMcqXrDACO4zgH6gRlbGVfYcYW7yDS7eXS4J/a+ONrWmOHobshUJiRUCwnUK/4igmrCJYlL8GylhsPwi9hQ==", "license": "Apache-2.0" }, "node_modules/@adobe/alloy": { @@ -141,9 +142,9 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", - "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.1.tgz", + "integrity": "sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==", "dev": true, "license": "MIT" }, @@ -178,19 +179,10 @@ "license": "Apache-2.0" }, "node_modules/@adobe/reactor-promise": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@adobe/reactor-promise/-/reactor-promise-1.2.0.tgz", - "integrity": "sha512-0haTPOfJRKvTuG5Sbja3xAFPdv8aJUz+CiU2ocV5F88az0g6fOwt4+NLlLKzJcbh7qcg6MjDMZopbNc0xqUUVg==", - "license": "Apache-2.0", - "dependencies": { - "promise-polyfill": "8.1.3" - } - }, - "node_modules/@adobe/reactor-promise/node_modules/promise-polyfill": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz", - "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==", - "license": "MIT" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@adobe/reactor-promise/-/reactor-promise-2.0.0.tgz", + "integrity": "sha512-kP5jg4J77Rtl9TpNmADaXRWvy3ST+9MDHUlkDtuwuFNL6IKG3L4V930zcM0u0lTntskTd/gAJadTJaWOn1mBCQ==", + "license": "Apache-2.0" }, "node_modules/@adobe/reactor-query-string": { "version": "2.0.0", @@ -212,9 +204,9 @@ } }, "node_modules/@babel/cli": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.25.9.tgz", - "integrity": "sha512-I+02IfrTiSanpxJBlZQYb18qCxB6c2Ih371cVpfgIrPQrjAYkf45XxomTJOG8JBWX5GY35/+TmhCMdJ4ZPkL8Q==", + "version": "7.26.4", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.26.4.tgz", + "integrity": "sha512-+mORf3ezU3p3qr+82WvJSnQNE1GAYeoCfEv4fik6B5/2cvKZ75AX8oawWQdXtM9MmndooQj15Jr9kelRFWsuRw==", "dev": true, "license": "MIT", "dependencies": { @@ -274,9 +266,9 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz", - "integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", @@ -288,9 +280,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz", - "integrity": "sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", + "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -363,13 +355,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz", - "integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", + "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.0", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.3", + "@babel/types": "^7.26.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -390,19 +382,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", - "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", @@ -459,13 +438,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", - "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz", + "integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", - "regexpu-core": "^6.1.1", + "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, "engines": { @@ -485,9 +464,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", + "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", @@ -598,19 +577,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", - "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", @@ -679,12 +645,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", - "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -773,15 +739,15 @@ } }, "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.24.7.tgz", - "integrity": "sha512-RL9GR0pUG5Kc8BUWLNDm2T5OpYwSX15r98I0IkgmRQTXuELq/OynH8xtMTMvTJFjXbMWFVTKtYkTaYQsuAwQlQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.25.9.tgz", + "integrity": "sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-decorators": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-decorators": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -803,13 +769,13 @@ } }, "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.24.7.tgz", - "integrity": "sha512-Ui4uLJJrRV1lb38zg1yYTmRKmiZLiftDEvZN2iq3kd9kUFU+PttmzTbAFC2ucRk/XJmtek6G23gPsuZbhrT8fQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.9.tgz", + "integrity": "sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -832,13 +798,13 @@ } }, "node_modules/@babel/plugin-syntax-flow": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.7.tgz", - "integrity": "sha512-9G8GYT/dxn/D1IIKOUBmGX0mnmj46mGH9NnZyJLwtCpgh5f7D2VbuKodb+2s9m1Yavh1s7ASQN8lf0eqrb1LTw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz", + "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -891,13 +857,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1156,12 +1122,11 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", - "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { @@ -1187,14 +1152,14 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.7.tgz", - "integrity": "sha512-cjRKJ7FobOH2eakx7Ja+KpJRj8+y+/SiB3ooYm/n2UJfxu0oEaOoxOinitkJcPqv9KxS0kxTGPUaR7L2XcXDXA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.9.tgz", + "integrity": "sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-flow": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-flow": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1313,14 +1278,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", - "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-simple-access": "^7.25.9" + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1552,13 +1516,13 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz", - "integrity": "sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", + "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1568,17 +1532,17 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz", - "integrity": "sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1588,13 +1552,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz", - "integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", + "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.24.7" + "@babel/plugin-transform-react-jsx": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1604,14 +1568,14 @@ } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz", - "integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", + "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1930,15 +1894,15 @@ } }, "node_modules/@babel/preset-flow": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.24.7.tgz", - "integrity": "sha512-NL3Lo0NorCU607zU3NwRyJbpaB6E3t0xtd3LfAQKDfkeX4/ggcDXvkmkW42QWT5owUeW/jAe4hn+2qvkV1IbfQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.25.9.tgz", + "integrity": "sha512-EASHsAhE+SSlEzJ4bzfusnXSHiU+JfAYzj+jbw2vgQKgq5HrUr8qs+vgtiEL5dOH6sEweI+PNt2D7AqrDSHyqQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-transform-flow-strip-types": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-transform-flow-strip-types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1962,18 +1926,18 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz", - "integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.26.3.tgz", + "integrity": "sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.24.7", - "@babel/plugin-transform-react-jsx-development": "^7.24.7", - "@babel/plugin-transform-react-pure-annotations": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-transform-react-display-name": "^7.25.9", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/plugin-transform-react-jsx-development": "^7.25.9", + "@babel/plugin-transform-react-pure-annotations": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1983,9 +1947,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", - "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -2009,16 +1973,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", + "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.3", + "@babel/parser": "^7.26.3", "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/types": "^7.26.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2036,9 +2000,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -2220,9 +2184,9 @@ } }, "node_modules/@electron/asar": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.10.tgz", - "integrity": "sha512-mvBSwIBUeiRscrCeJE1LwctAriBj65eUDm0Pc11iE5gRwzkmsdbS7FnZ1XUWjpSeQWL1L5g12Fc/SchPM9DUOw==", + "version": "3.2.17", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.17.tgz", + "integrity": "sha512-OcWImUI686w8LkghQj9R2ynZ2ME693Ek6L1SiaAgqGKzBaTIZw3fHDqN82Rcl+EU1Gm9EgkJ5KLIY/q5DCRbbA==", "dev": true, "license": "MIT", "dependencies": { @@ -2661,16 +2625,19 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } @@ -2688,9 +2655,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -2790,9 +2757,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz", - "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", "dev": true, "license": "MIT", "engines": { @@ -2817,13 +2784,13 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -2852,14 +2819,14 @@ "license": "BSD-3-Clause" }, "node_modules/@inquirer/checkbox": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.0.1.tgz", - "integrity": "sha512-ehJjmNPdguajc1hStvjN7DJNVjwG5LC1mgGMGFjCmdkn2fxB2GtULftMnlaqNmvMdPpqdaSoOFpl86VkLtG4pQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.0.4.tgz", + "integrity": "sha512-fYAKCAcGNMdfjL6hZTRUwkIByQ8EIZCXKrIQZH7XjADnN/xvRUhj8UdBbpC4zoUzvChhkSC/zRKaP/tDs3dZpg==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/figures": "^1.0.7", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.2", + "@inquirer/figures": "^1.0.9", + "@inquirer/type": "^3.0.2", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -2870,39 +2837,15 @@ "@types/node": ">=18" } }, - "node_modules/@inquirer/checkbox/node_modules/@inquirer/type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", - "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - } - }, "node_modules/@inquirer/confirm": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.0.1.tgz", - "integrity": "sha512-6ycMm7k7NUApiMGfVc32yIPp28iPKxhGRMqoNDiUjq2RyTAkbs5Fx0TdzBqhabcKvniDdAAvHCmsRjnNfTsogw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.1.tgz", + "integrity": "sha512-vVLSbGci+IKQvDOtzpPTCOiEJCNidHcAq9JYVoWTW0svb5FiwSLotkM+JXNXejfjnzVYV9n0DTBythl9+XgTxg==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0" - }, - "engines": { - "node": ">=18" + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2" }, - "peerDependencies": { - "@types/node": ">=18" - } - }, - "node_modules/@inquirer/confirm/node_modules/@inquirer/type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", - "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", - "license": "MIT", "engines": { "node": ">=18" }, @@ -2911,13 +2854,13 @@ } }, "node_modules/@inquirer/core": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.0.1.tgz", - "integrity": "sha512-KKTgjViBQUi3AAssqjUFMnMO3CM3qwCHvePV9EW+zTKGKafFGFF01sc1yOIYjLJ7QU52G/FbzKc+c01WLzXmVQ==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.2.tgz", + "integrity": "sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==", "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.7", - "@inquirer/type": "^3.0.0", + "@inquirer/figures": "^1.0.9", + "@inquirer/type": "^3.0.2", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", @@ -2930,35 +2873,14 @@ "node": ">=18" } }, - "node_modules/@inquirer/core/node_modules/@inquirer/type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", - "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - } - }, - "node_modules/@inquirer/core/node_modules/mute-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", - "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/@inquirer/editor": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.0.1.tgz", - "integrity": "sha512-qAHHJ6hs343eNtCKgV2wV5CImFxYG8J1pl/YCeI5w9VoW7QpulRUU26+4NsMhjR6zDRjKBsH/rRjCIcaAOHsrg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.1.tgz", + "integrity": "sha512-xn9aDaiP6nFa432i68JCaL302FyL6y/6EG97nAtfIPnWZ+mWPgCMLGc4XZ2QQMsZtu9q3Jd5AzBPjXh10aX9kA==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2", "external-editor": "^3.1.0" }, "engines": { @@ -2968,26 +2890,14 @@ "@types/node": ">=18" } }, - "node_modules/@inquirer/editor/node_modules/@inquirer/type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", - "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - } - }, "node_modules/@inquirer/expand": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.1.tgz", - "integrity": "sha512-9anjpdc802YInXekwePsa5LWySzVMHbhVS6v6n5IJxrl8w09mODOeP69wZ1d0WrOvot2buQSmYp4lW/pq8y+zQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.4.tgz", + "integrity": "sha512-GYocr+BPyxKPxQ4UZyNMqZFSGKScSUc0Vk17II3J+0bDcgGsQm0KYQNooN1Q5iBfXsy3x/VWmHGh20QnzsaHwg==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -2997,35 +2907,23 @@ "@types/node": ">=18" } }, - "node_modules/@inquirer/expand/node_modules/@inquirer/type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", - "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - } - }, "node_modules/@inquirer/figures": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.7.tgz", - "integrity": "sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.9.tgz", + "integrity": "sha512-BXvGj0ehzrngHTPTDqUoDT3NXL8U0RxUk2zJm2A66RhCEIWdtU1v6GuUqNAgArW4PQ9CinqIWyHdQgdwOj06zQ==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@inquirer/input": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.0.1.tgz", - "integrity": "sha512-m+SliZ2m43cDRIpAdQxfv5QOeAQCuhS8TGLvtzEP1An4IH1kBES4RLMRgE/fC+z29aN8qYG8Tq/eXQQKTYwqAg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.1.tgz", + "integrity": "sha512-nAXAHQndZcXB+7CyjIW3XuQZZHbQQ0q8LX6miY6bqAWwDzNa9JUioDBYrFmOUNIsuF08o1WT/m2gbBXvBhYVxg==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0" + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2" }, "engines": { "node": ">=18" @@ -3034,39 +2932,15 @@ "@types/node": ">=18" } }, - "node_modules/@inquirer/input/node_modules/@inquirer/type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", - "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - } - }, "node_modules/@inquirer/number": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.1.tgz", - "integrity": "sha512-gF3erqfm0snpwBjbyKXUUe17QJ7ebm49btXApajrM0rgCCoYX0o9W5NCuYNae87iPxaIJVjtuoQ42DX32IdbMA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.4.tgz", + "integrity": "sha512-DX7a6IXRPU0j8kr2ovf+QaaDiIf+zEKaZVzCWdLOTk7XigqSXvoh4cul7x68xp54WTQrgSnW7P1WBJDbyY3GhA==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0" - }, - "engines": { - "node": ">=18" + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2" }, - "peerDependencies": { - "@types/node": ">=18" - } - }, - "node_modules/@inquirer/number/node_modules/@inquirer/type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", - "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", - "license": "MIT", "engines": { "node": ">=18" }, @@ -3075,13 +2949,13 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.1.tgz", - "integrity": "sha512-D7zUuX4l4ZpL3D7/SWu9ibijP09jigwHi/gfUHLx5GMS5oXzuMfPV2xPMG1tskco4enTx70HA0VtMXecerpvbg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.4.tgz", + "integrity": "sha512-wiliQOWdjM8FnBmdIHtQV2Ca3S1+tMBUerhyjkRCv1g+4jSvEweGu9GCcvVEgKDhTBT15nrxvk5/bVrGUqSs1w==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2", "ansi-escapes": "^4.3.2" }, "engines": { @@ -3091,34 +2965,22 @@ "@types/node": ">=18" } }, - "node_modules/@inquirer/password/node_modules/@inquirer/type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", - "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - } - }, "node_modules/@inquirer/prompts": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.0.1.tgz", - "integrity": "sha512-cu2CpGC2hz7WTt2VBvdkzahDvYice6vYA/8Dm7Fy3tRNzKuQTF2EY3CV4H2GamveWE6tA2XzyXtbWX8+t4WMQg==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.2.1.tgz", + "integrity": "sha512-v2JSGri6/HXSfoGIwuKEn8sNCQK6nsB2BNpy2lSX6QH9bsECrMv93QHnj5+f+1ZWpF/VNioIV2B/PDox8EvGuQ==", "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^4.0.1", - "@inquirer/confirm": "^5.0.1", - "@inquirer/editor": "^4.0.1", - "@inquirer/expand": "^4.0.1", - "@inquirer/input": "^4.0.1", - "@inquirer/number": "^3.0.1", - "@inquirer/password": "^4.0.1", - "@inquirer/rawlist": "^4.0.1", - "@inquirer/search": "^3.0.1", - "@inquirer/select": "^4.0.1" + "@inquirer/checkbox": "^4.0.4", + "@inquirer/confirm": "^5.1.1", + "@inquirer/editor": "^4.2.1", + "@inquirer/expand": "^4.0.4", + "@inquirer/input": "^4.1.1", + "@inquirer/number": "^3.0.4", + "@inquirer/password": "^4.0.4", + "@inquirer/rawlist": "^4.0.4", + "@inquirer/search": "^3.0.4", + "@inquirer/select": "^4.0.4" }, "engines": { "node": ">=18" @@ -3128,13 +2990,13 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.1.tgz", - "integrity": "sha512-0LuMOgaWs7W8JNcbiKkoFwyWFDEeCmLqDCygF0hidQUVa6J5grFVRZxrpompiWDFM49Km2rf7WoZwRo1uf1yWQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.4.tgz", + "integrity": "sha512-IsVN2EZdNHsmFdKWx9HaXb8T/s3FlR/U1QPt9dwbSyPtjFbMTlW9CRFvnn0bm/QIsrMRD2oMZqrQpSWPQVbXXg==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -3144,27 +3006,15 @@ "@types/node": ">=18" } }, - "node_modules/@inquirer/rawlist/node_modules/@inquirer/type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", - "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - } - }, "node_modules/@inquirer/search": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.1.tgz", - "integrity": "sha512-ehMqjiO0pAf+KtdONKeCLVy4i3fy3feyRRhDrvzWhiwB8JccgKn7eHFr39l+Nx/FaZAhr0YxIJvkK5NuNvG+Ww==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.4.tgz", + "integrity": "sha512-tSkJk2SDmC2MEdTIjknXWmCnmPr5owTs9/xjfa14ol1Oh95n6xW7SYn5fiPk4/vrJPys0ggSWiISdPze4LTa7A==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/figures": "^1.0.7", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.2", + "@inquirer/figures": "^1.0.9", + "@inquirer/type": "^3.0.2", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -3174,27 +3024,15 @@ "@types/node": ">=18" } }, - "node_modules/@inquirer/search/node_modules/@inquirer/type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", - "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - } - }, "node_modules/@inquirer/select": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.0.1.tgz", - "integrity": "sha512-tVRatFRGU49bxFCKi/3P+C0E13KZduNFbWuHWRx0L2+jbiyKRpXgHp9qiRHWRk/KarhYBXzH/di6w3VQ5aJd5w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.0.4.tgz", + "integrity": "sha512-ZzYLuLoUzTIW9EJm++jBpRiTshGqS3Q1o5qOEQqgzaBlmdsjQr6pA4TUNkwu6OBYgM2mIRbCz6mUhFDfl/GF+w==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/figures": "^1.0.7", - "@inquirer/type": "^3.0.0", + "@inquirer/core": "^10.1.2", + "@inquirer/figures": "^1.0.9", + "@inquirer/type": "^3.0.2", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -3205,10 +3043,10 @@ "@types/node": ">=18" } }, - "node_modules/@inquirer/select/node_modules/@inquirer/type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", - "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", + "node_modules/@inquirer/type": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz", + "integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==", "license": "MIT", "engines": { "node": ">=18" @@ -3236,9 +3074,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -3248,29 +3086,17 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { @@ -3318,9 +3144,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -3376,9 +3202,9 @@ } }, "node_modules/@mswjs/interceptors": { - "version": "0.37.3", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.37.3.tgz", - "integrity": "sha512-USvgCL/uOGFtVa6SVyRrC8kIAedzRohxIXN5LISlg5C5vLZCn7dgMFVSNhSF9cuBEFrm/O2spDWEZeMnw4ZXYg==", + "version": "0.37.5", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.37.5.tgz", + "integrity": "sha512-AAwRb5vXFcY4L+FvZ7LZusDuZ0vEe0Zm8ohn1FM6/X7A3bj4mqmkAcGRWuvC2JwSygNwHAAmMnAI73vPHeqsHA==", "dev": true, "license": "MIT", "dependencies": { @@ -3456,17 +3282,17 @@ } }, "node_modules/@octokit/core": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.2.tgz", - "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.3.tgz", + "integrity": "sha512-z+j7DixNnfpdToYsOutStDgeRzJSMnbj8T1C/oQjB6Aa+kRfNjs/Fn7W6c8bmlt6mfy3FkgeKBRnDjxQow5dow==", "dev": true, "license": "MIT", "dependencies": { "@octokit/auth-token": "^5.0.0", - "@octokit/graphql": "^8.0.0", - "@octokit/request": "^9.0.0", - "@octokit/request-error": "^6.0.1", - "@octokit/types": "^13.0.0", + "@octokit/graphql": "^8.1.2", + "@octokit/request": "^9.1.4", + "@octokit/request-error": "^6.1.6", + "@octokit/types": "^13.6.2", "before-after-hook": "^3.0.2", "universal-user-agent": "^7.0.0" }, @@ -3475,13 +3301,13 @@ } }, "node_modules/@octokit/endpoint": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz", - "integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.2.tgz", + "integrity": "sha512-XybpFv9Ms4hX5OCHMZqyODYqGTZ3H6K6Vva+M9LR7ib/xr1y1ZnlChYv9H680y77Vd/i/k+thXApeRASBQkzhA==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/types": "^13.0.0", + "@octokit/types": "^13.6.2", "universal-user-agent": "^7.0.2" }, "engines": { @@ -3489,14 +3315,14 @@ } }, "node_modules/@octokit/graphql": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.1.tgz", - "integrity": "sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==", + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.2.tgz", + "integrity": "sha512-bdlj/CJVjpaz06NBpfHhp4kGJaRZfz7AzC+6EwUImRtrwIw8dIgJ63Xg0OzV9pRn3rIzrt5c2sa++BL0JJ8GLw==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/request": "^9.0.0", - "@octokit/types": "^13.0.0", + "@octokit/request": "^9.1.4", + "@octokit/types": "^13.6.2", "universal-user-agent": "^7.0.0" }, "engines": { @@ -3511,13 +3337,13 @@ "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "11.3.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.3.tgz", - "integrity": "sha512-o4WRoOJZlKqEEgj+i9CpcmnByvtzoUYC6I8PD2SA95M+BJ2x8h7oLcVOg9qcowWXBOdcTRsMZiwvM3EyLm9AfA==", + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.6.tgz", + "integrity": "sha512-zcvqqf/+TicbTCa/Z+3w4eBJcAxCFymtc0UAIsR3dEVoNilWld4oXdscQ3laXamTszUZdusw97K8+DrbFiOwjw==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/types": "^13.5.0" + "@octokit/types": "^13.6.2" }, "engines": { "node": ">= 18" @@ -3540,13 +3366,13 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.4.tgz", - "integrity": "sha512-gusyAVgTrPiuXOdfqOySMDztQHv6928PQ3E4dqVGEtOvRXAKRbJR4b1zQyniIT9waqaWk/UDaoJ2dyPr7Bk7Iw==", + "version": "13.2.6", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.6.tgz", + "integrity": "sha512-wMsdyHMjSfKjGINkdGKki06VEkgdEldIGstIEyGX0wbYHGByOwN/KiM+hAAlUwAtPkP3gvXtVQA9L3ITdV2tVw==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/types": "^13.5.0" + "@octokit/types": "^13.6.1" }, "engines": { "node": ">= 18" @@ -3556,15 +3382,16 @@ } }, "node_modules/@octokit/request": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz", - "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.4.tgz", + "integrity": "sha512-tMbOwGm6wDII6vygP3wUVqFTw3Aoo0FnVQyhihh8vVq12uO3P+vQZeo2CKMpWtPSogpACD0yyZAlVlQnjW71DA==", "dev": true, "license": "MIT", "dependencies": { "@octokit/endpoint": "^10.0.0", "@octokit/request-error": "^6.0.1", - "@octokit/types": "^13.1.0", + "@octokit/types": "^13.6.2", + "fast-content-type-parse": "^2.0.0", "universal-user-agent": "^7.0.2" }, "engines": { @@ -3572,13 +3399,13 @@ } }, "node_modules/@octokit/request-error": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.4.tgz", - "integrity": "sha512-VpAhIUxwhWZQImo/dWAN/NpPqqojR6PSLgLYAituLM6U+ddx9hCioFGwBr5Mi+oi5CLeJkcAs3gJ0PYYzU6wUg==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.6.tgz", + "integrity": "sha512-pqnVKYo/at0NuOjinrgcQYpEbv4snvP3bKMRqHaD9kIsk9u1LCpb2smHZi8/qJfgeNqLo5hNW4Z7FezNdEo0xg==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/types": "^13.0.0" + "@octokit/types": "^13.6.2" }, "engines": { "node": ">= 18" @@ -3601,9 +3428,9 @@ } }, "node_modules/@octokit/types": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", - "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", + "version": "13.6.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.2.tgz", + "integrity": "sha512-WpbZfZUcZU77DrSW4wbsSgTPfKcp286q3ItaIgvSbBpZJlu6mnYXAkjZz6LVZPXkEvLIM8McanyZejKTYUHipA==", "dev": true, "license": "MIT", "dependencies": { @@ -3667,9 +3494,9 @@ "license": "MIT" }, "node_modules/@promptbook/utils": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@promptbook/utils/-/utils-0.58.0.tgz", - "integrity": "sha512-TglWndmjikWN+OGg9eNOUaMTM7RHr8uFCtgxfWULT1BUjcohywdijf54vS1U4mZ1tBLdHD4/fIrIHtmHzPUIZQ==", + "version": "0.69.5", + "resolved": "https://registry.npmjs.org/@promptbook/utils/-/utils-0.69.5.tgz", + "integrity": "sha512-xm5Ti/Hp3o4xHrsK9Yy3MS6KbDxYbq485hDsFvxqaNA7equHLPdo8H8faTitTeb14QCDfLW4iwCxdVYu5sn6YQ==", "dev": true, "funding": [ { @@ -3683,37 +3510,29 @@ ], "license": "CC-BY-4.0", "dependencies": { - "spacetrim": "0.11.36" + "spacetrim": "0.11.59" } }, "node_modules/@puppeteer/browsers": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", - "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", + "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", "dev": true, "license": "Apache-2.0", "dependencies": { "debug": "4.3.4", "extract-zip": "2.0.1", "progress": "2.0.3", - "proxy-agent": "6.3.0", + "proxy-agent": "6.3.1", "tar-fs": "3.0.4", "unbzip2-stream": "1.4.3", - "yargs": "17.7.1" + "yargs": "17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" }, "engines": { "node": ">=16.3.0" - }, - "peerDependencies": { - "typescript": ">= 4.7.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } } }, "node_modules/@puppeteer/browsers/node_modules/debug": { @@ -3734,6 +3553,13 @@ } } }, + "node_modules/@puppeteer/browsers/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, "node_modules/@puppeteer/browsers/node_modules/tar-fs": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", @@ -3758,25 +3584,6 @@ "streamx": "^2.15.0" } }, - "node_modules/@puppeteer/browsers/node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@rollup/plugin-babel": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz", @@ -3804,9 +3611,9 @@ } }, "node_modules/@rollup/plugin-commonjs": { - "version": "28.0.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.1.tgz", - "integrity": "sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==", + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.2.tgz", + "integrity": "sha512-BEFI2EDqzl+vA1rl97IDRZ61AIwGH093d9nz8+dThxJNH8oSoB7MjWvPCX3dkaK1/RCJ/1v/R1XB15FuSs0fQw==", "license": "MIT", "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -3829,36 +3636,10 @@ } } }, - "node_modules/@rollup/plugin-commonjs/node_modules/fdir": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", - "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-commonjs/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@rollup/plugin-node-resolve": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz", - "integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==", + "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==", "license": "MIT", "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -3902,14 +3683,14 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" + "picomatch": "^4.0.2" }, "engines": { "node": ">=14.0.0" @@ -3924,9 +3705,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.4.tgz", - "integrity": "sha512-jfUJrFct/hTA0XDM5p/htWKoNNTbDLY0KRwEt6pyOA6k2fmk0WVwl65PdUdJZgzGEHWx+49LilkcSaumQRyNQw==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.0.tgz", + "integrity": "sha512-qFcFto9figFLz2g25DxJ1WWL9+c91fTxnGuwhToCl8BaqDsDYMl/kOnBXAyAqkkzAWimYMSWNPWEjt+ADAHuoQ==", "cpu": [ "arm" ], @@ -3937,9 +3718,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.4.tgz", - "integrity": "sha512-j4nrEO6nHU1nZUuCfRKoCcvh7PIywQPUCBa2UsootTHvTHIoIu2BzueInGJhhvQO/2FTRdNYpf63xsgEqH9IhA==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.0.tgz", + "integrity": "sha512-vqrQdusvVl7dthqNjWCL043qelBK+gv9v3ZiqdxgaJvmZyIAAXMjeGVSqZynKq69T7062T5VrVTuikKSAAVP6A==", "cpu": [ "arm64" ], @@ -3950,9 +3731,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.4.tgz", - "integrity": "sha512-GmU/QgGtBTeraKyldC7cDVVvAJEOr3dFLKneez/n7BvX57UdhOqDsVwzU7UOnYA7AAOt+Xb26lk79PldDHgMIQ==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.0.tgz", + "integrity": "sha512-617pd92LhdA9+wpixnzsyhVft3szYiN16aNUMzVkf2N+yAk8UXY226Bfp36LvxYTUt7MO/ycqGFjQgJ0wlMaWQ==", "cpu": [ "arm64" ], @@ -3963,9 +3744,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.4.tgz", - "integrity": "sha512-N6oDBiZCBKlwYcsEPXGDE4g9RoxZLK6vT98M8111cW7VsVJFpNEqvJeIPfsCzbf0XEakPslh72X0gnlMi4Ddgg==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.0.tgz", + "integrity": "sha512-Y3b4oDoaEhCypg8ajPqigKDcpi5ZZovemQl9Edpem0uNv6UUjXv7iySBpGIUTSs2ovWOzYpfw9EbFJXF/fJHWw==", "cpu": [ "x64" ], @@ -3976,9 +3757,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.4.tgz", - "integrity": "sha512-py5oNShCCjCyjWXCZNrRGRpjWsF0ic8f4ieBNra5buQz0O/U6mMXCpC1LvrHuhJsNPgRt36tSYMidGzZiJF6mw==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.0.tgz", + "integrity": "sha512-3REQJ4f90sFIBfa0BUokiCdrV/E4uIjhkWe1bMgCkhFXbf4D8YN6C4zwJL881GM818qVYE9BO3dGwjKhpo2ABA==", "cpu": [ "arm64" ], @@ -3989,9 +3770,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.4.tgz", - "integrity": "sha512-L7VVVW9FCnTTp4i7KrmHeDsDvjB4++KOBENYtNYAiYl96jeBThFfhP6HVxL74v4SiZEVDH/1ILscR5U9S4ms4g==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.0.tgz", + "integrity": "sha512-ZtY3Y8icbe3Cc+uQicsXG5L+CRGUfLZjW6j2gn5ikpltt3Whqjfo5mkyZ86UiuHF9Q3ZsaQeW7YswlHnN+lAcg==", "cpu": [ "x64" ], @@ -4002,9 +3783,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.4.tgz", - "integrity": "sha512-10ICosOwYChROdQoQo589N5idQIisxjaFE/PAnX2i0Zr84mY0k9zul1ArH0rnJ/fpgiqfu13TFZR5A5YJLOYZA==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.0.tgz", + "integrity": "sha512-bsPGGzfiHXMhQGuFGpmo2PyTwcrh2otL6ycSZAFTESviUoBOuxF7iBbAL5IJXc/69peXl5rAtbewBFeASZ9O0g==", "cpu": [ "arm" ], @@ -4015,9 +3796,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.4.tgz", - "integrity": "sha512-ySAfWs69LYC7QhRDZNKqNhz2UKN8LDfbKSMAEtoEI0jitwfAG2iZwVqGACJT+kfYvvz3/JgsLlcBP+WWoKCLcw==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.0.tgz", + "integrity": "sha512-kvyIECEhs2DrrdfQf++maCWJIQ974EI4txlz1nNSBaCdtf7i5Xf1AQCEJWOC5rEBisdaMFFnOWNLYt7KpFqy5A==", "cpu": [ "arm" ], @@ -4028,9 +3809,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.4.tgz", - "integrity": "sha512-uHYJ0HNOI6pGEeZ/5mgm5arNVTI0nLlmrbdph+pGXpC9tFHFDQmDMOEqkmUObRfosJqpU8RliYoGz06qSdtcjg==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.0.tgz", + "integrity": "sha512-CFE7zDNrokaotXu+shwIrmWrFxllg79vciH4E/zeK7NitVuWEaXRzS0mFfFvyhZfn8WfVOG/1E9u8/DFEgK7WQ==", "cpu": [ "arm64" ], @@ -4041,9 +3822,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.4.tgz", - "integrity": "sha512-38yiWLemQf7aLHDgTg85fh3hW9stJ0Muk7+s6tIkSUOMmi4Xbv5pH/5Bofnsb6spIwD5FJiR+jg71f0CH5OzoA==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.0.tgz", + "integrity": "sha512-MctNTBlvMcIBP0t8lV/NXiUwFg9oK5F79CxLU+a3xgrdJjfBLVIEHSAjQ9+ipofN2GKaMLnFFXLltg1HEEPaGQ==", "cpu": [ "arm64" ], @@ -4053,10 +3834,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.0.tgz", + "integrity": "sha512-fBpoYwLEPivL3q368+gwn4qnYnr7GVwM6NnMo8rJ4wb0p/Y5lg88vQRRP077gf+tc25akuqd+1Sxbn9meODhwA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.4.tgz", - "integrity": "sha512-q73XUPnkwt9ZNF2xRS4fvneSuaHw2BXuV5rI4cw0fWYVIWIBeDZX7c7FWhFQPNTnE24172K30I+dViWRVD9TwA==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.0.tgz", + "integrity": "sha512-1hiHPV6dUaqIMXrIjN+vgJqtfkLpqHS1Xsg0oUfUVD98xGp1wX89PIXgDF2DWra1nxAd8dfE0Dk59MyeKaBVAw==", "cpu": [ "ppc64" ], @@ -4067,9 +3861,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.4.tgz", - "integrity": "sha512-Aie/TbmQi6UXokJqDZdmTJuZBCU3QBDA8oTKRGtd4ABi/nHgXICulfg1KI6n9/koDsiDbvHAiQO3YAUNa/7BCw==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.0.tgz", + "integrity": "sha512-U0xcC80SMpEbvvLw92emHrNjlS3OXjAM0aVzlWfar6PR0ODWCTQtKeeB+tlAPGfZQXicv1SpWwRz9Hyzq3Jx3g==", "cpu": [ "riscv64" ], @@ -4080,9 +3874,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.4.tgz", - "integrity": "sha512-P8MPErVO/y8ohWSP9JY7lLQ8+YMHfTI4bAdtCi3pC2hTeqFJco2jYspzOzTUB8hwUWIIu1xwOrJE11nP+0JFAQ==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.0.tgz", + "integrity": "sha512-VU/P/IODrNPasgZDLIFJmMiLGez+BN11DQWfTVlViJVabyF3JaeaJkP6teI8760f18BMGCQOW9gOmuzFaI1pUw==", "cpu": [ "s390x" ], @@ -4093,9 +3887,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.4.tgz", - "integrity": "sha512-K03TljaaoPK5FOyNMZAAEmhlyO49LaE4qCsr0lYHUKyb6QacTNF9pnfPpXnFlFD3TXuFbFbz7tJ51FujUXkXYA==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.0.tgz", + "integrity": "sha512-laQVRvdbKmjXuFA3ZiZj7+U24FcmoPlXEi2OyLfbpY2MW1oxLt9Au8q9eHd0x6Pw/Kw4oe9gwVXWwIf2PVqblg==", "cpu": [ "x64" ], @@ -4106,9 +3900,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.4.tgz", - "integrity": "sha512-VJYl4xSl/wqG2D5xTYncVWW+26ICV4wubwN9Gs5NrqhJtayikwCXzPL8GDsLnaLU3WwhQ8W02IinYSFJfyo34Q==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.0.tgz", + "integrity": "sha512-3wzKzduS7jzxqcOvy/ocU/gMR3/QrHEFLge5CD7Si9fyHuoXcidyYZ6jyx8OPYmCcGm3uKTUl+9jUSAY74Ln5A==", "cpu": [ "x64" ], @@ -4119,9 +3913,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.4.tgz", - "integrity": "sha512-ku2GvtPwQfCqoPFIJCqZ8o7bJcj+Y54cZSr43hHca6jLwAiCbZdBUOrqE6y29QFajNAzzpIOwsckaTFmN6/8TA==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.0.tgz", + "integrity": "sha512-jROwnI1+wPyuv696rAFHp5+6RFhXGGwgmgSfzE8e4xfit6oLRg7GyMArVUoM3ChS045OwWr9aTnU+2c1UdBMyw==", "cpu": [ "arm64" ], @@ -4132,9 +3926,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.4.tgz", - "integrity": "sha512-V3nCe+eTt/W6UYNr/wGvO1fLpHUrnlirlypZfKCT1fG6hWfqhPgQV/K/mRBXBpxc0eKLIF18pIOFVPh0mqHjlg==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.0.tgz", + "integrity": "sha512-duzweyup5WELhcXx5H1jokpr13i3BV9b48FMiikYAwk/MT1LrMYYk2TzenBd0jj4ivQIt58JWSxc19y4SvLP4g==", "cpu": [ "ia32" ], @@ -4145,9 +3939,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.4.tgz", - "integrity": "sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.0.tgz", + "integrity": "sha512-DYvxS0M07PvgvavMIybCOBYheyrqlui6ZQBHJs6GqduVzHSZ06TPPvlfvnYstjODHQ8UUXFwt5YE+h0jFI8kwg==", "cpu": [ "x64" ], @@ -4359,55 +4153,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, "node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -4422,16 +4167,18 @@ } }, "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/cors": { "version": "2.8.17", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -4478,9 +4225,9 @@ } }, "node_modules/@types/lodash": { - "version": "4.17.7", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", - "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.14.tgz", + "integrity": "sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==", "dev": true, "license": "MIT" }, @@ -4492,12 +4239,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.14.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", - "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", + "version": "22.10.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz", + "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==", "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.20.0" } }, "node_modules/@types/resolve": { @@ -4538,9 +4285,9 @@ "license": "MIT" }, "node_modules/@types/ws": { - "version": "8.5.11", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz", - "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==", + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", + "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", "dev": true, "license": "MIT", "dependencies": { @@ -4559,9 +4306,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", + "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", "license": "ISC" }, "node_modules/@vitest/browser": { @@ -4601,14 +4348,23 @@ } } }, - "node_modules/@vitest/browser/node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "node_modules/@vitest/expect": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", + "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } }, - "node_modules/@vitest/browser/node_modules/@vitest/mocker": { + "node_modules/@vitest/mocker": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", @@ -4635,60 +4391,7 @@ } } }, - "node_modules/@vitest/browser/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@vitest/browser/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@vitest/browser/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@vitest/browser/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vitest/browser/node_modules/estree-walker": { + "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==", @@ -4698,131 +4401,6 @@ "@types/estree": "^1.0.0" } }, - "node_modules/@vitest/browser/node_modules/msw": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.6.8.tgz", - "integrity": "sha512-nxXxnH6WALZ9a7rsQp4HU2AaD4iGAiouMmE/MY4al7pXTibgA6OZOuKhmN2WBIM6w9qMKwRtX8p2iOb45B2M/Q==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@bundled-es-modules/cookie": "^2.0.1", - "@bundled-es-modules/statuses": "^1.0.1", - "@bundled-es-modules/tough-cookie": "^0.1.6", - "@inquirer/confirm": "^5.0.0", - "@mswjs/interceptors": "^0.37.0", - "@open-draft/deferred-promise": "^2.2.0", - "@open-draft/until": "^2.1.0", - "@types/cookie": "^0.6.0", - "@types/statuses": "^2.0.4", - "chalk": "^4.1.2", - "graphql": "^16.8.1", - "headers-polyfill": "^4.0.2", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.3", - "path-to-regexp": "^6.3.0", - "strict-event-emitter": "^0.5.1", - "type-fest": "^4.26.1", - "yargs": "^17.7.2" - }, - "bin": { - "msw": "cli/index.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/mswjs" - }, - "peerDependencies": { - "typescript": ">= 4.8.x" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@vitest/browser/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@vitest/browser/node_modules/type-fest": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.30.1.tgz", - "integrity": "sha512-ojFL7eDMX2NF0xMbDwPZJ8sb7ckqtlAi1GsmgsFXvErT9kFTk1r0DuQKvrCh73M6D4nngeHJmvogF9OluXs7Hw==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vitest/browser/node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/@vitest/browser/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "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/@vitest/expect": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", - "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "2.1.8", - "@vitest/utils": "2.1.8", - "chai": "^5.1.2", - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "node_modules/@vitest/pretty-format": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", @@ -4894,16 +4472,16 @@ } }, "node_modules/@wdio/config": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.33.0.tgz", - "integrity": "sha512-SaCZNKrDtBghf7ujyaxTiU4pBW+1Kms32shSoXpJ/wFop6/MiA7nb19qpUPoJtEDw5/NOKevUKz8nBMBXphiew==", + "version": "7.40.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.40.0.tgz", + "integrity": "sha512-ayQELXyxa+k9/2a509F5a1oTsCa/w8D1nDrd+hzm+1mYb4Te2lceWCCzm+atGKkMpvjLH4GvhrEBYLh3rIWk2A==", "dev": true, "license": "MIT", "dependencies": { "@types/glob": "^8.1.0", "@wdio/logger": "7.26.0", - "@wdio/types": "7.33.0", - "@wdio/utils": "7.33.0", + "@wdio/types": "7.40.0", + "@wdio/utils": "7.40.0", "deepmerge": "^4.0.0", "glob": "^8.0.3" }, @@ -5048,22 +4626,22 @@ } }, "node_modules/@wdio/repl": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.24.12.tgz", - "integrity": "sha512-321F3sWafnlw93uRTSjEBVuvWCxTkWNDs7ektQS15drrroL3TMeFOynu4rDrIz0jXD9Vas0HCD2Tq/P0uxFLdw==", + "version": "8.40.3", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.40.3.tgz", + "integrity": "sha512-mWEiBbaC7CgxvSd2/ozpbZWebnRIc8KRu/J81Hlw/txUWio27S7IpXBlZGVvhEsNzq0+cuxB/8gDkkXvMPbesw==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "^20.1.0" + "@types/node": "^22.2.0" }, "engines": { "node": "^16.13 || >=18" } }, "node_modules/@wdio/types": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.33.0.tgz", - "integrity": "sha512-tNcuN5Kl+i5CffaeTYV1omzAo4rVjiI1m9raIA8ph6iVteWdCzYv2/ImpGgFiBPb7Mf6VokU3+q9Slh5Jitaww==", + "version": "7.40.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.40.0.tgz", + "integrity": "sha512-MWMbU+8uk+JrF7ygP/TJDsaSvFozKauiW6EnG7rxx9+GvU1Q1B3l4UjAc7GDbgLKjwt8T2y5GDRiDoD3UOjVyw==", "dev": true, "license": "MIT", "dependencies": { @@ -5083,24 +4661,31 @@ } }, "node_modules/@wdio/types/node_modules/@types/node": { - "version": "18.19.40", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.40.tgz", - "integrity": "sha512-MIxieZHrm4Ee8XArBIc+Or9HINt2StOmCbgRcXGSJl8q14svRvkZPe7LJq9HKtTI1SK3wU8b91TjntUm7T69Pg==", + "version": "18.19.70", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.70.tgz", + "integrity": "sha512-RE+K0+KZoEpDUbGGctnGdkrLFwi1eYKTlIHNl2Um98mUkGsm1u2Ff6Ltd0e8DktTtC98uy7rSj+hO8t/QuLoVQ==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } }, + "node_modules/@wdio/types/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, "node_modules/@wdio/utils": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.33.0.tgz", - "integrity": "sha512-4kQQ86EvEN6fBY5+u7M08cT6LfJtpk1rHd203xyxmbmV9lpNv/OCl4CsC+SD0jGT0aZZqYSIJ/Pil07pAh5K0g==", + "version": "7.40.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.40.0.tgz", + "integrity": "sha512-jLF57xHmz5nnGuM6ZRWjVYa/LQb22CS7yG50dUFa9wJ509mC1HlUzaA01Gjk9TV5jf9vnwE/yZfUMCoecTgG9w==", "dev": true, "license": "MIT", "dependencies": { "@wdio/logger": "7.26.0", - "@wdio/types": "7.33.0", + "@wdio/types": "7.40.0", "p-iteration": "^1.1.8" }, "engines": { @@ -5108,9 +4693,9 @@ } }, "node_modules/@zip.js/zip.js": { - "version": "2.7.47", - "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.47.tgz", - "integrity": "sha512-jmtJMA3/Jl4rMzo/DZ79s6g0CJ1AZcNAO6emTy/vHfIKAB/iiFY7PLs6KmbRTJ+F8GnK2eCLnjQfCCneRxXgzg==", + "version": "2.7.54", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.54.tgz", + "integrity": "sha512-qMrJVg2hoEsZJjMJez9yI2+nZlBUxgYzGV3mqcb2B/6T1ihXp0fWBDYlVHlHquuorgNUQP5a8qSmX6HF5rFJNg==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -5137,6 +4722,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, + "license": "MIT", "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -5146,9 +4732,9 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -5211,16 +4797,13 @@ } }, "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, "license": "MIT", - "dependencies": { - "debug": "4" - }, "engines": { - "node": ">= 6.0.0" + "node": ">= 14" } }, "node_modules/aggregate-error": { @@ -5288,13 +4871,13 @@ } }, "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -5314,6 +4897,19 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -5529,9 +5125,9 @@ } }, "node_modules/archiver-utils/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.6.0.tgz", + "integrity": "sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw==", "dev": true, "license": "MIT", "dependencies": { @@ -5576,16 +5172,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/archiver/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/archiver/node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -5611,23 +5197,10 @@ "ieee754": "^1.2.1" } }, - "node_modules/archiver/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/archiver/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.6.0.tgz", + "integrity": "sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw==", "dev": true, "license": "MIT", "dependencies": { @@ -5641,16 +5214,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/archiver/node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.1.0" - } - }, "node_modules/archiver/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -5734,14 +5297,14 @@ } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" @@ -5829,16 +5392,16 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5848,16 +5411,16 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5867,20 +5430,19 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -5933,9 +5495,9 @@ } }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, "license": "MIT" }, @@ -5983,16 +5545,16 @@ } }, "node_modules/aws4": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.0.tgz", - "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", "dev": true, "license": "MIT" }, "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "dev": true, "license": "MIT", "dependencies": { @@ -6002,9 +5564,9 @@ } }, "node_modules/b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", "dev": true, "license": "Apache-2.0" }, @@ -6070,13 +5632,13 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz", + "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==", "license": "MIT", "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", + "@babel/helper-define-polyfill-provider": "^0.6.3", "semver": "^6.3.1" }, "peerDependencies": { @@ -6106,12 +5668,12 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz", + "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==", "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" + "@babel/helper-define-polyfill-provider": "^0.6.3" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -6158,17 +5720,17 @@ "license": "MIT" }, "node_modules/bare-events": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", - "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.2.tgz", + "integrity": "sha512-KSdMqLj1ZERZMP1PTmnLK7SqJu9z9/SbwUUPZly2puMtfVcytC+jl6mb/9XYiqq0PXcx1rNDS+Qvl1g54Lho6A==", "dev": true, "license": "Apache-2.0", "optional": true }, "node_modules/bare-fs": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz", - "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -6179,9 +5741,9 @@ } }, "node_modules/bare-os": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz", - "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", "dev": true, "license": "Apache-2.0", "optional": true @@ -6198,14 +5760,14 @@ } }, "node_modules/bare-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz", - "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.1.tgz", + "integrity": "sha512-eVZbtKM+4uehzrsj49KtCy3Pbg7kO1pJ3SKZ1SFrIH/0pnj9scuGGgUlNDf/7qS8WKtGdiJY5Kyhs/ivYPTB/g==", "dev": true, "license": "Apache-2.0", "optional": true, "dependencies": { - "streamx": "^2.18.0" + "streamx": "^2.21.0" } }, "node_modules/base64-js": { @@ -6234,6 +5796,7 @@ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", "dev": true, + "license": "MIT", "engines": { "node": "^4.5.0 || >= 5.9" } @@ -6265,6 +5828,16 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, "node_modules/bin-check": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", @@ -6454,9 +6027,9 @@ } }, "node_modules/bin-version/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "license": "MIT", "dependencies": { @@ -6616,6 +6189,20 @@ "node": ">=6" } }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -6625,35 +6212,19 @@ "engines": { "node": ">=8" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" } }, "node_modules/bluebird": { @@ -6668,6 +6239,7 @@ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dev": true, + "license": "MIT", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -6708,6 +6280,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "license": "MIT" }, @@ -6756,9 +6329,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", + "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", "funding": [ { "type": "opencollective", @@ -6775,9 +6348,9 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -6853,6 +6426,25 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true, + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/bundlesize": { "version": "0.18.2", "resolved": "https://registry.npmjs.org/bundlesize/-/bundlesize-0.18.2.tgz", @@ -6972,17 +6564,47 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -7048,9 +6670,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001673", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001673.tgz", - "integrity": "sha512-WTrjUCSMp3LYX0nE12ECkV0a+e6LC85E0Auz75555/qr78Oc8YWhEPNfDd6SHdtlCMSzqtuXY0uyEMNRcsKpKw==", + "version": "1.0.30001690", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", + "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", "funding": [ { "type": "opencollective", @@ -7119,10 +6741,23 @@ "node": ">=12" } }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, "license": "MIT", "engines": { @@ -7255,13 +6890,14 @@ } }, "node_modules/chromium-bidi": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", - "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.8.tgz", + "integrity": "sha512-blqh+1cEQbHBKmok3rVJkBlBxt9beKBgOsxbFgs7UJcoVbbeZ+K7+6liAsjgpc8l1Xd55cQUy14fXZdGSb4zIw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "mitt": "3.0.0" + "mitt": "3.0.1", + "urlpattern-polyfill": "10.0.0" }, "peerDependencies": { "devtools-protocol": "*" @@ -7325,9 +6961,9 @@ } }, "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -7338,9 +6974,9 @@ } }, "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true, "license": "MIT" }, @@ -7438,6 +7074,38 @@ "dev": true, "license": "MIT" }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cliui/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -7617,9 +7285,9 @@ } }, "node_modules/compress-commons/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.6.0.tgz", + "integrity": "sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw==", "dev": true, "license": "MIT", "dependencies": { @@ -7671,9 +7339,9 @@ "license": "MIT" }, "node_modules/concurrently": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.0.tgz", - "integrity": "sha512-VxkzwMAn4LP7WyMnJNbHN5mKV9L2IbyDjpzemKr99sXNR3GqRNMMHdm7prV1ws9wg7ETj6WUkNOigZVsptwbgg==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.2.tgz", + "integrity": "sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7887,14 +7555,15 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/core-js": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz", - "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -7904,12 +7573,12 @@ } }, "node_modules/core-js-compat": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", - "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", + "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", "license": "MIT", "dependencies": { - "browserslist": "^4.23.3" + "browserslist": "^4.24.2" }, "funding": { "type": "opencollective", @@ -7928,6 +7597,7 @@ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "dev": true, + "license": "MIT", "dependencies": { "object-assign": "^4", "vary": "^1" @@ -8005,9 +7675,9 @@ } }, "node_modules/crc32-stream/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.6.0.tgz", + "integrity": "sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw==", "dev": true, "license": "MIT", "dependencies": { @@ -8063,9 +7733,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -8088,10 +7758,11 @@ } }, "node_modules/css-shorthand-properties": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz", - "integrity": "sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.2.tgz", + "integrity": "sha512-C2AugXIpRGQTxaCW0N7n5jD/p5irUmCrwl03TrnMFBHDbdq44CFWR2zO7rK9xPN4Eo3pUxC4vQzQgbIpzrD1PQ==", + "dev": true, + "license": "MIT" }, "node_modules/css-value": { "version": "0.0.1", @@ -8162,39 +7833,6 @@ "node": ">=18" } }, - "node_modules/data-urls/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/data-urls/node_modules/tr46": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", - "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/data-urls/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/data-urls/node_modules/whatwg-mimetype": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", @@ -8205,30 +7843,16 @@ "node": ">=18" } }, - "node_modules/data-urls/node_modules/whatwg-url": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", - "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "^5.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -8238,31 +7862,31 @@ } }, "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/inspect-js" } }, "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" }, @@ -8302,9 +7926,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -8318,12 +7942,6 @@ } } }, - "node_modules/debug/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==", - "license": "MIT" - }, "node_modules/decamelize": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", @@ -8897,9 +8515,9 @@ "license": "MIT" }, "node_modules/devtools-protocol": { - "version": "0.0.1302984", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1302984.tgz", - "integrity": "sha512-Rgh2Sk5fUSCtEx4QGH9iwTyECdFPySG2nlz5J8guGh2Wlha6uzSOCq/DCEC8faHlLaMPZJMuZ4ovgcX4LvOkKA==", + "version": "0.0.1109433", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1109433.tgz", + "integrity": "sha512-w1Eqih66egbSr2eOoGZ+NsdF7HdxmKDo3pKFBySEGsmVvwWWNXzNCDcKrbFnd23Jf7kH1M806OfelXwu+Jk11g==", "dev": true, "license": "BSD-3-Clause" }, @@ -8977,9 +8595,9 @@ } }, "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -9206,6 +8824,21 @@ "node": ">=4" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -9213,6 +8846,16 @@ "dev": true, "license": "MIT" }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, "node_modules/duplexer3": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", @@ -9256,17 +8899,18 @@ } }, "node_modules/edgedriver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.6.0.tgz", - "integrity": "sha512-IeJXEczG+DNYBIa9gFgVYTqrawlxmc9SUqUsWU2E98jOsO/amA7wzabKOS8Bwgr/3xWoyXCJ6yGFrbFKrilyyQ==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.6.1.tgz", + "integrity": "sha512-3Ve9cd5ziLByUdigw6zovVeWJjVs8QHVmqOB0sJ0WNeVPcwf4p18GnxMmVvlFmYRloUwf5suNuorea4QzwBIOA==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "@wdio/logger": "^8.28.0", - "@zip.js/zip.js": "^2.7.44", + "@wdio/logger": "^8.38.0", + "@zip.js/zip.js": "^2.7.48", "decamelize": "^6.0.0", "edge-paths": "^3.0.5", + "fast-xml-parser": "^4.4.1", "node-fetch": "^3.3.2", "which": "^4.0.0" }, @@ -9291,9 +8935,9 @@ } }, "node_modules/edgedriver/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -9382,9 +9026,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.47", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.47.tgz", - "integrity": "sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ==", + "version": "1.5.77", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.77.tgz", + "integrity": "sha512-AnJSrt5JpRVgY6dgd5yccguLc5A7oMSF0Kt3fcW+Hp5WTuFbl5upeSFZbMZYy2o7jhmIhU8Ekrd82GhyXUqUUg==", "license": "ISC" }, "node_modules/elegant-spinner": { @@ -9417,9 +9061,10 @@ } }, "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, "license": "MIT" }, "node_modules/encodeurl": { @@ -9447,6 +9092,7 @@ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", "dev": true, + "license": "MIT", "dependencies": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", @@ -9468,18 +9114,69 @@ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "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/ent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.1.tgz", - "integrity": "sha512-QHuXVeZx9d+tIQAz/XztU0ZwZf2Agg9CcXcgE1rurqvdBeDBrpSwjl8/6XUqMg7tw2Y7uAdKb2sRv+bSEFqQ5A==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.2.tgz", + "integrity": "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==", "dev": true, "license": "MIT", "dependencies": { - "punycode": "^1.4.1" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "punycode": "^1.4.1", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -9532,58 +9229,63 @@ } }, "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", "dev": true, "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" }, "engines": { "node": ">= 0.4" @@ -9593,14 +9295,11 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -9616,9 +9315,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", "dev": true, "license": "MIT" }, @@ -9636,15 +9335,16 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -9661,15 +9361,15 @@ } }, "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -9785,16 +9485,17 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -10073,9 +9774,9 @@ } }, "node_modules/eslint/node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -10567,6 +10268,23 @@ ], "license": "MIT" }, + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "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", @@ -10588,9 +10306,9 @@ "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -10598,7 +10316,7 @@ "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -10616,10 +10334,33 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz", + "integrity": "sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -10635,6 +10376,20 @@ "pend": "~1.2.0" } }, + "node_modules/fdir": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -10769,14 +10524,13 @@ } }, "node_modules/find-babel-config": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-2.1.1.tgz", - "integrity": "sha512-5Ji+EAysHGe1OipH7GN4qDjok5Z1uw5KAwDCbicU/4wyTZY7CqOCzcWbG7J5ad9mazq67k89fXlbc1MuIfl9uA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-2.1.2.tgz", + "integrity": "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg==", "dev": true, "license": "MIT", "dependencies": { - "json5": "^2.2.3", - "path-exists": "^4.0.0" + "json5": "^2.2.3" } }, "node_modules/find-up": { @@ -10860,15 +10614,15 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "license": "ISC" }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "dev": true, "funding": [ { @@ -10897,9 +10651,9 @@ } }, "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, "license": "ISC", "dependencies": { @@ -10924,9 +10678,9 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dev": true, "license": "MIT", "dependencies": { @@ -11028,6 +10782,59 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/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/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -11038,16 +10845,18 @@ } }, "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -11143,20 +10952,20 @@ } }, "node_modules/geckodriver": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.4.2.tgz", - "integrity": "sha512-/JFJ7DJPJUvDhLjzQk+DwjlkAmiShddfRHhZ/xVL9FWbza5Bi3UMGmmerEKqD69JbRs7R81ZW31co686mdYZyA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.2.1.tgz", + "integrity": "sha512-4m/CRk0OI8MaANRuFIahvOxYTSjlNAO2p9JmE14zxueknq6cdtB5M9UGRQ8R9aMV0bLGNVHHDnDXmoXdOwJfWg==", "dev": true, "hasInstallScript": true, "license": "MPL-2.0", "dependencies": { - "@wdio/logger": "^8.28.0", - "@zip.js/zip.js": "^2.7.44", + "@wdio/logger": "^8.11.0", "decamelize": "^6.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.4", - "node-fetch": "^3.3.2", - "tar-fs": "^3.0.6", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.1", + "tar-fs": "^3.0.4", + "unzipper": "^0.10.14", "which": "^4.0.0" }, "bin": { @@ -11182,23 +10991,10 @@ "node": "^16.13 || >=18" } }, - "node_modules/geckodriver/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/geckodriver/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -11218,20 +11014,6 @@ "node": ">= 12" } }, - "node_modules/geckodriver/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/geckodriver/node_modules/isexe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", @@ -11340,9 +11122,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", - "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", "dev": true, "license": "MIT", "engines": { @@ -11363,17 +11145,22 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dev": true, "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -11408,6 +11195,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-proxy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", @@ -11445,15 +11246,15 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -11463,59 +11264,20 @@ } }, "node_modules/get-uri": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", - "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", + "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", "dev": true, "license": "MIT", "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4", - "fs-extra": "^11.2.0" + "debug": "^4.3.4" }, "engines": { "node": ">= 14" } }, - "node_modules/get-uri/node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/get-uri/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/get-uri/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/getos": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", @@ -11648,9 +11410,9 @@ } }, "node_modules/globals": { - "version": "15.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz", - "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==", + "version": "15.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", + "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", "dev": true, "license": "MIT", "engines": { @@ -11709,13 +11471,13 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11778,9 +11540,9 @@ } }, "node_modules/graphql": { - "version": "16.9.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", - "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "version": "16.10.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", + "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", "dev": true, "license": "MIT", "engines": { @@ -11824,9 +11586,9 @@ } }, "node_modules/happy-dom": { - "version": "15.11.1", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-15.11.1.tgz", - "integrity": "sha512-c6Ysm3743TQBBaNu26IOFtlb7rl5iS85zOq5PpZwe2NO70/8fs6nZOnC9mN1cxRsAlL7nx6G+/4kk2zBz4afJg==", + "version": "15.11.7", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-15.11.7.tgz", + "integrity": "sha512-KyrFvnl+J9US63TEzwoiJOQzZBJY7KgBushJA8X61DMbNsH+2ONkDuLDnCnwUiPTF42tLoEmrPyoqbenVA5zrg==", "dev": true, "license": "MIT", "dependencies": { @@ -11838,16 +11600,6 @@ "node": ">=18.0.0" } }, - "node_modules/happy-dom/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/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -11874,11 +11626,14 @@ } }, "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -11906,11 +11661,14 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -11929,9 +11687,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "license": "MIT", "engines": { @@ -12181,19 +11939,6 @@ "node": ">= 14" } }, - "node_modules/http-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -12267,17 +12012,17 @@ } }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "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": "6", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/human-signals": { @@ -12298,9 +12043,9 @@ "license": "Unlicense" }, "node_modules/husky": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.6.tgz", - "integrity": "sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==", + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", "dev": true, "license": "MIT", "bin": { @@ -12347,9 +12092,9 @@ "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "license": "MIT", "engines": { "node": ">= 4" @@ -12465,15 +12210,15 @@ "license": "ISC" }, "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -12522,14 +12267,15 @@ "license": "BSD-3-Clause" }, "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -12545,14 +12291,36 @@ "dev": true, "license": "MIT" }, + "node_modules/is-async-function": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", + "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -12572,14 +12340,14 @@ } }, "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -12615,9 +12383,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -12630,12 +12398,14 @@ } }, "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" }, "engines": { @@ -12646,13 +12416,14 @@ } }, "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -12703,6 +12474,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-finite": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", @@ -12729,6 +12516,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -12741,6 +12547,19 @@ "node": ">=0.10.0" } }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", @@ -12754,19 +12573,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-node-process": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", @@ -12785,13 +12591,14 @@ } }, "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -12895,14 +12702,16 @@ } }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -12921,14 +12730,27 @@ "node": ">=0.10.0" } }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -12951,13 +12773,14 @@ } }, "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -12967,13 +12790,15 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -12983,13 +12808,13 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -13012,14 +12837,47 @@ "dev": true, "license": "MIT" }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -13196,9 +13054,9 @@ } }, "node_modules/jackspeak": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz", - "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -13209,29 +13067,26 @@ }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/jasmine": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.4.0.tgz", - "integrity": "sha512-E2u4ylX5tgGYvbynImU6EUBKKrSVB1L72FEPjGh4M55ov1VsxR26RA2JU91L9YSPFgcjo4mCLyKn/QXvEYGBkA==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.5.0.tgz", + "integrity": "sha512-JKlEVCVD5QBPYLsg/VE+IUtjyseDCrW8rMBu8la+9ysYashDgavMLM9Kotls1FhI6dCJLJ40dBCIfQjGLPZI1Q==", "dev": true, "license": "MIT", "dependencies": { "glob": "^10.2.2", - "jasmine-core": "~5.4.0" + "jasmine-core": "~5.5.0" }, "bin": { "jasmine": "bin/jasmine.js" } }, "node_modules/jasmine-core": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.4.0.tgz", - "integrity": "sha512-T4fio3W++llLd7LGSGsioriDHgWyhoL6YTu4k37uwJLF7DzOzspz7mNxRoM3cQdLWtL/ebazQpIf/yZGJx/gzg==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.5.0.tgz", + "integrity": "sha512-NHOvoPO6o9gVR6pwqEACTEpbgcH+JJ6QDypyymGbSUIFIFsMMbBJ/xsFNud8MSClfnWclXd7RQlAZBz7yVo5TQ==", "dev": true, "license": "MIT" }, @@ -13430,89 +13285,6 @@ } } }, - "node_modules/jsdom/node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/jsdom/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/jsdom/node_modules/parse5": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", - "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "entities": "^4.5.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/jsdom/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/jsdom/node_modules/tough-cookie": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", - "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/jsdom/node_modules/tr46": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", - "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/jsdom/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/jsdom/node_modules/whatwg-mimetype": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", @@ -13523,46 +13295,10 @@ "node": ">=18" } }, - "node_modules/jsdom/node_modules/whatwg-url": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", - "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "^5.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/jsdom/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "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/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -13933,6 +13669,13 @@ "dev": true, "license": "MIT" }, + "node_modules/karma/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/karma/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -13949,10 +13692,20 @@ "path-is-absolute": "^1.0.0" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/karma/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, "node_modules/karma/node_modules/rimraf": { @@ -13972,6 +13725,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/karma/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/karma/node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -14108,9 +13876,9 @@ } }, "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, "license": "MIT", "engines": { @@ -14121,22 +13889,22 @@ } }, "node_modules/lint-staged": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", - "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.3.0.tgz", + "integrity": "sha512-vHFahytLoF2enJklgtOtCtIjZrKD/LoxlaUusd5nh7dWv/dkKQJY74ndFSzxCdv7g0ueGg1ORgTSt4Y9LPZn9A==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "~5.3.0", + "chalk": "~5.4.1", "commander": "~12.1.0", - "debug": "~4.3.6", + "debug": "~4.4.0", "execa": "~8.0.1", - "lilconfig": "~3.1.2", - "listr2": "~8.2.4", + "lilconfig": "~3.1.3", + "listr2": "~8.2.5", "micromatch": "~4.0.8", "pidtree": "~0.6.0", "string-argv": "~0.3.2", - "yaml": "~2.5.0" + "yaml": "~2.6.1" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -14158,10 +13926,17 @@ "os-family": "^1.0.0" } }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true, + "license": "ISC" + }, "node_modules/listr2": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", - "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", + "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==", "dev": true, "license": "MIT", "dependencies": { @@ -14177,9 +13952,9 @@ } }, "node_modules/listr2/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -14189,10 +13964,23 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/listr2/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true, "license": "MIT" }, @@ -14256,9 +14044,9 @@ } }, "node_modules/locate-app": { - "version": "2.4.21", - "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.4.21.tgz", - "integrity": "sha512-ySSBwlUnVKoLgw39q8YaNtvklhaTMoVqBf2+CuY3hkOFuWubHAJ6NJuTjv+jfTV1FuOgKsigRdsYUIeVgKHvNA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.5.0.tgz", + "integrity": "sha512-xIqbzPMBYArJRmPGUZD9CzV9wOqmVtQnaAn3wrj3s6WYW0bQvPI7x+sPYUGmDTYMHefVK//zc6HEYZ1qnxIK+Q==", "dev": true, "funding": [ { @@ -14270,21 +14058,21 @@ "url": "https://github.com/hejny/locate-app/blob/main/README.md#%EF%B8%8F-contributing" } ], - "license": "SEE LICENSE IN LICENSE", + "license": "Apache-2.0", "dependencies": { - "@promptbook/utils": "0.58.0", - "type-fest": "2.13.0", - "userhome": "1.0.0" + "@promptbook/utils": "0.69.5", + "type-fest": "4.26.0", + "userhome": "1.0.1" } }, "node_modules/locate-app/node_modules/type-fest": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.13.0.tgz", - "integrity": "sha512-lPfAm42MxE4/456+QyIaaVBAwgpJb6xZ8PRu09utnhPdWwcyj9vgy6Sq0Z5yNbJ21EdxB5dRU/Qg8bsyAMtlcw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.0.tgz", + "integrity": "sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=12.20" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -14406,6 +14194,23 @@ "dev": true, "license": "MIT" }, + "node_modules/log-update-async-hook/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update-async-hook/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/log-update-async-hook/node_modules/mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", @@ -14429,6 +14234,21 @@ "node": ">=4" } }, + "node_modules/log-update-async-hook/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/log-update-async-hook/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -14464,9 +14284,9 @@ } }, "node_modules/log-update/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -14476,10 +14296,23 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/log-update/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true, "license": "MIT" }, @@ -14586,9 +14419,9 @@ } }, "node_modules/loglevel": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", - "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", "dev": true, "license": "MIT", "engines": { @@ -14653,9 +14486,9 @@ } }, "node_modules/macos-release": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.2.0.tgz", - "integrity": "sha512-fSErXALFNsnowREYZ49XCdOHF8wOPWuFOGQrAhP7x5J/BqQv+B02cNsTykGpDgRVx43EKg++6ANmTaGTtW+hUA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.3.0.tgz", + "integrity": "sha512-tPJQ1HeyiU2vRruNGhZ+VleWuMQRro8iFtJxYgnS4NQe+EukKF6aGiIT+7flZhISAt2iaXBCfFGvAyif7/f8nQ==", "dev": true, "license": "MIT", "engines": { @@ -14666,9 +14499,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.12", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", - "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -14747,6 +14580,16 @@ "node": ">=10" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -14779,6 +14622,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -14787,6 +14631,19 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -14899,9 +14756,9 @@ } }, "node_modules/mitt": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", - "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==", + "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" }, @@ -14952,12 +14809,69 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/msw": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.7.0.tgz", + "integrity": "sha512-BIodwZ19RWfCbYTxWTUfTXc+sg4OwjCAgxU1ZsgmggX/7S3LdUifsbUPJs61j0rWb19CZRGY5if77duhc0uXzw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.1", + "@bundled-es-modules/statuses": "^1.0.1", + "@bundled-es-modules/tough-cookie": "^0.1.6", + "@inquirer/confirm": "^5.0.0", + "@mswjs/interceptors": "^0.37.0", + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "path-to-regexp": "^6.3.0", + "picocolors": "^1.1.1", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.26.1", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.8.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw/node_modules/type-fest": { + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.31.0.tgz", + "integrity": "sha512-yCxltHW07Nkhv/1F6wWBr8kz+5BGMfP+RbRSYFnegVb0qV/UMT0G0ElBloPVerqn4M2ZV80Ir1FtCcYv1cT6vQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mustache": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz", @@ -14971,17 +14885,26 @@ "npm": ">=1.4.0" } }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/nan": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", - "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", + "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", "dev": true, "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -15015,6 +14938,7 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -15115,10 +15039,35 @@ } } }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "license": "MIT" }, "node_modules/noop-logger": { @@ -15246,9 +15195,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "dev": true, "license": "MIT", "engines": { @@ -15269,15 +15218,17 @@ } }, "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -15337,13 +15288,14 @@ } }, "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, @@ -15445,6 +15397,24 @@ "dev": true, "license": "MIT" }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -15562,47 +15532,20 @@ } }, "node_modules/pac-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", - "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.1.0.tgz", + "integrity": "sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==", "dev": true, "license": "MIT", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", + "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.5", + "https-proxy-agent": "^7.0.6", "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" @@ -15623,9 +15566,9 @@ } }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, "license": "BlueOak-1.0.0" }, @@ -15698,16 +15641,24 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/parse-uri/-/parse-uri-1.0.9.tgz", "integrity": "sha512-YZfRHHkEZa6qTfPF/xgZ1ErQYCABfud/Vcqp1Q1GNa7RKwv6Oe0YaxXfQQMnQsGdNTo3fwaT0GbVEX7dMAr7tw==", + "license": "MIT", "engines": { "node": ">= 0.10" } }, - "node_modules/parse5": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz", - "integrity": "sha512-w2jx/0tJzvgKwZa58sj2vAYq/S/K1QJfIB3cWYea/Iu1scFPDQQ3IQiVZTHWtRBwAjv2Yd7S/xeZf3XqLDb3bA==", - "dev": true - }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -15798,9 +15749,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz", - "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", "dev": true, "license": "ISC", "engines": { @@ -15875,12 +15826,12 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -16157,9 +16108,9 @@ } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, "license": "MIT", "bin": { @@ -16200,19 +16151,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -16353,47 +16291,20 @@ "license": "ISC" }, "node_modules/proxy-agent": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", - "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", + "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.0", + "pac-proxy-agent": "^7.0.1", "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" + "socks-proxy-agent": "^8.0.2" }, "engines": { "node": ">= 14" @@ -16440,16 +16351,32 @@ "license": "ISC" }, "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/psl/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/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dev": true, "license": "MIT", "dependencies": { @@ -16465,29 +16392,21 @@ "license": "MIT" }, "node_modules/puppeteer-core": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", - "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", + "version": "21.11.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-21.11.0.tgz", + "integrity": "sha512-ArbnyA3U5SGHokEvkfWjW+O8hOxV1RSJxOgriX/3A4xZRqixt9ZFHD0yPgZQF05Qj0oAqi8H/7stDorjoHY90Q==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "1.4.6", - "chromium-bidi": "0.4.16", + "@puppeteer/browsers": "1.9.1", + "chromium-bidi": "0.5.8", "cross-fetch": "4.0.0", "debug": "4.3.4", - "devtools-protocol": "0.0.1147663", - "ws": "8.13.0" + "devtools-protocol": "0.0.1232444", + "ws": "8.16.0" }, "engines": { - "node": ">=16.3.0" - }, - "peerDependencies": { - "typescript": ">= 4.7.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=16.13.2" } }, "node_modules/puppeteer-core/node_modules/debug": { @@ -16509,16 +16428,23 @@ } }, "node_modules/puppeteer-core/node_modules/devtools-protocol": { - "version": "0.0.1147663", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", - "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "version": "0.0.1232444", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1232444.tgz", + "integrity": "sha512-pM27vqEfxSxRkTMnF+XCmxSEb6duO5R+t8A9DEEJgy4Wz2RVanje2mmj99B6A3zv2r/qGfYlOvYznUhuokizmg==", "dev": true, "license": "BSD-3-Clause" }, + "node_modules/puppeteer-core/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, "node_modules/puppeteer-core/node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "license": "MIT", "engines": { @@ -16561,6 +16487,7 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.6" }, @@ -16754,6 +16681,39 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -16767,6 +16727,19 @@ "node": ">=8.10.0" } }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/recursive-readdir": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", @@ -16780,6 +16753,29 @@ "node": ">=6.0.0" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -16814,16 +16810,18 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -16833,15 +16831,15 @@ } }, "node_modules/regexpu-core": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", - "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", "license": "MIT", "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", "regjsgen": "^0.8.0", - "regjsparser": "^0.11.0", + "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -16856,9 +16854,9 @@ "license": "MIT" }, "node_modules/regjsparser": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz", - "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", "license": "BSD-2-Clause", "dependencies": { "jsesc": "~3.0.2" @@ -16867,6 +16865,18 @@ "regjsparser": "bin/parser" } }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/repeating": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", @@ -16938,6 +16948,16 @@ "node": ">= 0.12" } }, + "node_modules/request/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/request/node_modules/qs": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", @@ -16948,6 +16968,20 @@ "node": ">=0.6" } }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/request/node_modules/uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -16994,18 +17028,21 @@ "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -17183,9 +17220,9 @@ "license": "BSD-3-Clause" }, "node_modules/rollup": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.4.tgz", - "integrity": "sha512-vGorVWIsWfX3xbcyAS+I047kFKapHYivmkaT63Smj77XwvLSJos6M1xGqZnBPFQFBRZDOcG1QnYEIxAvTr/HjA==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.0.tgz", + "integrity": "sha512-sDnr1pcjTgUT69qBksNF1N1anwfbyYG6TBQ22b03bII8EdiUQ7J0TlozVaTMjT/eEJAO49e1ndV7t+UZfL1+vA==", "license": "MIT", "dependencies": { "@types/estree": "1.0.6" @@ -17198,24 +17235,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.4", - "@rollup/rollup-android-arm64": "4.24.4", - "@rollup/rollup-darwin-arm64": "4.24.4", - "@rollup/rollup-darwin-x64": "4.24.4", - "@rollup/rollup-freebsd-arm64": "4.24.4", - "@rollup/rollup-freebsd-x64": "4.24.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.4", - "@rollup/rollup-linux-arm-musleabihf": "4.24.4", - "@rollup/rollup-linux-arm64-gnu": "4.24.4", - "@rollup/rollup-linux-arm64-musl": "4.24.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.4", - "@rollup/rollup-linux-riscv64-gnu": "4.24.4", - "@rollup/rollup-linux-s390x-gnu": "4.24.4", - "@rollup/rollup-linux-x64-gnu": "4.24.4", - "@rollup/rollup-linux-x64-musl": "4.24.4", - "@rollup/rollup-win32-arm64-msvc": "4.24.4", - "@rollup/rollup-win32-ia32-msvc": "4.24.4", - "@rollup/rollup-win32-x64-msvc": "4.24.4", + "@rollup/rollup-android-arm-eabi": "4.30.0", + "@rollup/rollup-android-arm64": "4.30.0", + "@rollup/rollup-darwin-arm64": "4.30.0", + "@rollup/rollup-darwin-x64": "4.30.0", + "@rollup/rollup-freebsd-arm64": "4.30.0", + "@rollup/rollup-freebsd-x64": "4.30.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.30.0", + "@rollup/rollup-linux-arm-musleabihf": "4.30.0", + "@rollup/rollup-linux-arm64-gnu": "4.30.0", + "@rollup/rollup-linux-arm64-musl": "4.30.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.30.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.30.0", + "@rollup/rollup-linux-riscv64-gnu": "4.30.0", + "@rollup/rollup-linux-s390x-gnu": "4.30.0", + "@rollup/rollup-linux-x64-gnu": "4.30.0", + "@rollup/rollup-linux-x64-musl": "4.30.0", + "@rollup/rollup-win32-arm64-msvc": "4.30.0", + "@rollup/rollup-win32-ia32-msvc": "4.30.0", + "@rollup/rollup-win32-x64-msvc": "4.30.0", "fsevents": "~2.3.2" } }, @@ -17305,20 +17343,6 @@ } } }, - "node_modules/rollup-plugin-license/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/rollup-pluginutils": { "version": "2.8.2", "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", @@ -17384,15 +17408,16 @@ "license": "MIT" }, "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -17415,16 +17440,40 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -17467,6 +17516,19 @@ "node": ">= 4" } }, + "node_modules/sauce-connect-launcher/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/sauce-connect-launcher/node_modules/async": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", @@ -17493,10 +17555,24 @@ "path-is-absolute": "^1.0.0" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sauce-connect-launcher/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" } }, "node_modules/sauce-connect-launcher/node_modules/rimraf": { @@ -17597,10 +17673,17 @@ "dev": true, "license": "MIT" }, + "node_modules/saucelabs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/saucelabs/node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz", + "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17612,6 +17695,31 @@ "node": ">= 6" } }, + "node_modules/saucelabs/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/saucelabs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/saucelabs/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -17804,9 +17912,9 @@ "license": "ISC" }, "node_modules/set-cookie-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", - "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", "dev": true, "license": "MIT" }, @@ -17844,6 +17952,21 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -17880,26 +18003,86 @@ } }, "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", "dev": true, "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -18035,6 +18218,19 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "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", @@ -18064,10 +18260,11 @@ } }, "node_modules/socket.io": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz", - "integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", "dev": true, + "license": "MIT", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", @@ -18092,6 +18289,46 @@ "ws": "~8.17.1" } }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "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/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -18106,6 +18343,42 @@ "node": ">=10.0.0" } }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/socks": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", @@ -18122,13 +18395,13 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", - "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "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.1", + "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" }, @@ -18136,19 +18409,6 @@ "node": ">= 14" } }, - "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -18205,9 +18465,9 @@ } }, "node_modules/spacetrim": { - "version": "0.11.36", - "resolved": "https://registry.npmjs.org/spacetrim/-/spacetrim-0.11.36.tgz", - "integrity": "sha512-jqv5aAfMLkBnFK+38QUtEGgU7x1KrfpDnCdjX4+W1IEVgA8Kf3tk8K9je8j2nkCSXdIngjda53fuXERr4/61kw==", + "version": "0.11.59", + "resolved": "https://registry.npmjs.org/spacetrim/-/spacetrim-0.11.59.tgz", + "integrity": "sha512-lLYsktklSRKprreOm7NXReW8YiX2VBjbgmXYEziOoGf/qsJqAEACaDvoTtUOycwjpaSh+bT8eu0KrJn7UNxiCg==", "dev": true, "funding": [ { @@ -18219,7 +18479,7 @@ "url": "https://github.com/hejny/spacetrim/blob/main/README.md#%EF%B8%8F-contributing" } ], - "license": "SEE LICENSE IN LICENSE" + "license": "Apache-2.0" }, "node_modules/spdx-compare": { "version": "1.0.0", @@ -18258,9 +18518,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", - "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", "license": "CC0-1.0" }, "node_modules/spdx-ranges": { @@ -18361,16 +18621,16 @@ } }, "node_modules/start-server-and-test": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.8.tgz", - "integrity": "sha512-v2fV6NV2F7tL1ocwfI4Wpait+IKjRbT5l3ZZ+ZikXdMLmxYsS8ynGAsCQAUVXkVyGyS+UibsRnvgHkMvJIvCsw==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.9.tgz", + "integrity": "sha512-DDceIvc4wdpr+z3Aqkot2QMho8TcUBh5qH0wEHDpEexBTzlheOcmh53d3dExABY4J5C7qS2UbSXqRWLtxpbWIQ==", "dev": true, "license": "MIT", "dependencies": { "arg": "^5.0.2", "bluebird": "3.7.2", "check-more-types": "2.24.0", - "debug": "4.3.7", + "debug": "4.4.0", "execa": "5.1.1", "lazy-ass": "1.6.0", "ps-tree": "1.2.0", @@ -18544,9 +18804,9 @@ } }, "node_modules/streamx": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", - "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", + "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==", "dev": true, "license": "MIT", "dependencies": { @@ -18596,17 +18856,21 @@ } }, "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/string-width-cjs": { @@ -18625,6 +18889,13 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/string-width-cjs/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", @@ -18635,26 +18906,49 @@ "node": ">=8" } }, - "node_modules/string-width/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", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -18664,16 +18958,20 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -18803,6 +19101,13 @@ "node": ">=0.8.0" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true, + "license": "MIT" + }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -18839,9 +19144,9 @@ "license": "MIT" }, "node_modules/synckit": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", - "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", "dev": true, "license": "MIT", "dependencies": { @@ -18868,6 +19173,18 @@ "tar-stream": "^2.1.4" } }, + "node_modules/tar-fs/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/tar-fs/node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -18919,21 +19236,10 @@ "node": ">= 0.8.0" } }, - "node_modules/tar-stream/node_modules/bl": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", - "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", - "dev": true, - "license": "MIT", - "dependencies": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, "node_modules/terser": { - "version": "5.31.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.3.tgz", - "integrity": "sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==", + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", + "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -18955,9 +19261,9 @@ "license": "MIT" }, "node_modules/testcafe": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-3.7.0.tgz", - "integrity": "sha512-e77yuzX/1eSqb6ctE0oW+PJbgsA975Ui8xYiVAFTQjZnt9oZixB7gEgbwRdIls5EEWmtcxJ5tvqTm1rPBsrOvA==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-3.7.1.tgz", + "integrity": "sha512-Nz7lnGPsbocm7+iNMxEfoDNQyi2rovJv0g7TXzhJfKt7WMJXMT3OyQza2g9AbICee29GenzKtzt3JhUsyIiOug==", "dev": true, "license": "MIT", "dependencies": { @@ -19044,7 +19350,7 @@ "source-map-support": "^0.5.16", "strip-bom": "^2.0.0", "testcafe-browser-tools": "2.0.26", - "testcafe-hammerhead": "31.7.3", + "testcafe-hammerhead": "31.7.4", "testcafe-legacy-api": "5.1.8", "testcafe-reporter-json": "^2.1.0", "testcafe-reporter-list": "^2.2.0", @@ -19418,9 +19724,9 @@ } }, "node_modules/testcafe-hammerhead": { - "version": "31.7.3", - "resolved": "https://registry.npmjs.org/testcafe-hammerhead/-/testcafe-hammerhead-31.7.3.tgz", - "integrity": "sha512-LmldhnuUUNcel66z8hjwPkxGrA6jaGt6K9B8iuxOVVRuhpqFfmP3do5MeplK9NyPbIjkAW6WsHDu+nUM88IUsA==", + "version": "31.7.4", + "resolved": "https://registry.npmjs.org/testcafe-hammerhead/-/testcafe-hammerhead-31.7.4.tgz", + "integrity": "sha512-cTBLS4pDGTJpGoLI9TBuB/TfmVuawSss4PRqxfa2MhvD2QVnLgOlQ/ODNZcYJXaeJKrmlSoSlF6uSt5lif00Uw==", "dev": true, "license": "MIT", "dependencies": { @@ -19519,18 +19825,12 @@ "mime": "cli.js" } }, - "node_modules/testcafe-hammerhead/node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "node_modules/testcafe-hammerhead/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true, - "license": "MIT", - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } + "license": "MIT" }, "node_modules/testcafe-hammerhead/node_modules/punycode": { "version": "2.3.1", @@ -19711,9 +20011,9 @@ "license": "MIT" }, "node_modules/testcafe-reporter-saucelabs": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/testcafe-reporter-saucelabs/-/testcafe-reporter-saucelabs-3.5.1.tgz", - "integrity": "sha512-7aVMDZPErrynVTNDDEPfGZ8/Iq69iR7HAkxVGT2lwWAF0Zmdx0z1YPmZvmgGrTikWPIwE3ysJ/+F+mFQSc4Eeg==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/testcafe-reporter-saucelabs/-/testcafe-reporter-saucelabs-3.6.0.tgz", + "integrity": "sha512-4yHWSuFeZ1vtkIxuoF3D/EQDmG9Iw+SFUXDiOBI33lf1ipnI4XgtpUBn8PM+HKBM5oQxaOOBw9tc57am+gF0kg==", "dev": true, "license": "MIT", "dependencies": { @@ -19945,13 +20245,6 @@ "node": ">=0.12" } }, - "node_modules/testcafe/node_modules/devtools-protocol": { - "version": "0.0.1109433", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1109433.tgz", - "integrity": "sha512-w1Eqih66egbSr2eOoGZ+NsdF7HdxmKDo3pKFBySEGsmVvwWWNXzNCDcKrbFnd23Jf7kH1M806OfelXwu+Jk11g==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/testcafe/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -20123,6 +20416,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/testcafe/node_modules/parse5": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz", + "integrity": "sha512-w2jx/0tJzvgKwZa58sj2vAYq/S/K1QJfIB3cWYea/Iu1scFPDQQ3IQiVZTHWtRBwAjv2Yd7S/xeZf3XqLDb3bA==", + "dev": true + }, "node_modules/testcafe/node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -20196,6 +20495,27 @@ "node": ">=0.4.0" } }, + "node_modules/testcafe/node_modules/typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/testcafe/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, "node_modules/testcafe/node_modules/url-to-options": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-2.0.0.tgz", @@ -20207,9 +20527,9 @@ } }, "node_modules/text-decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", - "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==", + "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==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -20257,16 +20577,16 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz", - "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", "dev": true, "license": "MIT" }, "node_modules/tinypool": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", - "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", "dev": true, "license": "MIT", "engines": { @@ -20294,22 +20614,22 @@ } }, "node_modules/tldts": { - "version": "6.1.66", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.66.tgz", - "integrity": "sha512-l3ciXsYFel/jSRfESbyKYud1nOw7WfhrBEF9I3UiarYk/qEaOOwu3qXNECHw4fHGHGTEOuhf/VdKgoDX5M/dhQ==", + "version": "6.1.71", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.71.tgz", + "integrity": "sha512-LQIHmHnuzfZgZWAf2HzL83TIIrD8NhhI0DVxqo9/FdOd4ilec+NTNZOlDZf7EwrTNoutccbsHjvWHYXLAtvxjw==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^6.1.66" + "tldts-core": "^6.1.71" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.66", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.66.tgz", - "integrity": "sha512-s07jJruSwndD2X8bVjwioPfqpIc1pDTzszPe9pL1Skbh4bjytL85KNQ3tolqLbCvpQHawIsGfFi9dgerWjqW4g==", + "version": "6.1.71", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.71.tgz", + "integrity": "sha512-LRbChn2YRpic1KxY+ldL1pGXN/oVvKfCVufwfVzEQdFYNo39uF7AJa/WXdo+gYO7PTvdfkCPCed6Hkvz/kR7jg==", "dev": true, "license": "MIT" }, @@ -20366,20 +20686,32 @@ } }, "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" + "tldts": "^6.1.32" }, "engines": { - "node": ">=0.8" + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" } }, - "node_modules/tough-cookie/node_modules/punycode": { + "node_modules/tr46/node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", @@ -20389,12 +20721,15 @@ "node": ">=6" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", "dev": true, - "license": "MIT" + "license": "MIT/X11", + "engines": { + "node": "*" + } }, "node_modules/tree-kill": { "version": "1.2.2", @@ -20476,9 +20811,9 @@ } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD" }, @@ -20525,9 +20860,9 @@ } }, "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==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, "license": "MIT", "engines": { @@ -20561,32 +20896,32 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -20596,18 +20931,19 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -20617,18 +20953,18 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.3", "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -20638,11 +20974,13 @@ } }, "node_modules/typescript": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", - "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -20652,9 +20990,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.38", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.38.tgz", - "integrity": "sha512-fYmIy7fKTSFAhG3fuPlubeGaMoAd6r0rSnfEsO5nEY55i26KSLt9EH7PLQiiqPUhNqYIJvSkTy1oArIcXAbPbA==", + "version": "0.7.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.40.tgz", + "integrity": "sha512-us1E3K+3jJppDBa3Tl0L3MOJiGhe1C6P0+nIvQAFYbxlMAx0h81eOwLmU57xgqToduDDPx3y5QsdjPfDu+FgOQ==", "dev": true, "funding": [ { @@ -20671,14 +21009,17 @@ } ], "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, "engines": { "node": "*" } }, "node_modules/uglify-js": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.0.tgz", - "integrity": "sha512-wNKHUY2hYYkf6oSFfhwwiHo4WCHzHmzcXsqXYTN9ja3iApYIFbb2U6ics9hBcYLHcYGQoAlwnZlTrf3oF+BL/Q==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, "license": "BSD-2-Clause", "optional": true, @@ -20690,16 +21031,19 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -20724,9 +21068,9 @@ "license": "MIT" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -20803,6 +21147,32 @@ "dev": true, "license": "MIT" }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "dev": true, + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -20912,11 +21282,19 @@ "node": ">= 4" } }, + "node_modules/urlpattern-polyfill": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", + "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", + "dev": true, + "license": "MIT" + }, "node_modules/userhome": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/userhome/-/userhome-1.0.0.tgz", - "integrity": "sha512-ayFKY3H+Pwfy4W98yPdtH1VqH4psDeyW8lYYFzfecR9d6hqLpqhecktvYR3SEEXt7vG0S1JEpciI3g94pMErig==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/userhome/-/userhome-1.0.1.tgz", + "integrity": "sha512-5cnLm4gseXjAclKowC4IjByaGsjtAoV6PrOQOljplNB54ReUYJP8HdAFq2muHinSDAh09PPX/uXDPfdxRHvuSA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -20946,9 +21324,9 @@ } }, "node_modules/uuid": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz", - "integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.4.tgz", + "integrity": "sha512-IzL6VtTTYcAhA/oghbFJ1Dkmqev+FpQWnCBaKq/gUluLxliWvO8DPFWfIviRmYbtaavtSQe4WBL++rFjdcGWEg==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -20963,6 +21341,7 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -21138,43 +21517,6 @@ } } }, - "node_modules/vitest/node_modules/@vitest/mocker": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", - "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "2.1.8", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.12" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/vitest/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, "node_modules/void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", @@ -21323,18 +21665,18 @@ } }, "node_modules/webdriver": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.33.0.tgz", - "integrity": "sha512-cyMRAVUHgQhEBHojOeNet2e8GkfyvvjpioNCPcF6qUtT+URdagr8Mh0t4Fs+Jr0tpuMqFnw70xZexAcV/6I/jg==", + "version": "7.40.0", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.40.0.tgz", + "integrity": "sha512-CKi3cDWgNVE/ibcsBfdtA+pQVeZ4oYlecLlwemulVxJdgr4l5bv+nXuoIhnYeVb6aAI4naK772vmWQ0XuRYhDQ==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "^18.0.0", - "@wdio/config": "7.33.0", + "@wdio/config": "7.40.0", "@wdio/logger": "7.26.0", "@wdio/protocols": "7.27.0", - "@wdio/types": "7.33.0", - "@wdio/utils": "7.33.0", + "@wdio/types": "7.40.0", + "@wdio/utils": "7.40.0", "got": "^11.0.2", "ky": "0.30.0", "lodash.merge": "^4.6.1" @@ -21344,34 +21686,41 @@ } }, "node_modules/webdriver/node_modules/@types/node": { - "version": "18.19.40", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.40.tgz", - "integrity": "sha512-MIxieZHrm4Ee8XArBIc+Or9HINt2StOmCbgRcXGSJl8q14svRvkZPe7LJq9HKtTI1SK3wU8b91TjntUm7T69Pg==", + "version": "18.19.70", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.70.tgz", + "integrity": "sha512-RE+K0+KZoEpDUbGGctnGdkrLFwi1eYKTlIHNl2Um98mUkGsm1u2Ff6Ltd0e8DktTtC98uy7rSj+hO8t/QuLoVQ==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } }, + "node_modules/webdriver/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, "node_modules/webdriverio": { - "version": "8.39.1", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.39.1.tgz", - "integrity": "sha512-dPwLgLNtP+l4vnybz+YFxxH8nBKOP7j6VVzKtfDyTLDQg9rz3U8OA4xMMQCBucnrVXy3KcKxGqlnMa+c4IfWCQ==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.41.0.tgz", + "integrity": "sha512-WlQfw0mUEhTS8DPr+TBSYMhEnqXkFr2dcUwPb5XkffTB+i0wftf+BLXJPSVD9M1PTLyYcFdCIu68pqR54dq5BA==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "^20.1.0", - "@wdio/config": "8.39.0", + "@types/node": "^22.2.0", + "@wdio/config": "8.41.0", "@wdio/logger": "8.38.0", - "@wdio/protocols": "8.38.0", - "@wdio/repl": "8.24.12", - "@wdio/types": "8.39.0", - "@wdio/utils": "8.39.0", + "@wdio/protocols": "8.40.3", + "@wdio/repl": "8.40.3", + "@wdio/types": "8.41.0", + "@wdio/utils": "8.41.0", "archiver": "^7.0.0", "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools-protocol": "^0.0.1302984", + "devtools-protocol": "^0.0.1359167", "grapheme-splitter": "^1.0.2", "import-meta-resolve": "^4.0.0", "is-plain-obj": "^4.1.0", @@ -21379,12 +21728,12 @@ "lodash.clonedeep": "^4.5.0", "lodash.zip": "^4.2.0", "minimatch": "^9.0.0", - "puppeteer-core": "^20.9.0", + "puppeteer-core": "^21.11.0", "query-selector-shadow-dom": "^1.0.0", "resq": "^1.9.1", "rgb2hex": "0.2.5", "serialize-error": "^11.0.1", - "webdriver": "8.39.0" + "webdriver": "8.41.0" }, "engines": { "node": "^16.13 || >=18" @@ -21398,28 +21747,6 @@ } } }, - "node_modules/webdriverio/node_modules/@puppeteer/browsers": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", - "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "debug": "4.3.4", - "extract-zip": "2.0.1", - "progress": "2.0.3", - "proxy-agent": "6.3.1", - "tar-fs": "3.0.4", - "unbzip2-stream": "1.4.3", - "yargs": "17.7.2" - }, - "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=16.3.0" - } - }, "node_modules/webdriverio/node_modules/@sindresorhus/is": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", @@ -21447,15 +21774,15 @@ } }, "node_modules/webdriverio/node_modules/@wdio/config": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.39.0.tgz", - "integrity": "sha512-yNuGPMPibY91s936gnJCHWlStvIyDrwLwGfLC/NCdTin4F7HL4Gp5iJnHWkJFty1/DfFi8jjoIUBNLM8HEez+A==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.41.0.tgz", + "integrity": "sha512-/6Z3sfSyhX5oVde0l01fyHimbqRYIVUDBnhDG2EMSCoC2lsaJX3Bm3IYpYHYHHFsgoDCi3B3Gv++t9dn2eSZZw==", "dev": true, "license": "MIT", "dependencies": { "@wdio/logger": "8.38.0", - "@wdio/types": "8.39.0", - "@wdio/utils": "8.39.0", + "@wdio/types": "8.41.0", + "@wdio/utils": "8.41.0", "decamelize": "^6.0.0", "deepmerge-ts": "^5.0.0", "glob": "^10.2.2", @@ -21482,39 +21809,39 @@ } }, "node_modules/webdriverio/node_modules/@wdio/protocols": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.38.0.tgz", - "integrity": "sha512-7BPi7aXwUtnXZPeWJRmnCNFjyDvGrXlBmN9D4Pi58nILkyjVRQKEY9/qv/pcdyB0cvmIvw++Kl/1Lg+RxG++UA==", + "version": "8.40.3", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.40.3.tgz", + "integrity": "sha512-wK7+eyrB3TAei8RwbdkcyoNk2dPu+mduMBOdPJjp8jf/mavd15nIUXLID1zA+w5m1Qt1DsT1NbvaeO9+aJQ33A==", "dev": true, "license": "MIT" }, "node_modules/webdriverio/node_modules/@wdio/types": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.39.0.tgz", - "integrity": "sha512-86lcYROTapOJuFd9ouomFDfzDnv3Kn+jE0RmqfvN9frZAeLVJ5IKjX9M6HjplsyTZhjGO1uCaehmzx+HJus33Q==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.41.0.tgz", + "integrity": "sha512-t4NaNTvJZci3Xv/yUZPH4eTL0hxrVTf5wdwNnYIBrzMnlRDbNefjQ0P7FM7ZjQCLaH92AEH6t/XanUId7Webug==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "^20.1.0" + "@types/node": "^22.2.0" }, "engines": { "node": "^16.13 || >=18" } }, "node_modules/webdriverio/node_modules/@wdio/utils": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.39.0.tgz", - "integrity": "sha512-jY+n6jlGeK+9Tx8T659PKLwMQTGpLW5H78CSEWgZLbjbVSr2LfGR8Lx0CRktNXxAtqEVZPj16Pi74OtAhvhE6Q==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.41.0.tgz", + "integrity": "sha512-0TcTjBiax1VxtJQ/iQA0ZyYOSHjjX2ARVmEI0AMo9+AuIq+xBfnY561+v8k9GqOMPKsiH/HrK3xwjx8xCVS03g==", "dev": true, "license": "MIT", "dependencies": { "@puppeteer/browsers": "^1.6.0", "@wdio/logger": "8.38.0", - "@wdio/types": "8.39.0", + "@wdio/types": "8.41.0", "decamelize": "^6.0.0", "deepmerge-ts": "^5.1.0", "edgedriver": "^5.5.0", - "geckodriver": "^4.3.1", + "geckodriver": "~4.2.0", "get-port": "^7.0.0", "import-meta-resolve": "^4.0.0", "locate-app": "^2.1.0", @@ -21526,23 +21853,10 @@ "node": "^16.13 || >=18" } }, - "node_modules/webdriverio/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/webdriverio/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -21588,27 +21902,16 @@ "responselike": "^3.0.0" }, "engines": { - "node": ">=14.16" - } - }, - "node_modules/webdriverio/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=14.16" } }, + "node_modules/webdriverio/node_modules/devtools-protocol": { + "version": "0.0.1359167", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1359167.tgz", + "integrity": "sha512-f/9PeTaSH3weS/WAwrQb5/s9R3KMOeTGe+Jkhg5952yInub7iDPjdlzRdrDgpLZfxHbTrBuG9aUkAMM+ocVkXQ==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/webdriverio/node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -21683,20 +21986,6 @@ "node": ">=10.19.0" } }, - "node_modules/webdriverio/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/webdriverio/node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -21753,14 +22042,11 @@ } }, "node_modules/webdriverio/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==", + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } + "license": "ISC" }, "node_modules/webdriverio/node_modules/mimic-response": { "version": "4.0.0", @@ -21831,33 +22117,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/webdriverio/node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/webdriverio/node_modules/proxy-agent": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", - "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/webdriverio/node_modules/responselike": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", @@ -21906,30 +22165,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/webdriverio/node_modules/tar-fs": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", - "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - } - }, - "node_modules/webdriverio/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, "node_modules/webdriverio/node_modules/type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", @@ -21944,19 +22179,19 @@ } }, "node_modules/webdriverio/node_modules/webdriver": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.39.0.tgz", - "integrity": "sha512-Kc3+SfiH4ufyrIht683VT2vnJocx0pfH8rYdyPvEh1b2OYewtFTHK36k9rBDHZiBmk6jcSXs4M2xeFgOuon9Lg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.41.0.tgz", + "integrity": "sha512-n8OrFnVT4hAaGa0Advr3T8ObJdeKNTRklHIEzM2CYVx/5DZt+2KwaKSxWsURNd4zU7FbsfaJUU4rQWCmvozQLg==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "^20.1.0", + "@types/node": "^22.2.0", "@types/ws": "^8.5.3", - "@wdio/config": "8.39.0", + "@wdio/config": "8.41.0", "@wdio/logger": "8.38.0", - "@wdio/protocols": "8.38.0", - "@wdio/types": "8.39.0", - "@wdio/utils": "8.39.0", + "@wdio/protocols": "8.40.3", + "@wdio/types": "8.41.0", + "@wdio/utils": "8.41.0", "deepmerge-ts": "^5.1.0", "got": "^12.6.1", "ky": "^0.33.0", @@ -21967,11 +22202,14 @@ } }, "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "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" + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } }, "node_modules/whatwg-encoding": { "version": "3.1.1", @@ -22010,14 +22248,17 @@ } }, "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", "dev": true, "license": "MIT", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/which": { @@ -22036,17 +22277,74 @@ } }, "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/which-collection": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -22121,16 +22419,17 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "for-each": "^0.3.3", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -22167,6 +22466,38 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/wide-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wide-align/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/windows-release": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-5.1.1.tgz", @@ -22384,6 +22715,38 @@ "dev": true, "license": "MIT" }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -22417,6 +22780,35 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -22424,9 +22816,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, "license": "MIT", "engines": { @@ -22499,9 +22891,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", - "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", + "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", "dev": true, "license": "ISC", "bin": { @@ -22540,6 +22932,38 @@ "node": ">=12" } }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", @@ -22626,9 +23050,9 @@ } }, "node_modules/zip-stream/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.6.0.tgz", + "integrity": "sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 49c1055bf..cd4fc6ef3 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,8 @@ "prepare": "husky && cd sandbox && npm install", "prepack": "rimraf libEs5 libEs6 && babel src -d libEs5 --env-name npmEs5 && babel src -d libEs6 --env-name npmEs6", "checkthattestfilesexist": "./scripts/checkThatTestFilesExist.js", - "add-license": "./scripts/add-license.js" + "add-license": "./scripts/add-license.js", + "postinstall": "npx playwright install chromium" }, "lint-staged": { "./*.{cjs,mjs,js,jsx}": [ From 21dff58330a89ee7a16ef22d043e204018167268 Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Tue, 7 Jan 2025 12:48:37 -0700 Subject: [PATCH 04/15] Add coverage options for vitest. Change properties order. --- package-lock.json | 170 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + vite.config.js | 3 + vitest.workspace.js | 4 +- 4 files changed, 176 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ef72d6eb8..042a14983 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,7 @@ "@eslint/js": "^9.14.0", "@octokit/rest": "^21.0.2", "@vitest/browser": "^2.1.8", + "@vitest/coverage-v8": "^2.1.8", "bundlesize": "^0.18.2", "chai": "^5.1.2", "chalk": "^5.3.0", @@ -2012,6 +2013,13 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, "node_modules/@bundled-es-modules/cookie": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz", @@ -4348,6 +4356,54 @@ } } }, + "node_modules/@vitest/coverage-v8": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.8.tgz", + "integrity": "sha512-2Y7BPlKH18mAZYAW1tYByudlCYrQyl5RGvnnDYJKW5tCiO5qg3KSAy3XAxcxKz900a0ZXxWtKrMuZLe3lKBpJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@bcoe/v8-coverage": "^0.2.3", + "debug": "^4.3.7", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.12", + "magicast": "^0.3.5", + "std-env": "^3.8.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "2.1.8", + "vitest": "2.1.8" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/coverage-v8/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/@vitest/expect": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", @@ -14507,6 +14563,18 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, "node_modules/make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -19260,6 +19328,108 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "license": "MIT" }, + "node_modules/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/test-exclude/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/testcafe": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-3.7.1.tgz", diff --git a/package.json b/package.json index cd4fc6ef3..d5c00028c 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "@eslint/js": "^9.14.0", "@octokit/rest": "^21.0.2", "@vitest/browser": "^2.1.8", + "@vitest/coverage-v8": "^2.1.8", "bundlesize": "^0.18.2", "chai": "^5.1.2", "chalk": "^5.3.0", diff --git a/vite.config.js b/vite.config.js index b1955dd21..17533bdc0 100644 --- a/vite.config.js +++ b/vite.config.js @@ -8,5 +8,8 @@ export default defineConfig({ environment: "happy-dom", isolate: false, pool: "threads", + coverage: { + include: ["src/**/*"], + }, }, }); diff --git a/vitest.workspace.js b/vitest.workspace.js index 95eb35ff3..d596ee352 100644 --- a/vitest.workspace.js +++ b/vitest.workspace.js @@ -27,9 +27,9 @@ export default defineWorkspace([ pool: "threads", browser: { name: "chromium", - enabled: true, - headless: true, provider: "playwright", + headless: true, + enabled: true, }, }, }, From 24ba152ae8d74ba25a2b27f806216d37f0785e54 Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Wed, 8 Jan 2025 11:18:06 -0700 Subject: [PATCH 05/15] Just run everything in the browser. Remove workspace config. Rename tests suite. --- .github/workflows/dev.yaml | 51 +------------------------------------- package.json | 12 ++++----- vite.config.js | 13 +++++++--- vitest.workspace.js | 36 --------------------------- 4 files changed, 15 insertions(+), 97 deletions(-) delete mode 100644 vitest.workspace.js diff --git a/.github/workflows/dev.yaml b/.github/workflows/dev.yaml index af03becbf..a5992240d 100644 --- a/.github/workflows/dev.yaml +++ b/.github/workflows/dev.yaml @@ -49,59 +49,10 @@ jobs: - name: Install dependencies if: steps.npm-cache.outputs.cache-hit != 'true' run: npm ci - - name: Build - run: npm run test:functional:build:int - - uses: saucelabs/sauce-connect-action@v2 - with: - username: ${{ secrets.SAUCE_USERNAME }} - accessKey: ${{ secrets.SAUCE_ACCESS_KEY }} - name: Run Unit Test - run: npx karma start karma.saucelabs.conf.cjs --single-run + run: npm run test env: BUILD_NUMBER: $GITHUB_RUN_NUMBER BUILD_ID: $GITHUB_RUN_ID JOB_NUMBER: $GITHUB_JOB - alloy-int-e2e: - name: "Dev Functional Test - Saucectl" - runs-on: ubuntu-latest - needs: unit-test - timeout-minutes: 60 - steps: - - name: Set up Node.js version - uses: actions/setup-node@v4 - with: - node-version: 22 - - - uses: actions/checkout@v4 - - - name: Clear cache - uses: actions/cache@v4 - id: npm-cache - with: - path: "**/node_modules" - key: ${{ runner.os }}-modules-${{ hashFiles('**/package-lock.json') }}-${{ secrets.NPM_CACHE_VERSION }} # increment NPM_CACHE_VERSION secret to force cache reset - - - name: Store Alloy version into env - uses: nyaa8/package-version@v1 - - - name: Install dependencies - if: steps.npm-cache.outputs.cache-hit != 'true' - run: npm ci - - - name: Build - run: npm run test:functional:build:int - env: - ALLOY_PROD_VERSION: ${{ env.PACKAGE_VERSION }} - - - uses: saucelabs/saucectl-run-action@v4.3.0 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - sauce-username: ${{ secrets.SAUCE_USERNAME }} - sauce-access-key: ${{ secrets.SAUCE_ACCESS_KEY }} - env: | - ALLOY_PROD_VERSION=${{ env.PACKAGE_VERSION }} - NPM_PACKAGE_VERSION=${{ env.PACKAGE_VERSION }} - ALLOY_ENV=int - config-file: ./.sauce/dev.yml \ No newline at end of file diff --git a/package.json b/package.json index d5c00028c..a15a14b02 100644 --- a/package.json +++ b/package.json @@ -21,15 +21,13 @@ "clean": "rimraf dist distTest libEs5 libEs6", "lint": "eslint --cache --fix \"*.{js,cjs,mjs,jsx}\" \"{src,test,vtest,scripts}/**/*.{js,cjs,mjs,jsx}\"", "format": "prettier --write \"*.{html,js,cjs,mjs,jsx}\" \"{sandbox,src,test,scripts,vtest}/**/*.{html,js,cjs,mjs,jsx}\"", - "test": "npm run test:unit && npm run test:scripts && npm run test:functional", - "test:unit": "karma start karma.conf.cjs --single-run", - "test:unit:debug": "karma start karma.conf.cjs --browsers=Chrome --single-run=false --debug", - "test:unit:watch": "karma start", - "test:unit:saucelabs:local": "karma start karma.saucelabs.conf.cjs --single-run", - "test:unit:coverage": "karma start --single-run --reporters spec,coverage", + "test": "npm run test:unit && npm run test:scripts", + "test:unit": "vitest run", + "test:unit:debug": "vitest --no-file-parallelism --browser.headless=false", + "test:unit:watch": "vitest", + "test:unit:coverage": "vitest run --coverage", "test:functional": "EDGE_BASE_PATH=\"ee-pre-prd\" ALLOY_ENV=\"int\" testcafe chrome", "test:functional:custom": "node scripts/helpers/runFunctionalTests.js", - "test:functional:saucelabs:dev": "NPM_PACKAGE_VERSION=$(npm pkg get version | tr -d '\"') EDGE_BASE_PATH=\"ee-pre-prd\" ALLOY_ENV=\"int\" saucectl run --config ./.sauce/dev.yml", "test:functional:watch": "EDGE_BASE_PATH=\"ee-pre-prd\" ALLOY_ENV=\"int\" ./scripts/watchFunctionalTests.js --browsers chrome", "test:functional:debug": "EDGE_BASE_PATH=\"ee-pre-prd\" ALLOY_ENV=\"int\" testcafe --inspect-brk chrome", "test:functional:build:int": "rollup -c --environment BASE_CODE_MIN,STANDALONE,NPM_PACKAGE_LOCAL", diff --git a/vite.config.js b/vite.config.js index 17533bdc0..3c2975b92 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,13 +1,18 @@ // eslint-disable-next-line import/no-unresolved -import { defineConfig } from "vitest/config"; +import { defineProject } from "vitest/config"; -export default defineConfig({ +export default defineProject({ test: { - name: "happy-dom", + name: "unit-tests", include: ["vtest/**/*.{test,spec}.?(c|m)[jt]s?(x)"], - environment: "happy-dom", isolate: false, pool: "threads", + browser: { + provider: "playwright", + name: "chromium", + enabled: true, + headless: true, + }, coverage: { include: ["src/**/*"], }, diff --git a/vitest.workspace.js b/vitest.workspace.js deleted file mode 100644 index d596ee352..000000000 --- a/vitest.workspace.js +++ /dev/null @@ -1,36 +0,0 @@ -// eslint-disable-next-line import/no-unresolved -import { defineWorkspace } from "vitest/config"; - -const filesForBrowser = [ - "vtest/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js", - "vtest/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js", - "vtest/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js", - "vtest/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js", - "vtest/unit/specs/utils/dom/selectNodesWithShadow.spec.js", - "vtest/unit/specs/utils/updateErrorMessage.spec.js", -]; - -// defineWorkspace provides a nice type hinting DX -export default defineWorkspace([ - { - // add "extends" to merge two configs together - extends: "./vite.config.js", - test: { - exclude: filesForBrowser, - }, - }, - { - test: { - name: "browser", - include: filesForBrowser, - isolate: false, - pool: "threads", - browser: { - name: "chromium", - provider: "playwright", - headless: true, - enabled: true, - }, - }, - }, -]); From 709632823a9f596cabe6db6ddc7e58b9fdcefdc4 Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Wed, 8 Jan 2025 13:29:26 -0700 Subject: [PATCH 06/15] Move scripts tests to vitest. --- package.json | 2 +- scripts/specs/applicationError.spec.js | 5 +++- scripts/specs/createLogger.spec.js | 26 ++++++++++-------- scripts/specs/exec.spec.js | 8 ++++-- scripts/specs/publishTag.spec.js | 28 ++++++++++++------- scripts/specs/publishToNpm.spec.js | 28 +++++++++++-------- scripts/specs/publishVersionBranch.spec.js | 32 +++++++++++++--------- scripts/specs/setupDeployment.spec.js | 18 ++++++------ scripts/specs/updateDevDependency.spec.js | 22 +++++++++------ scripts/specs/updatePackageVersion.spec.js | 14 ++++++---- scripts/specs/uploadToCDN.spec.js | 32 +++++++++++++--------- scripts/specs/vitest.config.js | 12 ++++++++ scripts/specs/withErrorHandling.spec.js | 18 ++++++++---- vite.config.js => vitest.config.js | 1 - 14 files changed, 155 insertions(+), 91 deletions(-) create mode 100644 scripts/specs/vitest.config.js rename vite.config.js => vitest.config.js (95%) diff --git a/package.json b/package.json index a15a14b02..51c1b10b8 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "test:functional:debug": "EDGE_BASE_PATH=\"ee-pre-prd\" ALLOY_ENV=\"int\" testcafe --inspect-brk chrome", "test:functional:build:int": "rollup -c --environment BASE_CODE_MIN,STANDALONE,NPM_PACKAGE_LOCAL", "test:functional:build:prod": "rollup -c --environment BASE_CODE_MIN,NPM_PACKAGE_PROD", - "test:scripts": "jasmine --config=scripts/specs/jasmine.json", + "test:scripts": "vitest run --config=./scripts/specs/vitest.config.js", "sandbox:build": "rollup -c --environment SANDBOX && cd sandbox && npm run build", "dev": "concurrently --names build,sandbox \"rollup -c -w --environment SANDBOX\" \"cd sandbox && export REACT_APP_NONCE=321 && npm start\"", "dev:standalone": "npm run clean && rollup -c -w --environment STANDALONE", diff --git a/scripts/specs/applicationError.spec.js b/scripts/specs/applicationError.spec.js index 9028e42fb..da1e80f25 100644 --- a/scripts/specs/applicationError.spec.js +++ b/scripts/specs/applicationError.spec.js @@ -9,6 +9,8 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + +import { describe, it, expect } from "vitest"; import ApplicationError from "../helpers/applicationError.js"; describe("ApplicationError", () => { @@ -19,11 +21,12 @@ describe("ApplicationError", () => { expect(e.name).toEqual("ApplicationError"); } }); + it("works with instanceof", () => { try { throw new ApplicationError("foo"); } catch (e) { - expect(e instanceof ApplicationError).toBeTrue(); + expect(e instanceof ApplicationError).toBeTruthy(); } }); }); diff --git a/scripts/specs/createLogger.spec.js b/scripts/specs/createLogger.spec.js index fc6a59b52..9d91d199a 100644 --- a/scripts/specs/createLogger.spec.js +++ b/scripts/specs/createLogger.spec.js @@ -9,6 +9,8 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + +import { vi, describe, beforeEach, it, expect } from "vitest"; import chalk from "chalk"; import createLogger from "../helpers/createLogger.js"; @@ -18,40 +20,42 @@ describe("createLogger", () => { const now = new Date(2001, 2, 3, 4, 5, 6, 7); const prefix = chalk.white("[04:05:06.007]"); beforeEach(() => { - myConsole = jasmine.createSpyObj("myConsole", [ - "log", - "info", - "warn", - "error", - ]); + myConsole = { log: vi.fn(), warn: vi.fn(), error: vi.fn(), info: vi.fn() }; logger = createLogger(myConsole, () => now); }); it("adds a prefix", () => { logger.log("mylog"); - expect(myConsole.log).toHaveBeenCalledOnceWith(`${prefix} mylog`); + + expect(myConsole.log).toHaveBeenCalledTimes(1); + expect(myConsole.log).toHaveBeenCalledWith(`${prefix} mylog`); }); it("leaves objects alone", () => { const err = new Error("myerror"); logger.error(err); - expect(myConsole.error).toHaveBeenCalledOnceWith(err); + + expect(myConsole.error).toHaveBeenCalledTimes(1); + expect(myConsole.error).toHaveBeenCalledWith(err); }); it("logs warnings yellow", () => { logger.warn("mywarn"); - expect(myConsole.warn).toHaveBeenCalledOnceWith( + expect(myConsole.warn).toHaveBeenCalledTimes(1); + expect(myConsole.warn).toHaveBeenCalledWith( `${prefix} ${chalk.yellow("mywarn")}`, ); }); it("logs errors red", () => { logger.error("myerror"); - expect(myConsole.error).toHaveBeenCalledOnceWith( + expect(myConsole.error).toHaveBeenCalledWith( `${prefix} ${chalk.red("myerror")}`, ); }); it("logs additional parameters", () => { logger.info("a", "b"); - expect(myConsole.info).toHaveBeenCalledOnceWith(`${prefix} a`, "b"); + + expect(myConsole.info).toHaveBeenCalledTimes(1); + expect(myConsole.info).toHaveBeenCalledWith(`${prefix} a`, "b"); }); }); diff --git a/scripts/specs/exec.spec.js b/scripts/specs/exec.spec.js index dccf0a3cf..9127e3bbe 100644 --- a/scripts/specs/exec.spec.js +++ b/scripts/specs/exec.spec.js @@ -9,6 +9,8 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + +import { describe, it, expect } from "vitest"; import { Writable } from "stream"; import exec from "../helpers/exec.js"; import ApplicationError from "../helpers/applicationError.js"; @@ -29,9 +31,9 @@ const createStringBackedWritableStream = () => { describe("exec", () => { it("throws an ApplicationError on a non-zero exit code.", async () => { const [outputStream] = createStringBackedWritableStream(); - await expectAsync( - exec("bad exit", "exit 42", { outputStream }), - ).toBeRejectedWithError(ApplicationError); + await expect(exec("bad exit", "exit 42", { outputStream })).rejects.toThrow( + ApplicationError, + ); }); it("logs the exit code", async () => { const [outputStream, getResult] = createStringBackedWritableStream(); diff --git a/scripts/specs/publishTag.spec.js b/scripts/specs/publishTag.spec.js index 45e5c97cc..fd2cb97f5 100644 --- a/scripts/specs/publishTag.spec.js +++ b/scripts/specs/publishTag.spec.js @@ -9,6 +9,8 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + +import { vi, describe, beforeEach, it, expect } from "vitest"; import publishTag from "../helpers/publishTag.js"; describe("publishTag", () => { @@ -18,28 +20,34 @@ describe("publishTag", () => { let container; beforeEach(() => { - exec = jasmine.createSpy("exec"); - execSync = jasmine.createSpy("execSync"); - logger = jasmine.createSpyObj("logger", ["warn", "info"]); + exec = vi.fn(); + execSync = vi.fn(); + logger = { warn: vi.fn(), info: vi.fn() }; container = { exec, execSync, logger, version: "1.2.3" }; }); it("doesn't publish a tag", async () => { - execSync.and.returnValue("v1.2.3"); + execSync.mockReturnValue("v1.2.3"); await publishTag(container); - expect(logger.warn).toHaveBeenCalledOnceWith( + + expect(logger.warn).toHaveBeenCalledTimes(1); + expect(logger.warn).toHaveBeenCalledWith( "Git tag v1.2.3 already published.", ); expect(logger.info).not.toHaveBeenCalled(); expect(exec).not.toHaveBeenCalled(); }); it("publishes a tag", async () => { - execSync.and.returnValue(""); - exec.and.returnValue(Promise.resolve(), Promise.resolve()); + execSync.mockReturnValue(""); + exec + .mockReturnValueOnce(Promise.resolve()) + .mockReturnValueOnce(Promise.resolve()); await publishTag(container); - expect(logger.info).toHaveBeenCalledOnceWith("Publishing Git tag v1.2.3."); + + expect(logger.info).toHaveBeenCalledTimes(1); + expect(logger.info).toHaveBeenCalledWith("Publishing Git tag v1.2.3."); expect(logger.warn).not.toHaveBeenCalled(); - expect(exec).toHaveBeenCalledWith("git tag", jasmine.any(String)); - expect(exec).toHaveBeenCalledWith("git push", jasmine.any(String)); + expect(exec).toHaveBeenCalledWith("git tag", expect.any(String)); + expect(exec).toHaveBeenCalledWith("git push", expect.any(String)); }); }); diff --git a/scripts/specs/publishToNpm.spec.js b/scripts/specs/publishToNpm.spec.js index f8cc1c773..029722097 100644 --- a/scripts/specs/publishToNpm.spec.js +++ b/scripts/specs/publishToNpm.spec.js @@ -9,6 +9,8 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + +import { vi, describe, beforeEach, it, expect } from "vitest"; import publishToNpm from "../helpers/publishToNpm.js"; describe("publishToNpm", () => { @@ -20,32 +22,36 @@ describe("publishToNpm", () => { let container; beforeEach(() => { - exec = jasmine.createSpy("exec"); - execSync = jasmine.createSpy("execSync"); - logger = jasmine.createSpyObj("logger", ["warn", "info"]); + exec = vi.fn(); + execSync = vi.fn(); + logger = { warn: vi.fn(), info: vi.fn() }; container = { exec, execSync, logger, npmTag, version }; }); it("publishes to NPM", async () => { - execSync.and.returnValue(""); + execSync.mockReturnValue(""); await publishToNpm(container); - expect(execSync).toHaveBeenCalledOnceWith( + + expect(execSync).toHaveBeenCalledTimes(1); + expect(execSync).toHaveBeenCalledWith( "npm view @adobe/alloy@1.2.3 version --json", ); expect(logger.warn).not.toHaveBeenCalled(); expect(logger.info).toHaveBeenCalledWith("Publishing NPM package."); - expect(exec).toHaveBeenCalledWith("npm publish", jasmine.any(String)); + expect(exec).toHaveBeenCalledWith("npm publish", expect.any(String)); }); it("doesn't publish to NPM", async () => { - execSync.and.returnValue('"1.2.3"'); + execSync.mockReturnValue('"1.2.3"'); await publishToNpm(container); - expect(execSync).toHaveBeenCalledOnceWith( + + expect(execSync).toHaveBeenCalledTimes(1); + expect(execSync).toHaveBeenCalledWith( "npm view @adobe/alloy@1.2.3 version --json", ); - expect(logger.warn).toHaveBeenCalledOnceWith( - "NPM already has version 1.2.3.", - ); + + expect(logger.warn).toHaveBeenCalledTimes(1); + expect(logger.warn).toHaveBeenCalledWith("NPM already has version 1.2.3."); expect(logger.info).not.toHaveBeenCalled(); expect(exec).not.toHaveBeenCalled(); }); diff --git a/scripts/specs/publishVersionBranch.spec.js b/scripts/specs/publishVersionBranch.spec.js index b60677c25..987bd7673 100644 --- a/scripts/specs/publishVersionBranch.spec.js +++ b/scripts/specs/publishVersionBranch.spec.js @@ -9,6 +9,8 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + +import { vi, describe, beforeEach, it, expect } from "vitest"; import publishVersionBranch from "../helpers/publishVersionBranch.js"; describe("publishVersionBranch", () => { @@ -18,16 +20,18 @@ describe("publishVersionBranch", () => { let container; beforeEach(() => { - exec = jasmine.createSpy("exec"); - execSync = jasmine.createSpy("execSync"); - logger = jasmine.createSpyObj("logger", ["warn", "info"]); + exec = vi.fn(); + execSync = vi.fn(); + logger = { warn: vi.fn(), info: vi.fn() }; container = { exec, execSync, logger, version: "1.2.3" }; }); it("doesn't publish a prerelease branch", async () => { container.version = "1.2.3-beta.0"; await publishVersionBranch(container); - expect(logger.info).toHaveBeenCalledOnceWith( + + expect(logger.info).toHaveBeenCalledTimes(1); + expect(logger.info).toHaveBeenCalledWith( "No need to create a test branch for a prerelease version.", ); expect(logger.warn).not.toHaveBeenCalled(); @@ -36,9 +40,11 @@ describe("publishVersionBranch", () => { }); it("doesn't publish a branch that was already published", async () => { - execSync.and.returnValue("v1.2.3"); + execSync.mockReturnValue("v1.2.3"); await publishVersionBranch(container); - expect(logger.warn).toHaveBeenCalledOnceWith( + + expect(logger.warn).toHaveBeenCalledTimes(1); + expect(logger.warn).toHaveBeenCalledWith( "Git branch v1.2.3 already published.", ); expect(logger.info).not.toHaveBeenCalled(); @@ -46,14 +52,14 @@ describe("publishVersionBranch", () => { }); it("publishes a branch", async () => { - execSync.and.returnValue(""); - exec.and.returnValue(Promise.resolve(), Promise.resolve()); + execSync.mockReturnValue(""); + exec.mockReturnValue(Promise.resolve(), Promise.resolve()); await publishVersionBranch(container); - expect(logger.info).toHaveBeenCalledOnceWith( - "Publishing Git branch v1.2.3.", - ); + + expect(logger.info).toHaveBeenCalledTimes(1); + expect(logger.info).toHaveBeenCalledWith("Publishing Git branch v1.2.3."); expect(logger.warn).not.toHaveBeenCalled(); - expect(exec).toHaveBeenCalledWith("git branch", jasmine.any(String)); - expect(exec).toHaveBeenCalledWith("git push", jasmine.any(String)); + expect(exec).toHaveBeenCalledWith("git branch", expect.any(String)); + expect(exec).toHaveBeenCalledWith("git push", expect.any(String)); }); }); diff --git a/scripts/specs/setupDeployment.spec.js b/scripts/specs/setupDeployment.spec.js index e7eaa003d..5bb66d6df 100644 --- a/scripts/specs/setupDeployment.spec.js +++ b/scripts/specs/setupDeployment.spec.js @@ -9,6 +9,8 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + +import { vi, describe, beforeEach, it, expect } from "vitest"; import setupDeployment from "../helpers/setupDeployment.js"; describe("setupDeployment", () => { @@ -20,8 +22,8 @@ describe("setupDeployment", () => { let container; beforeEach(() => { - exec = jasmine.createSpy("exec"); - logger = jasmine.createSpyObj("logger", ["info"]); + exec = vi.fn(); + logger = { info: vi.fn() }; container = { exec, githubActor, @@ -37,16 +39,16 @@ describe("setupDeployment", () => { expect(logger.info).toHaveBeenCalled(); // make sure all the container parameters are defined expect(exec).toHaveBeenCalledWith( - jasmine.anything(), - jasmine.stringMatching(/myactor/), + expect.anything(), + expect.stringMatching(/myactor/), ); expect(exec).toHaveBeenCalledWith( - jasmine.anything(), - jasmine.stringMatching(/myrepo/), + expect.anything(), + expect.stringMatching(/myrepo/), ); expect(exec).toHaveBeenCalledWith( - jasmine.anything(), - jasmine.stringMatching(/mytoken/), + expect.anything(), + expect.stringMatching(/mytoken/), ); }); }); diff --git a/scripts/specs/updateDevDependency.spec.js b/scripts/specs/updateDevDependency.spec.js index fe4903d14..70f98fcaf 100644 --- a/scripts/specs/updateDevDependency.spec.js +++ b/scripts/specs/updateDevDependency.spec.js @@ -9,6 +9,8 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + +import { vi, describe, beforeEach, it, expect } from "vitest"; import updateDevDependency from "../helpers/updateDevDependency.js"; describe("updateDevDependency", () => { @@ -20,35 +22,39 @@ describe("updateDevDependency", () => { let container; beforeEach(() => { - exec = jasmine.createSpy("exec"); - execSync = jasmine.createSpy("execSync"); - logger = jasmine.createSpyObj("logger", ["warn", "info"]); + exec = vi.fn(); + execSync = vi.fn(); + logger = { warn: vi.fn(), info: vi.fn() }; container = { exec, execSync, githubRef, logger, version }; }); it("installs the dev dependency", async () => { - execSync.and.returnValue( + execSync.mockReturnValue( JSON.stringify({ dependencies: { "@adobe/alloy": { version: "1.2.2" } }, }), ); - exec.and.returnValue(Promise.resolve()); + exec.mockReturnValue(Promise.resolve()); await updateDevDependency(container); expect(logger.warn).not.toHaveBeenCalled(); - expect(logger.info).toHaveBeenCalledOnceWith( + + expect(logger.info).toHaveBeenCalledTimes(1); + expect(logger.info).toHaveBeenCalledWith( "Installing @adobe/alloy@1.2.3 as a dev dependency.", ); expect(exec).toHaveBeenCalledTimes(4); }); it("doesn't install the dev dependency", async () => { - execSync.and.returnValue( + execSync.mockReturnValue( JSON.stringify({ dependencies: { "@adobe/alloy": { version: "1.2.3" } }, }), ); await updateDevDependency(container); - expect(logger.warn).toHaveBeenCalledOnceWith( + + expect(logger.warn).toHaveBeenCalledTimes(1); + expect(logger.warn).toHaveBeenCalledWith( "Dependency @adobe/alloy@1.2.3 already installed.", ); expect(logger.info).not.toHaveBeenCalled(); diff --git a/scripts/specs/updatePackageVersion.spec.js b/scripts/specs/updatePackageVersion.spec.js index cfdd9aa7f..ed7304b08 100644 --- a/scripts/specs/updatePackageVersion.spec.js +++ b/scripts/specs/updatePackageVersion.spec.js @@ -9,6 +9,8 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + +import { vi, describe, beforeEach, it, expect } from "vitest"; import updatePackageVersion from "../helpers/updatePackageVersion.js"; describe("updatePackageVersion", () => { @@ -19,16 +21,16 @@ describe("updatePackageVersion", () => { let container; beforeEach(() => { - exec = jasmine.createSpy("exec"); - exec.and.returnValue(Promise.resolve()); - logger = jasmine.createSpyObj("logger", ["warn", "info"]); + exec = vi.fn().mockReturnValue(Promise.resolve()); + logger = { warn: vi.fn(), info: vi.fn() }; container = { exec, githubRef, logger, version }; }); it("updates the package version", async () => { await updatePackageVersion({ currentVersion: "1.2.2", ...container }); expect(logger.warn).not.toHaveBeenCalled(); - expect(logger.info).toHaveBeenCalledOnceWith( + expect(logger.info).toHaveBeenCalledTimes(1); + expect(logger.info).toHaveBeenCalledWith( "Updating package.json with version 1.2.3.", ); expect(exec).toHaveBeenCalledTimes(5); @@ -36,7 +38,9 @@ describe("updatePackageVersion", () => { it("doesn't update the package version", async () => { await updatePackageVersion({ currentVersion: "1.2.3", ...container }); - expect(logger.warn).toHaveBeenCalledOnceWith( + + expect(logger.warn).toHaveBeenCalledTimes(1); + expect(logger.warn).toHaveBeenCalledWith( "Version in package.json is already 1.2.3.", ); expect(logger.info).not.toHaveBeenCalled(); diff --git a/scripts/specs/uploadToCDN.spec.js b/scripts/specs/uploadToCDN.spec.js index 022793a46..23efcfa48 100644 --- a/scripts/specs/uploadToCDN.spec.js +++ b/scripts/specs/uploadToCDN.spec.js @@ -9,6 +9,8 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + +import { vi, describe, beforeEach, it, expect } from "vitest"; import ApplicationError from "../helpers/applicationError.js"; import uploadToCDN from "../helpers/uploadToCDN.js"; @@ -20,15 +22,15 @@ describe("uploadToCDN", () => { let container; beforeEach(() => { - exec = jasmine.createSpy("exec"); - exec.and.returnValue(Promise.resolve()); - logger = jasmine.createSpyObj("logger", ["info"]); - urlExists = jasmine.createSpy("urlExists"); + exec = vi.fn(); + exec.mockReturnValue(Promise.resolve()); + logger = { info: vi.fn() }; + urlExists = vi.fn(); container = { exec, logger, urlExists, version }; }); it("uploads to CDN", async () => { - urlExists.and.returnValue(Promise.resolve(true)); + urlExists.mockReturnValue(Promise.resolve(true)); await uploadToCDN(container); expect(logger.info).toHaveBeenCalledWith("Building files for CDN"); expect(exec).toHaveBeenCalledWith("build", "npm run build"); @@ -44,16 +46,20 @@ describe("uploadToCDN", () => { "https://cdn1.adoberesources.net/alloy/1.2.3/alloy.min.js", ); }); + it("fails to upload min file to CDN", async () => { - urlExists.and.returnValues([Promise.resolve(false), Promise.resolve(true)]); - await expectAsync(uploadToCDN(container)).toBeRejectedWithError( - ApplicationError, - ); + urlExists + .mockReturnValueOnce(Promise.resolve(false)) + .mockReturnValueOnce(Promise.resolve(true)); + + await expect(uploadToCDN(container)).rejects.toThrow(ApplicationError); }); + it("fails to upload regular file to CDN", async () => { - urlExists.and.returnValues([Promise.resolve(true), Promise.resolve(false)]); - await expectAsync(uploadToCDN(container)).toBeRejectedWithError( - ApplicationError, - ); + urlExists + .mockReturnValueOnce(Promise.resolve(true)) + .mockReturnValueOnce(Promise.resolve(false)); + + await expect(uploadToCDN(container)).rejects.toThrow(ApplicationError); }); }); diff --git a/scripts/specs/vitest.config.js b/scripts/specs/vitest.config.js new file mode 100644 index 000000000..dd955f623 --- /dev/null +++ b/scripts/specs/vitest.config.js @@ -0,0 +1,12 @@ +// eslint-disable-next-line import/no-unresolved +import { defineProject } from "vitest/config"; + +export default defineProject({ + test: { + name: "scripts-tests", + include: ["scripts/specs/*.{test,spec}.?(c|m)[jt]s?(x)"], + isolate: false, + pool: "threads", + environment: "happy-dom", + }, +}); diff --git a/scripts/specs/withErrorHandling.spec.js b/scripts/specs/withErrorHandling.spec.js index 837d827d7..cac68b756 100644 --- a/scripts/specs/withErrorHandling.spec.js +++ b/scripts/specs/withErrorHandling.spec.js @@ -9,6 +9,8 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + +import { vi, describe, beforeEach, it, expect } from "vitest"; import ApplicationError from "../helpers/applicationError.js"; import withErrorHandling from "../helpers/withErrorHandling.js"; @@ -19,14 +21,14 @@ describe("withErrorHandling", () => { let func; beforeEach(() => { - logger = jasmine.createSpyObj("logger", ["info", "error"]); - process = jasmine.createSpyObj("process", ["exit"]); + logger = { info: vi.fn(), error: vi.fn() }; + process = { exit: vi.fn() }; container = { logger, process }; - func = jasmine.createSpy("func"); + func = vi.fn(); }); it("runs without failure", async () => { - func.and.returnValue(Promise.resolve()); + func.mockReturnValue(Promise.resolve()); await withErrorHandling(container, "Deploy", func); expect(logger.info).toHaveBeenCalledWith("Deploy."); expect(func).toHaveBeenCalled(); @@ -34,7 +36,9 @@ describe("withErrorHandling", () => { }); it("handles ApplicationErrors", async () => { - func.and.throwError(new ApplicationError("myerrormessage")); + func.mockImplementationOnce(() => { + throw new ApplicationError("myerrormessage"); + }); await withErrorHandling(container, "Deploy", func); expect(logger.info).toHaveBeenCalledWith("Deploy."); expect(logger.error).toHaveBeenCalledWith("Deploy FAILED."); @@ -43,7 +47,9 @@ describe("withErrorHandling", () => { }); it("handles unexpected errors", async () => { const error = new Error("myerrormessage"); - func.and.throwError(error); + func.mockImplementationOnce(() => { + throw error; + }); await withErrorHandling(container, "Deploy", func); expect(logger.info).toHaveBeenCalledWith("Deploy."); expect(logger.error).toHaveBeenCalledWith("Deploy FAILED."); diff --git a/vite.config.js b/vitest.config.js similarity index 95% rename from vite.config.js rename to vitest.config.js index 3c2975b92..cd31557ab 100644 --- a/vite.config.js +++ b/vitest.config.js @@ -6,7 +6,6 @@ export default defineProject({ name: "unit-tests", include: ["vtest/**/*.{test,spec}.?(c|m)[jt]s?(x)"], isolate: false, - pool: "threads", browser: { provider: "playwright", name: "chromium", From bd30ba08b7c1a768c60df3aa8e58ba0517677a0d Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Wed, 8 Jan 2025 13:34:52 -0700 Subject: [PATCH 07/15] Remove karma and jasmine. Remove karma config. --- .sauceignore | 12 - karma.conf.cjs | 87 - karma.saucelabs.conf.cjs | 74 - package-lock.json | 11429 +++++++++++++---------------------- package.json | 19 - sauceLabsCapabilities.json | 6 - scripts/specs/jasmine.json | 7 - 7 files changed, 4201 insertions(+), 7433 deletions(-) delete mode 100644 .sauceignore delete mode 100644 karma.conf.cjs delete mode 100644 karma.saucelabs.conf.cjs delete mode 100644 sauceLabsCapabilities.json delete mode 100644 scripts/specs/jasmine.json diff --git a/.sauceignore b/.sauceignore deleted file mode 100644 index 9be88b5a4..000000000 --- a/.sauceignore +++ /dev/null @@ -1,12 +0,0 @@ -# This file instructs saucectl to not package any files mentioned here. -.git/ -.github/ -.DS_Store -.hg/ -.vscode/ -.idea/ -.gitignore -.hgignore -.npmrc -*.gif -sandbox/ \ No newline at end of file diff --git a/karma.conf.cjs b/karma.conf.cjs deleted file mode 100644 index 5e441fa81..000000000 --- a/karma.conf.cjs +++ /dev/null @@ -1,87 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -const rollupConfig = require("./rollup.test.config.cjs"); - -module.exports = (config) => { - config.set({ - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: "", - // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ["jasmine"], - - // list of files / patterns to load in the browser - files: [ - { - pattern: "test/unit/specs/karmaEntry.spec.cjs", - watched: false, // The preprocessor will use its own watcher - }, - ], - - // list of files to exclude - exclude: [], - - // preprocess matching files before serving them to the browser - // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: { - "test/unit/specs/karmaEntry.spec.cjs": ["rollup"], - }, - - // test results reporter to use - // possible values: "dots", "progress" - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ["dots"], - - // web server port - port: 9876, - - // enable / disable colors in the output (reporters and logs) - colors: true, - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_INFO, - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: true, - - // start these browsers - // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ["ChromeHeadlessNoSandbox"], - customLaunchers: { - ChromeHeadlessNoSandbox: { - base: "ChromeHeadless", - flags: ["--no-sandbox"], - }, - }, - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - singleRun: false, - - // Concurrency level - // how many browser should be started simultaneous - concurrency: Infinity, - - coverageReporter: { - reporters: [ - { type: "html" }, - { type: "lcovonly", subdir: ".", file: "lcov.dat" }, - ], - }, - - captureTimeout: 180000, - browserDisconnectTimeout: 180000, - browserDisconnectTolerance: 3, - browserNoActivityTimeout: 300000, - - rollupPreprocessor: rollupConfig, - }); -}; diff --git a/karma.saucelabs.conf.cjs b/karma.saucelabs.conf.cjs deleted file mode 100644 index 4929ebcaf..000000000 --- a/karma.saucelabs.conf.cjs +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -const karmaSauceLauncher = require("karma-sauce-launcher"); -const karmaConfig = require("./karma.conf.cjs"); - -module.exports = (config) => { - karmaConfig(config); - const customLaunchers = { - sl_chromeW3C: { - base: "SauceLabs", - browserName: "chrome", - browserVersion: "latest", - platformName: "Windows 11", - "sauce:options": { - tunnelIdentifier: process.env.SAUCE_TUNNEL_ID, - headless: true, - }, - }, - sl_safariW3C: { - base: "SauceLabs", - browserName: "safari", - browserVersion: "latest", - platformName: "macOS 13", - "sauce:options": { - tunnelIdentifier: process.env.SAUCE_TUNNEL_ID, - headless: true, - }, - }, - sl_firefoxW3C: { - base: "SauceLabs", - browserName: "firefox", - browserVersion: "latest", - platformName: "Windows 11", - "sauce:options": { - tunnelIdentifier: process.env.SAUCE_TUNNEL_ID, - headless: true, - }, - }, - }; - - config.set({ - browsers: Object.keys(customLaunchers), - customLaunchers, - concurrency: 10, - colors: true, - sauceLabs: { - testName: "Alloy Unit Tests", - build: `GH #${process.env.BUILD_NUMBER} (${process.env.BUILD_ID})`, - tunnelIdentifier: process.env.SAUCE_TUNNEL_ID, - screenResolution: "1280x1024", - recordVideo: false, - recordScreenshots: false, - }, - plugins: [ - "karma-jasmine", - "karma-coverage", - "karma-jasmine-matchers", - "karma-spec-reporter", - "karma-rollup-preprocessor", - karmaSauceLauncher, - ], - - reporters: ["dots", "saucelabs"], - }); -}; diff --git a/package-lock.json b/package-lock.json index 042a14983..3ed2ae7e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,21 +61,7 @@ "handlebars": "^4.7.8", "happy-dom": "^15.11.1", "husky": "^9.1.6", - "jasmine": "^5.4.0", - "jasmine-core": "^5.4.0", "jsdom": "^25.0.1", - "karma": "^6.4.4", - "karma-chrome-launcher": "^3.2.0", - "karma-coverage": "^2.2.1", - "karma-firefox-launcher": "^2.1.3", - "karma-jasmine": "^5.1.0", - "karma-jasmine-html-reporter": "^2.1.0", - "karma-jasmine-matchers": "^5.0.0", - "karma-junit-reporter": "^2.0.1", - "karma-rollup-preprocessor": "^7.0.8", - "karma-safari-launcher": "^1.0.0", - "karma-sauce-launcher": "^4.3.6", - "karma-spec-reporter": "0.0.36", "lint-staged": "^15.2.10", "playwright": "^1.49.1", "prettier": "^3.3.3", @@ -2097,16 +2083,6 @@ "node": ">= 4.0.0" } }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/@devexpress/bin-v8-flags-filter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@devexpress/bin-v8-flags-filter/-/bin-v8-flags-filter-1.3.0.tgz", @@ -3517,6 +3493,8 @@ } ], "license": "CC-BY-4.0", + "optional": true, + "peer": true, "dependencies": { "spacetrim": "0.11.59" } @@ -3527,6 +3505,8 @@ "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "debug": "4.3.4", "extract-zip": "2.0.1", @@ -3549,6 +3529,8 @@ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ms": "2.1.2" }, @@ -3566,7 +3548,9 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@puppeteer/browsers/node_modules/tar-fs": { "version": "3.0.4", @@ -3574,6 +3558,8 @@ "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", @@ -3586,6 +3572,8 @@ "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", @@ -4027,13 +4015,6 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "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" - }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -4152,7 +4133,9 @@ "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@types/aria-query": { "version": "5.0.4", @@ -4181,16 +4164,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -4290,7 +4263,9 @@ "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@types/ws": { "version": "8.5.13", @@ -4298,6 +4273,8 @@ "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/node": "*" } @@ -4309,6 +4286,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@types/node": "*" } @@ -4687,6 +4665,8 @@ "integrity": "sha512-mWEiBbaC7CgxvSd2/ozpbZWebnRIc8KRu/J81Hlw/txUWio27S7IpXBlZGVvhEsNzq0+cuxB/8gDkkXvMPbesw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/node": "^22.2.0" }, @@ -4754,6 +4734,8 @@ "integrity": "sha512-qMrJVg2hoEsZJjMJez9yI2+nZlBUxgYzGV3mqcb2B/6T1ihXp0fWBDYlVHlHquuorgNUQP5a8qSmX6HF5rFJNg==", "dev": true, "license": "BSD-3-Clause", + "optional": true, + "peer": true, "engines": { "bun": ">=0.7.0", "deno": ">=1.0.0", @@ -4766,6 +4748,8 @@ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -4773,20 +4757,6 @@ "node": ">=6.5" } }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -4825,13 +4795,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/add-matchers": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/add-matchers/-/add-matchers-0.6.2.tgz", - "integrity": "sha512-hVO2wodMei9RF00qe+506MoeJ/NEOdCMEkSJ12+fC3hx/5Z4zmhNiP92nJEF6XhmXokeB0hOtuQrjHCx2vmXrQ==", - "dev": true, - "license": "MIT" - }, "node_modules/address": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/address/-/address-2.0.3.tgz", @@ -4945,6 +4908,7 @@ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "license": "ISC", + "optional": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -4959,6 +4923,7 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", + "optional": true, "engines": { "node": ">=8.6" }, @@ -4973,56 +4938,14 @@ "dev": true, "license": "ISC" }, - "node_modules/arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/archive-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/archive-type/-/archive-type-4.0.0.tgz", - "integrity": "sha512-zV4Ky0v1F8dBrdYElwTvQhweQ0P7Kwc1aluqJsYtOBP01jXcWCyW2IEfI1YiqsG+Iy7ZR+o5LF1N+PGECBxHWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "file-type": "^4.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/archive-type/node_modules/file-type": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz", - "integrity": "sha512-f2UbFQEk7LXgWpi5ntcO86OeA/cC80fuDDDaX/fZ2ZGel+AF7leRQqBBW1eJNiiQkrZlAoM6P+VYP5P6bOlDEQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/archiver": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "archiver-utils": "^5.0.2", "async": "^3.2.4", @@ -5042,6 +4965,8 @@ "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "glob": "^10.0.0", "graceful-fs": "^4.2.0", @@ -5061,6 +4986,8 @@ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -5085,6 +5012,8 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -5096,6 +5025,8 @@ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -5117,6 +5048,8 @@ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" }, @@ -5130,6 +5063,8 @@ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "license": "BlueOak-1.0.0", + "optional": true, + "peer": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -5145,7 +5080,9 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/archiver-utils/node_modules/minimatch": { "version": "9.0.5", @@ -5153,6 +5090,8 @@ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -5169,6 +5108,8 @@ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "license": "BlueOak-1.0.0", + "optional": true, + "peer": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -5186,6 +5127,8 @@ "integrity": "sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", @@ -5216,7 +5159,9 @@ "url": "https://feross.org/support" } ], - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/archiver-utils/node_modules/string_decoder": { "version": "1.3.0", @@ -5224,6 +5169,8 @@ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -5248,6 +5195,8 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -5259,6 +5208,8 @@ "integrity": "sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", @@ -5289,7 +5240,9 @@ "url": "https://feross.org/support" } ], - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/archiver/node_modules/string_decoder": { "version": "1.3.0", @@ -5297,6 +5250,8 @@ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -5307,6 +5262,8 @@ "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", @@ -5543,6 +5500,8 @@ "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.0.1" }, @@ -5624,7 +5583,9 @@ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "optional": true, + "peer": true }, "node_modules/babel-plugin-module-resolver": { "version": "5.0.0", @@ -5781,7 +5742,8 @@ "integrity": "sha512-KSdMqLj1ZERZMP1PTmnLK7SqJu9z9/SbwUUPZly2puMtfVcytC+jl6mb/9XYiqq0PXcx1rNDS+Qvl1g54Lho6A==", "dev": true, "license": "Apache-2.0", - "optional": true + "optional": true, + "peer": true }, "node_modules/bare-fs": { "version": "2.3.5", @@ -5790,6 +5752,7 @@ "dev": true, "license": "Apache-2.0", "optional": true, + "peer": true, "dependencies": { "bare-events": "^2.0.0", "bare-path": "^2.0.0", @@ -5802,7 +5765,8 @@ "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", "dev": true, "license": "Apache-2.0", - "optional": true + "optional": true, + "peer": true }, "node_modules/bare-path": { "version": "2.1.3", @@ -5811,6 +5775,7 @@ "dev": true, "license": "Apache-2.0", "optional": true, + "peer": true, "dependencies": { "bare-os": "^2.1.0" } @@ -5822,6 +5787,7 @@ "dev": true, "license": "Apache-2.0", "optional": true, + "peer": true, "dependencies": { "streamx": "^2.21.0" } @@ -5847,22 +5813,14 @@ ], "license": "MIT" }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, "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", + "optional": true, + "peer": true, "engines": { "node": ">=10.0.0" } @@ -5890,381 +5848,302 @@ "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", "dev": true, "license": "Unlicense", + "optional": true, + "peer": true, "engines": { "node": ">=0.6" } }, - "node_modules/bin-check": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", - "integrity": "sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==", + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "execa": "^0.7.0", - "executable": "^4.1.0" + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/bin-check/node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "node": "*" } }, - "node_modules/bin-check/node_modules/execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "license": "MIT", - "dependencies": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, + "optional": true, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bin-check/node_modules/get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/bin-check/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/bin-check/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "license": "ISC", + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/bin-check/node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { - "path-key": "^2.0.0" + "fill-range": "^7.1.1" }, "engines": { - "node": ">=4" - } - }, - "node_modules/bin-check/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/bin-check/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "node_modules/brotli-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-0.1.0.tgz", + "integrity": "sha512-5ny7BNvpe2TSmdafF1T9dnFYp3AIrJ8qJt29K0DQJzORlK38LBim/CmlY26JtreV6SWmXza7Oa+9m61SzvxR0Q==", "dev": true, "license": "MIT", "dependencies": { - "shebang-regex": "^1.0.0" + "duplexer": "^0.1.1", + "iltorb": "^2.4.3" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/bin-check/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "node": ">=0.12.0" } }, - "node_modules/bin-check/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/bin-check/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/bin-check/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true, - "license": "ISC" - }, - "node_modules/bin-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-3.1.0.tgz", - "integrity": "sha512-Mkfm4iE1VFt4xd4vH+gx+0/71esbfus2LsnCGe8Pi4mndSPyT+NGES/Eg99jx8/lUGWfu3z2yuB/bt5UB+iVbQ==", - "dev": true, + "node_modules/browserslist": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", + "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "execa": "^1.0.0", - "find-versions": "^3.0.0" + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/bin-version-check": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-4.0.0.tgz", - "integrity": "sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bin-version": "^3.0.0", - "semver": "^5.6.0", - "semver-truncate": "^1.1.2" + "bin": { + "browserslist": "cli.js" }, "engines": { - "node": ">=6" - } - }, - "node_modules/bin-version-check/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/bin-version/node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, - "node_modules/bin-version/node_modules/execa": { + "node_modules/buffer-crc32": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", "dev": true, "license": "MIT", - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, + "optional": true, + "peer": true, "engines": { - "node": ">=6" + "node": ">=8.0.0" } }, - "node_modules/bin-version/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", "dev": true, "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, + "optional": true, + "peer": true, "engines": { - "node": ">=6" + "node": ">=0.10" } }, - "node_modules/bin-version/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", "dev": true, - "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=0.10.0" + "node": ">=0.2.0" } }, - "node_modules/bin-version/node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "node_modules/bundlesize": { + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/bundlesize/-/bundlesize-0.18.2.tgz", + "integrity": "sha512-wthRURckcAbe0Qcr7xMH8evVE/kjID8gqY0M17XJI/FVgCljLx6Ag4lIDbV76KVb2Ey5iCA4n5Fur61TEhF1JQ==", "dev": true, "license": "MIT", "dependencies": { - "path-key": "^2.0.0" + "axios": "^1.6.2", + "brotli-size": "0.1.0", + "bytes": "^3.1.0", + "ci-env": "^1.4.0", + "commander": "^2.20.0", + "cosmiconfig": "^5.2.1", + "github-build": "^1.2.4", + "glob": "^7.1.4", + "gzip-size": "^4.0.0", + "prettycli": "^1.4.3" }, - "engines": { - "node": ">=4" + "bin": { + "bundlesize": "index.js", + "bundlesize-init": "src/init-status.js", + "bundlesize-pipe": "pipe.js" } }, - "node_modules/bin-version/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "node_modules/bundlesize/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/bin-version/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/bundlesize/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", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/bin-version/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "license": "MIT", "dependencies": { - "shebang-regex": "^1.0.0" + "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": ">=0.10.0" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/bin-version/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/bin-version/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/bin-version/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" + "node": ">= 0.8" } }, - "node_modules/bin-wrapper": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bin-wrapper/-/bin-wrapper-4.1.0.tgz", - "integrity": "sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q==", + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "dev": true, "license": "MIT", - "dependencies": { - "bin-check": "^4.1.0", - "bin-version-check": "^4.0.0", - "download": "^7.1.0", - "import-lazy": "^3.1.0", - "os-filter-obj": "^2.0.0", - "pify": "^4.0.1" - }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/bin-wrapper/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=10.6.0" } }, - "node_modules/binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "dev": true, "license": "MIT", "dependencies": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/cacheable-request/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==", "dev": true, "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, "engines": { "node": ">=8" }, @@ -6272,122 +6151,105 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bl": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", - "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, "license": "MIT", "dependencies": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true, - "license": "MIT" + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", "dev": true, "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">= 0.4" } }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.0.0" + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/body-parser/node_modules/ms": { + "node_modules/caller-callsite": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/boolean": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "license": "MIT" - }, - "node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", "dev": true, "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" + "caller-callsite": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/brotli-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-0.1.0.tgz", - "integrity": "sha512-5ny7BNvpe2TSmdafF1T9dnFYp3AIrJ8qJt29K0DQJzORlK38LBim/CmlY26JtreV6SWmXza7Oa+9m61SzvxR0Q==", + "node_modules/callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", "dev": true, "license": "MIT", - "dependencies": { - "duplexer": "^0.1.1", - "iltorb": "^2.4.3" - }, "engines": { - "node": ">=0.12.0" + "node": ">=4" } }, - "node_modules/browserslist": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", - "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", + "node_modules/caniuse-lite": { + "version": "1.0.30001690", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", + "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", "funding": [ { "type": "opencollective", @@ -6395,722 +6257,695 @@ }, { "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "CC-BY-4.0" + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/chai": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", + "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "dev": true, "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" + "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": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">=12" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", + "license": "MIT/X11", + "optional": true, + "peer": true, "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" } }, - "node_modules/buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, "license": "MIT", - "dependencies": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "license": "MIT" }, - "node_modules/buffer-crc32": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", - "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.0.0" + "node": ">= 16" } }, - "node_modules/buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/buffer-indexof-polyfill": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", - "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "node_modules/check-more-types": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">= 0.8.0" } }, - "node_modules/buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, "engines": { - "node": ">=0.2.0" + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/bundlesize": { - "version": "0.18.2", - "resolved": "https://registry.npmjs.org/bundlesize/-/bundlesize-0.18.2.tgz", - "integrity": "sha512-wthRURckcAbe0Qcr7xMH8evVE/kjID8gqY0M17XJI/FVgCljLx6Ag4lIDbV76KVb2Ey5iCA4n5Fur61TEhF1JQ==", + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC" + }, + "node_modules/chrome-remote-interface": { + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.32.2.tgz", + "integrity": "sha512-3UbFKtEmqApehPQnqdblcggx7KveQphEMKQmdJZsOguE9ylw2N2/9Z7arO7xS55+DBJ/hyP8RrayLt4MMdJvQg==", "dev": true, "license": "MIT", "dependencies": { - "axios": "^1.6.2", - "brotli-size": "0.1.0", - "bytes": "^3.1.0", - "ci-env": "^1.4.0", - "commander": "^2.20.0", - "cosmiconfig": "^5.2.1", - "github-build": "^1.2.4", - "glob": "^7.1.4", - "gzip-size": "^4.0.0", - "prettycli": "^1.4.3" + "commander": "2.11.x", + "ws": "^7.2.0" }, "bin": { - "bundlesize": "index.js", - "bundlesize-init": "src/init-status.js", - "bundlesize-pipe": "pipe.js" + "chrome-remote-interface": "bin/client.js" } }, - "node_modules/bundlesize/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "node_modules/chrome-remote-interface/node_modules/commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", "dev": true, "license": "MIT" }, - "node_modules/bundlesize/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", + "node_modules/chrome-remote-interface/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": "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" - }, + "license": "MIT", "engines": { - "node": "*" + "node": ">=8.3.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/chromium-bidi": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.8.tgz", + "integrity": "sha512-blqh+1cEQbHBKmok3rVJkBlBxt9beKBgOsxbFgs7UJcoVbbeZ+K7+6liAsjgpc8l1Xd55cQUy14fXZdGSb4zIw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "mitt": "3.0.1", + "urlpattern-polyfill": "10.0.0" + }, + "peerDependencies": { + "devtools-protocol": "*" } }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "node_modules/ci-env": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/ci-env/-/ci-env-1.17.0.tgz", + "integrity": "sha512-NtTjhgSEqv4Aj90TUYHQLxHdnCPXnjdtuGG1X8lTfp/JqeXTdw0FTWl/vUAPuvbWZTF8QVpv6ASe/XacE+7R2A==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "node_modules/ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, "license": "MIT", "engines": { - "node": ">=10.6.0" + "node": ">=6" } }, - "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cacheable-request/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/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", "dev": true, "license": "MIT", "dependencies": { - "pump": "^3.0.0" + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/call-bound": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "get-intrinsic": "^1.2.6" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "callsites": "^2.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { - "caller-callsite": "^2.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": "*" + "node": ">=7.0.0" } }, - "node_modules/callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "node_modules/cliui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "license": "MIT", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001690", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", - "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" + "license": "MIT" }, - "node_modules/capital-case": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", - "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "node_modules/cliui/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" + "engines": { + "node": ">=8" } }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/caw": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", - "integrity": "sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==", + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { - "get-proxy": "^2.0.0", - "isurl": "^1.0.0-alpha5", - "tunnel-agent": "^0.6.0", - "url-to-options": "^1.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/chai": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", - "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "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" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=12" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, - "license": "MIT/X11", + "license": "MIT", "dependencies": { - "traverse": ">=0.3.0 <0.4" + "mimic-response": "^1.0.0" }, - "engines": { - "node": "*" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", "dev": true, "license": "MIT", "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node": ">=0.10.0" + } + }, + "node_modules/coffeescript": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.7.0.tgz", + "integrity": "sha512-hzWp6TUE2d/jCcN67LrW1eh5b/rSDKQK6oD6VMLlggYVUUFexgTH9z3dNYihzX4RMhze5FTUsUmOXViJKFQR/A==", + "dev": true, + "license": "MIT", + "bin": { + "cake": "bin/cake", + "coffee": "bin/coffee" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "engines": { + "node": ">=6" } }, - "node_modules/change-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", - "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "license": "MIT", "dependencies": { - "camel-case": "^4.1.2", - "capital-case": "^1.0.4", - "constant-case": "^3.0.4", - "dot-case": "^3.0.4", - "header-case": "^2.0.4", - "no-case": "^3.0.4", - "param-case": "^3.0.4", - "pascal-case": "^3.1.2", - "path-case": "^3.0.4", - "sentence-case": "^3.0.4", - "snake-case": "^3.0.4", - "tslib": "^2.0.3" + "color-name": "1.1.3" } }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, "license": "MIT" }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, "engines": { - "node": ">= 16" + "node": ">= 0.8" } }, - "node_modules/check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", - "dev": true, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">=18" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "node_modules/commenting": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/commenting/-/commenting-1.1.0.tgz", + "integrity": "sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA==", + "license": "MIT" + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "license": "MIT" + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": ">= 14" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true, - "license": "ISC" - }, - "node_modules/chrome-remote-interface": { - "version": "0.32.2", - "resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.32.2.tgz", - "integrity": "sha512-3UbFKtEmqApehPQnqdblcggx7KveQphEMKQmdJZsOguE9ylw2N2/9Z7arO7xS55+DBJ/hyP8RrayLt4MMdJvQg==", + "node_modules/compress-commons/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "commander": "2.11.x", - "ws": "^7.2.0" - }, - "bin": { - "chrome-remote-interface": "bin/client.js" + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/chrome-remote-interface/node_modules/commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/chrome-remote-interface/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "node_modules/compress-commons/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/chromium-bidi": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.8.tgz", - "integrity": "sha512-blqh+1cEQbHBKmok3rVJkBlBxt9beKBgOsxbFgs7UJcoVbbeZ+K7+6liAsjgpc8l1Xd55cQUy14fXZdGSb4zIw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "mitt": "3.0.1", - "urlpattern-polyfill": "10.0.0" + "node": ">=8" }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, - "node_modules/ci-env": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/ci-env/-/ci-env-1.17.0.tgz", - "integrity": "sha512-NtTjhgSEqv4Aj90TUYHQLxHdnCPXnjdtuGG1X8lTfp/JqeXTdw0FTWl/vUAPuvbWZTF8QVpv6ASe/XacE+7R2A==", - "dev": true, - "license": "MIT" - }, - "node_modules/ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", - "dev": true, - "license": "MIT" - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.6.0.tgz", + "integrity": "sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "restore-cursor": "^5.0.0" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/cli-truncate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", - "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "node_modules/compress-commons/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "optional": true, + "peer": true }, - "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "node_modules/compress-commons/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "optional": true, + "peer": true, + "dependencies": { + "safe-buffer": "~5.2.0" } }, - "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "node_modules/concurrently": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.2.tgz", + "integrity": "sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" + "chalk": "^4.1.2", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" }, "engines": { "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, - "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/concurrently/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "license": "ISC", - "engines": { - "node": ">= 12" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/concurrently/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/cliui/node_modules/color-convert": { + "node_modules/concurrently/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", @@ -7123,186 +6958,111 @@ "node": ">=7.0.0" } }, - "node_modules/cliui/node_modules/color-name": { + "node_modules/concurrently/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true, "license": "MIT" }, - "node_modules/cliui/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", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "ISC" }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">= 0.6" } }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dev": true, + "node_modules/core-js-compat": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", + "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", "license": "MIT", "dependencies": { - "mimic-response": "^1.0.0" + "browserslist": "^4.24.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/coffeescript": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.7.0.tgz", - "integrity": "sha512-hzWp6TUE2d/jCcN67LrW1eh5b/rSDKQK6oD6VMLlggYVUUFexgTH9z3dNYihzX4RMhze5FTUsUmOXViJKFQR/A==", - "dev": true, - "license": "MIT", - "bin": { - "cake": "bin/cake", - "coffee": "bin/coffee" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true, "license": "MIT" }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", "dev": true, "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=4" } }, - "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "license": "MIT", + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "bin": { + "crc32": "bin/crc32.njs" + }, "engines": { - "node": ">=18" + "node": ">=0.8" } }, - "node_modules/commenting": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/commenting/-/commenting-1.1.0.tgz", - "integrity": "sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA==", - "license": "MIT" - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "license": "MIT" - }, - "node_modules/compress-commons": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", - "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "crc-32": "^1.2.0", - "crc32-stream": "^6.0.0", - "is-stream": "^2.0.1", - "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" }, "engines": { "node": ">= 14" } }, - "node_modules/compress-commons/node_modules/buffer": { + "node_modules/crc32-stream/node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", @@ -7322,30 +7082,21 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, - "node_modules/compress-commons/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/compress-commons/node_modules/readable-stream": { + "node_modules/crc32-stream/node_modules/readable-stream": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.6.0.tgz", "integrity": "sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", @@ -7357,7 +7108,7 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/compress-commons/node_modules/safe-buffer": { + "node_modules/crc32-stream/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", @@ -7376,5035 +7127,3156 @@ "url": "https://feross.org/support" } ], - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, - "node_modules/compress-commons/node_modules/string_decoder": { + "node_modules/crc32-stream/node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/concurrently": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.2.tgz", - "integrity": "sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==", + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "chalk": "^4.1.2", - "lodash": "^4.17.21", - "rxjs": "^7.8.1", - "shell-quote": "^1.8.1", - "supports-color": "^8.1.1", - "tree-kill": "^1.2.2", - "yargs": "^17.7.2" - }, - "bin": { - "conc": "dist/bin/concurrently.js", - "concurrently": "dist/bin/concurrently.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + "node-fetch": "^2.6.12" } }, - "node_modules/concurrently/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 8" } }, - "node_modules/concurrently/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/crypto-md5": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-md5/-/crypto-md5-1.0.0.tgz", + "integrity": "sha512-65Mtei8+EkSIK+5Ie4gpWXoJ/5bgpqPXFknHHXAyhDqKsEAAzUslGd8mOeawbfcuQ8fADNKcF4xQA3fqlZJ8Ig==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "BSD", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "iojs": ">=1.0.0", + "node": ">=0.5.2" } }, - "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/css-shorthand-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.2.tgz", + "integrity": "sha512-C2AugXIpRGQTxaCW0N7n5jD/p5irUmCrwl03TrnMFBHDbdq44CFWR2zO7rK9xPN4Eo3pUxC4vQzQgbIpzrD1PQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/css-value": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz", + "integrity": "sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", + "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "rrweb-cssom": "^0.7.1" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/concurrently/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "assert-plus": "^1.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.10" } }, - "node_modules/concurrently/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "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", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" + "optional": true, + "peer": true, + "engines": { + "node": ">= 14" } }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true, - "license": "MIT" - }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "dev": true, "license": "MIT", "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" }, "engines": { - "node": ">= 0.10.0" + "node": ">=18" } }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/data-urls/node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, "license": "MIT", - "dependencies": { - "ms": "2.0.0" + "engines": { + "node": ">=18" } }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/constant-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", - "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case": "^2.0.2" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "5.2.1" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" } }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, "engines": { - "node": ">= 0.6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.6" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" } }, - "node_modules/core-js": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", - "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", - "dev": true, - "hasInstallScript": true, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/core-js-compat": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", - "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", + "node_modules/decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true, "license": "MIT", - "dependencies": { - "browserslist": "^4.24.2" + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true, "license": "MIT" }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, "license": "MIT", "dependencies": { - "object-assign": "^4", - "vary": "^1" + "mimic-response": "^3.1.0" }, "engines": { - "node": ">= 0.10" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, "license": "MIT", - "dependencies": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" - }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "node_modules/dedent": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.4.0.tgz", + "integrity": "sha512-25DJIXD6mCqYHIqI3/aBfAvFgJSY9jIx397eUQSofXbWVR4lcB21a17qQ5Bswj0Zv+3Nf06zNCyfkGyvo0AqqQ==", "dev": true, - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } + "license": "MIT" }, - "node_modules/crc32-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", - "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, "license": "MIT", - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^4.0.0" - }, "engines": { - "node": ">= 14" - } - }, - "node_modules/crc32-stream/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "node": ">=6" } }, - "node_modules/crc32-stream/node_modules/readable-stream": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.6.0.tgz", - "integrity": "sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw==", + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=4.0.0" } }, - "node_modules/crc32-stream/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "license": "MIT" }, - "node_modules/crc32-stream/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, + "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==", "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "node_modules/deepmerge-ts": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz", + "integrity": "sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==", "dev": true, - "license": "MIT", - "dependencies": { - "node-fetch": "^2.6.12" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, + "license": "BSD-3-Clause", + "optional": true, + "peer": true, "engines": { - "node": ">= 8" + "node": ">=16.0.0" } }, - "node_modules/crypto-md5": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-md5/-/crypto-md5-1.0.0.tgz", - "integrity": "sha512-65Mtei8+EkSIK+5Ie4gpWXoJ/5bgpqPXFknHHXAyhDqKsEAAzUslGd8mOeawbfcuQ8fADNKcF4xQA3fqlZJ8Ig==", + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, - "license": "BSD", + "license": "MIT", "engines": { - "iojs": ">=1.0.0", - "node": ">=0.5.2" + "node": ">=10" } }, - "node_modules/css-shorthand-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.2.tgz", - "integrity": "sha512-C2AugXIpRGQTxaCW0N7n5jD/p5irUmCrwl03TrnMFBHDbdq44CFWR2zO7rK9xPN4Eo3pUxC4vQzQgbIpzrD1PQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/css-value": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz", - "integrity": "sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==", - "dev": true - }, - "node_modules/css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "license": "MIT" - }, - "node_modules/cssstyle": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", - "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "license": "MIT", "dependencies": { - "rrweb-cssom": "^0.7.1" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", - "dev": true, - "license": "MIT" - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "license": "MIT", "dependencies": { - "assert-plus": "^1.0.0" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=0.10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "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", + "optional": true, + "peer": true, + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, "engines": { "node": ">= 14" } }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "node_modules/del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha512-7yjqSoVSlJzA4t/VUwazuEagGeANEKB3f/aNI//06pfKgwoCb7f6Q1gETN1sZzYaj6chTQ0AhIwDiPdfOjko4A==", "dev": true, "license": "MIT", "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" }, "engines": { - "node": ">=18" + "node": ">=4" } }, - "node_modules/data-urls/node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "node_modules/del/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", "dev": true, "license": "MIT", + "dependencies": { + "array-uniq": "^1.0.1" + }, "engines": { - "node": ">=18" + "node": ">=0.10.0" } }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "node_modules/del/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": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" + "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": ">= 0.4" + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/inspect-js" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "node_modules/del/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/date-fns": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", - "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" + "node": ">=0.10.0" } }, - "node_modules/date-format": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", - "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "node_modules/del/node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">=0.10.0" } }, - "node_modules/debounce": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "node_modules/del/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", + "license": "ISC", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" + "glob": "^7.1.3" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "bin": { + "rimraf": "bin.js" } }, - "node_modules/decamelize": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", - "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, "license": "MIT", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.4.0" } }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "dev": true, "license": "MIT" }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">=6" } }, - "node_modules/decompress": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", - "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", "dev": true, "license": "MIT", "dependencies": { - "decompress-tar": "^4.0.0", - "decompress-tarbz2": "^4.0.0", - "decompress-targz": "^4.0.0", - "decompress-unzip": "^4.0.1", - "graceful-fs": "^4.1.10", - "make-dir": "^1.0.0", - "pify": "^2.3.0", - "strip-dirs": "^2.0.0" - }, - "engines": { - "node": ">=4" + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "node_modules/desired-capabilities": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/desired-capabilities/-/desired-capabilities-0.1.0.tgz", + "integrity": "sha512-MNcwZi1elX2YXbTUfs1R6A6RD5Ns3loTljRBu6FGNHY3LPFLVHzTg2tNLlZEpWVZdHQ0cVeQRHrCBdrnW/n1wA==", "dev": true, - "license": "MIT", + "license": "CC0", "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "extend": "^3.0.0" } }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=0.10" } }, - "node_modules/decompress-tar": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", - "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "node_modules/device-specs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/device-specs/-/device-specs-1.0.1.tgz", + "integrity": "sha512-rxns/NDZfbdYumnn801z9uo8kWIz3Eld7Bk/F0V9zw4sZemSoD93+gxHEonLdxYulkws4iCMt7ZP8zuM8EzUSg==", "dev": true, - "license": "MIT", - "dependencies": { - "file-type": "^5.2.0", - "is-stream": "^1.1.0", - "tar-stream": "^1.5.2" - }, - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/decompress-tar/node_modules/file-type": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", - "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", + "node_modules/devtools-protocol": { + "version": "0.0.1109433", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1109433.tgz", + "integrity": "sha512-w1Eqih66egbSr2eOoGZ+NsdF7HdxmKDo3pKFBySEGsmVvwWWNXzNCDcKrbFnd23Jf7kH1M806OfelXwu+Jk11g==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "license": "BSD-3-Clause" }, - "node_modules/decompress-tar/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "engines": { - "node": ">=0.10.0" + "node": ">=0.3.1" } }, - "node_modules/decompress-tarbz2": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", - "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "license": "MIT", "dependencies": { - "decompress-tar": "^4.1.0", - "file-type": "^6.1.0", - "is-stream": "^1.1.0", - "seek-bzip": "^1.0.5", - "unbzip2-stream": "^1.0.9" + "path-type": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/decompress-tarbz2/node_modules/file-type": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", - "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", - "dev": true, - "license": "MIT", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, "engines": { - "node": ">=4" + "node": ">=6.0.0" } }, - "node_modules/decompress-tarbz2/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "dev": true, + "license": "BSD-2-Clause", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/decompress-targz": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", - "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, "license": "MIT", "dependencies": { - "decompress-tar": "^4.1.1", - "file-type": "^5.2.0", - "is-stream": "^1.1.0" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": ">=4" + "node": ">= 0.4" } }, - "node_modules/decompress-targz/node_modules/file-type": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", - "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "peer": true, + "dependencies": { + "readable-stream": "^2.0.2" } }, - "node_modules/decompress-targz/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, - "node_modules/decompress-unzip": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", - "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==", + "node_modules/edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "file-type": "^3.8.0", - "get-stream": "^2.2.0", - "pify": "^2.3.0", - "yauzl": "^2.4.2" + "@types/which": "^2.0.1", + "which": "^2.0.2" }, "engines": { - "node": ">=4" + "node": ">=14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/shirshak55" } }, - "node_modules/decompress-unzip/node_modules/file-type": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "node_modules/edgedriver": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.6.1.tgz", + "integrity": "sha512-3Ve9cd5ziLByUdigw6zovVeWJjVs8QHVmqOB0sJ0WNeVPcwf4p18GnxMmVvlFmYRloUwf5suNuorea4QzwBIOA==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "optional": true, + "peer": true, + "dependencies": { + "@wdio/logger": "^8.38.0", + "@zip.js/zip.js": "^2.7.48", + "decamelize": "^6.0.0", + "edge-paths": "^3.0.5", + "fast-xml-parser": "^4.4.1", + "node-fetch": "^3.3.2", + "which": "^4.0.0" + }, + "bin": { + "edgedriver": "bin/edgedriver.js" } }, - "node_modules/decompress-unzip/node_modules/get-stream": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", - "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", + "node_modules/edgedriver/node_modules/@wdio/logger": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz", + "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "object-assign": "^4.0.1", - "pinkie-promise": "^2.0.0" + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=0.10.0" + "node": "^16.13 || >=18" } }, - "node_modules/decompress-unzip/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "node_modules/edgedriver/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/decompress/node_modules/make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "node_modules/edgedriver/node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "dev": true, "license": "MIT", - "dependencies": { - "pify": "^3.0.0" - }, + "optional": true, + "peer": true, "engines": { - "node": ">=4" + "node": ">= 12" } }, - "node_modules/decompress/node_modules/make-dir/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "node_modules/edgedriver/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, - "license": "MIT", + "license": "ISC", + "optional": true, + "peer": true, "engines": { - "node": ">=4" + "node": ">=16" } }, - "node_modules/decompress/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "node_modules/edgedriver/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, "engines": { - "node": ">=0.10.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, - "node_modules/dedent": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.4.0.tgz", - "integrity": "sha512-25DJIXD6mCqYHIqI3/aBfAvFgJSY9jIx397eUQSofXbWVR4lcB21a17qQ5Bswj0Zv+3Nf06zNCyfkGyvo0AqqQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "node_modules/edgedriver/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "node_modules/edgedriver/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, - "license": "MIT", + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, "engines": { - "node": ">=4.0.0" + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "license": "MIT" + "node_modules/electron-to-chromium": { + "version": "1.5.77", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.77.tgz", + "integrity": "sha512-AnJSrt5JpRVgY6dgd5yccguLc5A7oMSF0Kt3fcW+Hp5WTuFbl5upeSFZbMZYy2o7jhmIhU8Ekrd82GhyXUqUUg==", + "license": "ISC" }, - "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==", + "node_modules/elegant-spinner": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", + "integrity": "sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/deepmerge-ts": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz", - "integrity": "sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==", + "node_modules/email-validator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz", + "integrity": "sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==", "dev": true, - "license": "BSD-3-Clause", "engines": { - "node": ">=16.0.0" + "node": ">4.0" } }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "node_modules/emittery": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.4.1.tgz", + "integrity": "sha512-r4eRSeStEGf6M5SKdrQhhLK5bOwOBxQhIE3YSTnZE3GpKiLfnnhE+tPtrJE79+eDJgm39BM6LSoI8SCx4HbwlQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=6" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "once": "^1.4.0" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">= 0.4" + "node": ">=0.12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "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==", + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true, "license": "MIT", - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, "engines": { - "node": ">= 14" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha512-7yjqSoVSlJzA4t/VUwazuEagGeANEKB3f/aNI//06pfKgwoCb7f6Q1gETN1sZzYaj6chTQ0AhIwDiPdfOjko4A==", + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "license": "MIT", "dependencies": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" - }, - "engines": { - "node": ">=4" + "is-arrayish": "^0.2.1" } }, - "node_modules/del/node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", "dev": true, "license": "MIT", "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" + "stackframe": "^1.3.4" } }, - "node_modules/del/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", + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", "dev": true, - "license": "ISC", + "license": "MIT", "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" + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" }, "engines": { - "node": "*" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/del/node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", - "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/del/node_modules/globby/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/del/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } + "license": "MIT" }, - "node_modules/delayed-stream": { + "node_modules/es-object-atoms": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "dev": true, "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">= 0.4" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "hasown": "^2.0.0" } }, - "node_modules/des.js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", - "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/desired-capabilities": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/desired-capabilities/-/desired-capabilities-0.1.0.tgz", - "integrity": "sha512-MNcwZi1elX2YXbTUfs1R6A6RD5Ns3loTljRBu6FGNHY3LPFLVHzTg2tNLlZEpWVZdHQ0cVeQRHrCBdrnW/n1wA==", + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, - "license": "CC0", - "dependencies": { - "extend": "^3.0.0" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true, - "license": "MIT" - }, - "node_modules/device-specs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/device-specs/-/device-specs-1.0.1.tgz", - "integrity": "sha512-rxns/NDZfbdYumnn801z9uo8kWIz3Eld7Bk/F0V9zw4sZemSoD93+gxHEonLdxYulkws4iCMt7ZP8zuM8EzUSg==", - "dev": true, - "license": "MIT" - }, - "node_modules/devtools-protocol": { - "version": "0.0.1109433", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1109433.tgz", - "integrity": "sha512-w1Eqih66egbSr2eOoGZ+NsdF7HdxmKDo3pKFBySEGsmVvwWWNXzNCDcKrbFnd23Jf7kH1M806OfelXwu+Jk11g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", - "dev": true, - "license": "MIT" - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "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", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true, - "license": "MIT" - }, - "node_modules/dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/download": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/download/-/download-7.1.0.tgz", - "integrity": "sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "archive-type": "^4.0.0", - "caw": "^2.0.1", - "content-disposition": "^0.5.2", - "decompress": "^4.2.0", - "ext-name": "^5.0.0", - "file-type": "^8.1.0", - "filenamify": "^2.0.0", - "get-stream": "^3.0.0", - "got": "^8.3.1", - "make-dir": "^1.2.0", - "p-event": "^2.1.0", - "pify": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/download/node_modules/@sindresorhus/is": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", - "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/download/node_modules/cacheable-request": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", - "integrity": "sha512-vag0O2LKZ/najSoUwDbVlnlCFvhBE/7mGTY2B5FgCBDcRD+oVV1HYTOwM6JZfMg/hIcM6IwnTZ1uQQL5/X3xIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-response": "1.0.2", - "get-stream": "3.0.0", - "http-cache-semantics": "3.8.1", - "keyv": "3.0.0", - "lowercase-keys": "1.0.0", - "normalize-url": "2.0.1", - "responselike": "1.0.2" - } - }, - "node_modules/download/node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", - "integrity": "sha512-RPlX0+PHuvxVDZ7xX+EBVAp4RsVxP/TdDSN2mJYdiq1Lc4Hz7EUSjUI7RZrKKlmrIzVhf6Jo2stj7++gVarS0A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/download/node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^1.0.0" - } - }, - "node_modules/download/node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/download/node_modules/get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/download/node_modules/got": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", - "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^0.7.0", - "cacheable-request": "^2.1.1", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "into-stream": "^3.1.0", - "is-retry-allowed": "^1.1.0", - "isurl": "^1.0.0-alpha5", - "lowercase-keys": "^1.0.0", - "mimic-response": "^1.0.0", - "p-cancelable": "^0.4.0", - "p-timeout": "^2.0.1", - "pify": "^3.0.0", - "safe-buffer": "^5.1.1", - "timed-out": "^4.0.1", - "url-parse-lax": "^3.0.0", - "url-to-options": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/download/node_modules/http-cache-semantics": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/download/node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/download/node_modules/keyv": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", - "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.0" - } - }, - "node_modules/download/node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/download/node_modules/make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/download/node_modules/normalize-url": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", - "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "prepend-http": "^2.0.0", - "query-string": "^5.0.1", - "sort-keys": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/download/node_modules/p-cancelable": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", - "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/download/node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "lowercase-keys": "^1.0.0" - } - }, - "node_modules/download/node_modules/sort-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", - "integrity": "sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-obj": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true, - "license": "MIT" - }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/duplexer3": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", - "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/edge-paths": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", - "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/which": "^2.0.1", - "which": "^2.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/shirshak55" - } - }, - "node_modules/edgedriver": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.6.1.tgz", - "integrity": "sha512-3Ve9cd5ziLByUdigw6zovVeWJjVs8QHVmqOB0sJ0WNeVPcwf4p18GnxMmVvlFmYRloUwf5suNuorea4QzwBIOA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@wdio/logger": "^8.38.0", - "@zip.js/zip.js": "^2.7.48", - "decamelize": "^6.0.0", - "edge-paths": "^3.0.5", - "fast-xml-parser": "^4.4.1", - "node-fetch": "^3.3.2", - "which": "^4.0.0" - }, - "bin": { - "edgedriver": "bin/edgedriver.js" - } - }, - "node_modules/edgedriver/node_modules/@wdio/logger": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz", - "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^5.1.2", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": "^16.13 || >=18" - } - }, - "node_modules/edgedriver/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/edgedriver/node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/edgedriver/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16" - } - }, - "node_modules/edgedriver/node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/edgedriver/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/edgedriver/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.77", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.77.tgz", - "integrity": "sha512-AnJSrt5JpRVgY6dgd5yccguLc5A7oMSF0Kt3fcW+Hp5WTuFbl5upeSFZbMZYy2o7jhmIhU8Ekrd82GhyXUqUUg==", - "license": "ISC" - }, - "node_modules/elegant-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/email-validator": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz", - "integrity": "sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==", - "dev": true, - "engines": { - "node": ">4.0" - } - }, - "node_modules/emittery": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.4.1.tgz", - "integrity": "sha512-r4eRSeStEGf6M5SKdrQhhLK5bOwOBxQhIE3YSTnZE3GpKiLfnnhE+tPtrJE79+eDJgm39BM6LSoI8SCx4HbwlQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", - "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "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/ent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.2.tgz", - "integrity": "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "punycode": "^1.4.1", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "stackframe": "^1.3.4" - } - }, - "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-regex": "^1.2.1", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true, - "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==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", - "dev": true, - "license": "MIT", - "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.2" - } - }, - "node_modules/eslint-config-airbnb-base/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", - "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-ban": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-ban/-/eslint-plugin-ban-1.6.0.tgz", - "integrity": "sha512-gZptoV+SFHOHO57/5lmPvizMvSXrjFatP9qlVQf3meL/WHo9TxSoERygrMlESl19CPh95U86asTxohT8OprwDw==", - "dev": true, - "license": "ISC", - "dependencies": { - "requireindex": "~1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", - "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.8", - "array.prototype.findlastindex": "^1.2.5", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.0", - "hasown": "^2.0.2", - "is-core-module": "^2.15.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.0", - "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.8", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", - "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.9.1" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": "*", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-testcafe": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-testcafe/-/eslint-plugin-testcafe-0.2.1.tgz", - "integrity": "sha512-LZMHQ2kHFXzbt6ZSS2yUOQhr8QaHwaqvmra1EnXKK0qEwpAvegLdjntCbRPtuD6bDGxPFG87Y7mkI3S9TjZA4A==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "license": "Apache-2.0", - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/esotope-hammerhead": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/esotope-hammerhead/-/esotope-hammerhead-0.6.8.tgz", - "integrity": "sha512-2Zhg0c6NfrNA4QT5s4+QG5WJQtq3Se7GonNwtNwfr7sVIo/7L8rirPfh9yyloEmDA7R0yPgD10teFxhf2vWyIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "0.0.46" - } - }, - "node_modules/esotope-hammerhead/node_modules/@types/estree": { - "version": "0.0.46", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.46.tgz", - "integrity": "sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==", - "dev": true, - "license": "MIT" - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "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==", - "license": "MIT" - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", - "dev": true, - "license": "MIT", - "dependencies": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true, - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/executable/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true, - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, - "node_modules/expect-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", - "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/ext-list": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", - "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "^1.28.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ext-name": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", - "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ext-list": "^2.0.0", - "sort-keys-length": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "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" + "esbuild": "bin/esbuild" }, "engines": { - "node": ">= 10.17.0" + "node": ">=12" }, "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, - "node_modules/fast-content-type-parse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", - "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "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", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "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", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "license": "MIT" - }, - "node_modules/fast-xml-parser": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz", - "integrity": "sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", - "license": "ISC", - "dependencies": { - "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/fdir": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", - "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, - "node_modules/fetch-blob": { + "node_modules/escalade": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, "engines": { - "node": "^12.20 || >= 14.13" + "node": ">=6" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "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==", "license": "MIT", - "dependencies": { - "flat-cache": "^3.0.4" - }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/file-type": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz", - "integrity": "sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ==", + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, "engines": { - "node": ">=6" + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" } }, - "node_modules/filename-reserved-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", - "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", + "optional": true, + "peer": true, "engines": { - "node": ">=4" + "node": ">=4.0" } }, - "node_modules/filenamify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz", - "integrity": "sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==", - "dev": true, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", "dependencies": { - "filename-reserved-regex": "^2.0.0", - "strip-outer": "^1.0.0", - "trim-repeated": "^1.0.0" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", "dev": true, "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" }, "engines": { - "node": ">=8" + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" } }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "node_modules/eslint-config-airbnb-base/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" + "bin": { + "eslint-config-prettier": "bin/cli.js" }, - "engines": { - "node": ">= 0.8" + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.0.0" + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } }, - "node_modules/finalhandler/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, "license": "MIT", "dependencies": { - "ee-first": "1.1.1" + "debug": "^3.2.7" }, "engines": { - "node": ">= 0.8" + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/find-babel-config": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-2.1.2.tgz", - "integrity": "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg==", + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", "dependencies": { - "json5": "^2.2.3" + "ms": "^2.1.1" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "license": "MIT", + "node_modules/eslint-plugin-ban": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-ban/-/eslint-plugin-ban-1.6.0.tgz", + "integrity": "sha512-gZptoV+SFHOHO57/5lmPvizMvSXrjFatP9qlVQf3meL/WHo9TxSoERygrMlESl19CPh95U86asTxohT8OprwDw==", + "dev": true, + "license": "ISC", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "requireindex": "~1.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/find-versions": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", - "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, "license": "MIT", "dependencies": { - "semver-regex": "^2.0.0" + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" }, "engines": { - "node": ">=6" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "ms": "^2.1.1" } }, - "node_modules/flat-cache/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", - "license": "ISC", + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", "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" + "esutils": "^2.0.2" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.10.0" } }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "semver": "bin/semver.js" } }, - "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, "engines": { - "node": ">=4.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" }, "peerDependenciesMeta": { - "debug": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { "optional": true } } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/eslint-plugin-testcafe": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testcafe/-/eslint-plugin-testcafe-0.2.1.tgz", + "integrity": "sha512-LZMHQ2kHFXzbt6ZSS2yUOQhr8QaHwaqvmra1EnXKK0qEwpAvegLdjntCbRPtuD6bDGxPFG87Y7mkI3S9TjZA4A==", "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.3" - } + "license": "MIT" }, - "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "dev": true, - "license": "ISC", + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8.0.0" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "license": "Apache-2.0", "engines": { - "node": "*" + "node": ">=10" } }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "dev": true, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, "engines": { - "node": ">= 6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/form-data-encoder": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", - "dev": true, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">= 14.17" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dev": true, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "license": "MIT", "dependencies": { - "fetch-blob": "^3.1.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=12.20.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", - "dev": true, - "license": "MIT" - }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", - "dev": true, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "license": "MIT", + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "license": "BSD-2-Clause", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "license": "Apache-2.0", "engines": { - "node": ">=6 <7 || >=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=4.0" } }, - "node_modules/fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "deprecated": "This package is no longer supported.", - "dev": true, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "license": "ISC", "dependencies": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=0.6" + "node": ">=10.13.0" } }, - "node_modules/fstream/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", + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "license": "MIT", "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" + "type-fest": "^0.20.2" }, "engines": { - "node": "*" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fstream/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", "dependencies": { - "glob": "^7.1.3" + "argparse": "^2.0.1" }, "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", - "dev": true, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "license": "MIT", + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", - "deprecated": "This package is no longer supported.", + "node_modules/esotope-hammerhead": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/esotope-hammerhead/-/esotope-hammerhead-0.6.8.tgz", + "integrity": "sha512-2Zhg0c6NfrNA4QT5s4+QG5WJQtq3Se7GonNwtNwfr7sVIo/7L8rirPfh9yyloEmDA7R0yPgD10teFxhf2vWyIw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" + "@types/estree": "0.0.46" } }, - "node_modules/gauge/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "node_modules/esotope-hammerhead/node_modules/@types/estree": { + "version": "0.0.46", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.46.tgz", + "integrity": "sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/gauge/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", - "dev": true, - "license": "MIT", + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "license": "BSD-2-Clause", "dependencies": { - "number-is-nan": "^1.0.0" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": ">=0.10.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/gauge/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/gauge/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "license": "Apache-2.0", "engines": { - "node": ">=0.10.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/geckodriver": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.2.1.tgz", - "integrity": "sha512-4m/CRk0OI8MaANRuFIahvOxYTSjlNAO2p9JmE14zxueknq6cdtB5M9UGRQ8R9aMV0bLGNVHHDnDXmoXdOwJfWg==", - "dev": true, - "hasInstallScript": true, - "license": "MPL-2.0", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "license": "BSD-3-Clause", "dependencies": { - "@wdio/logger": "^8.11.0", - "decamelize": "^6.0.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "node-fetch": "^3.3.1", - "tar-fs": "^3.0.4", - "unzipper": "^0.10.14", - "which": "^4.0.0" - }, - "bin": { - "geckodriver": "bin/geckodriver.js" + "estraverse": "^5.1.0" }, "engines": { - "node": "^16.13 || >=18 || >=20" + "node": ">=0.10" } }, - "node_modules/geckodriver/node_modules/@wdio/logger": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz", - "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", - "dev": true, - "license": "MIT", + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", "dependencies": { - "chalk": "^5.1.2", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^7.1.0" + "estraverse": "^5.2.0" }, "engines": { - "node": "^16.13 || >=18" + "node": ">=4.0" } }, - "node_modules/geckodriver/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=4.0" } }, - "node_modules/geckodriver/node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "dev": true, - "license": "MIT", + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", "engines": { - "node": ">= 12" + "node": ">=4.0" } }, - "node_modules/geckodriver/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "dev": true, - "license": "ISC", + "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==", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", "engines": { - "node": ">=16" + "node": ">=0.10.0" } }, - "node_modules/geckodriver/node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", "dev": true, "license": "MIT", "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" } }, - "node_modules/geckodriver/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, + "optional": true, + "peer": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=6" } }, - "node_modules/geckodriver/node_modules/tar-fs": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", - "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, "license": "MIT", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" + "optional": true, + "peer": true, + "engines": { + "node": ">=0.8.x" } }, - "node_modules/geckodriver/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/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "license": "MIT", "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/geckodriver/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", "engines": { - "node": ">=6.9.0" + "node": ">=6" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/expect-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", + "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", "dev": true, - "license": "ISC", + "license": "Apache-2.0", "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=12.0.0" } }, - "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true, + "license": "MIT" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=4" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "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": "MIT", + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "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==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "function-bind": "^1.1.2", - "get-proto": "^1.0.0", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" + "pump": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-os-info": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-os-info/-/get-os-info-1.0.2.tgz", - "integrity": "sha512-Nlgt85ph6OHZ4XvTcC8LMLDDFUzf7LAinYJZUwzrnc3WiO+vDEHDmNItTtzixBDLv94bZsvJGrrDRAE6uPs4MQ==", + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true, - "license": "ISC", - "dependencies": { - "getos": "^3.2.1", - "macos-release": "^3.0.1", - "os-family": "^1.1.0", - "windows-release": "^5.0.1" - } + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" }, - "node_modules/get-port": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", - "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "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", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "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", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "optional": true, + "peer": true }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" }, "engines": { - "node": ">= 0.4" + "node": ">=8.6.0" } }, - "node_modules/get-proxy": { + "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", - "integrity": "sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz", + "integrity": "sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "npm-conf": "^1.1.0" + "strnum": "^1.0.5" }, - "engines": { - "node": ">=4" + "bin": { + "fxparser": "src/cli/cli.js" } }, - "node_modules/get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", + "node_modules/fastq": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "license": "ISC", + "dependencies": { + "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", - "engines": { - "node": ">=0.10.0" + "optional": true, + "peer": true, + "dependencies": { + "pend": "~1.2.0" } }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, + "node_modules/fdir": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", "license": "MIT", - "engines": { - "node": ">=16" + "peerDependencies": { + "picomatch": "^3 || ^4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^12.20 || >= 14.13" } }, - "node_modules/get-uri": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", - "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", - "dev": true, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "license": "MIT", "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4" + "flat-cache": "^3.0.4" }, "engines": { - "node": ">= 14" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/getos": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { - "async": "^3.2.0" + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "node_modules/find-babel-config": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-2.1.2.tgz", + "integrity": "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg==", "dev": true, "license": "MIT", "dependencies": { - "assert-plus": "^1.0.0" + "json5": "^2.2.3" } }, - "node_modules/github-build": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/github-build/-/github-build-1.2.4.tgz", - "integrity": "sha512-1kdMmIrvYH18ITHGMVa5BXOxj/+i/VZzPR4PGMBpLW9h15woU+gpM/mlqOk+jmuD4mmib8Dgb6Xcbyy0v+RqqA==", - "dev": true, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "license": "MIT", "dependencies": { - "axios": "1.6.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/github-build/node_modules/axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", - "dev": true, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "dev": true, - "license": "MIT" - }, - "node_modules/glob": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", - "dev": true, + "node_modules/flat-cache/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", "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "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": "20 || >=22" + "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==", - "dev": true, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "license": "ISC", "dependencies": { - "is-glob": "^4.0.1" + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "is-callable": "^1.1.3" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": "20 || >=22" + "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/global-agent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.2.0.tgz", - "integrity": "sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg==", + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true, - "license": "BSD-3-Clause", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dev": true, + "license": "MIT", "dependencies": { - "boolean": "^3.0.1", - "core-js": "^3.6.5", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=10.0" + "node": ">= 6" } }, - "node_modules/globals": { - "version": "15.14.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", - "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 14.17" } }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" + "fetch-blob": "^3.1.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12.20.0" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", "dev": true, + "license": "MIT" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, "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" + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/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", + "optional": true, + "peer": true, + "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": ">=10" + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/globby/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", - "engines": { - "node": ">= 0.4" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, "license": "MIT", "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { - "node": ">=10.19.0" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "license": "MIT" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "license": "MIT" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/graphlib": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", - "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", + "deprecated": "This package is no longer supported.", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "lodash": "^4.17.15" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, - "node_modules/graphql": { - "version": "16.10.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", - "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", + "node_modules/gauge/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + "node": ">=0.10.0" } }, - "node_modules/gzip-size": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-4.1.0.tgz", - "integrity": "sha512-1g6EPVvIHuPmpAdBBpsIVYLgjzGV/QqcFRJXpMyrqEWG10JhOaTjQeCcjMDyX0Iqfm/Q5M9twR/mbDk5f5MqkA==", + "node_modules/gauge/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", "dev": true, "license": "MIT", "dependencies": { - "duplexer": "^0.1.1", - "pify": "^3.0.0" + "number-is-nan": "^1.0.0" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } + "license": "ISC" }, - "node_modules/happy-dom": { - "version": "15.11.7", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-15.11.7.tgz", - "integrity": "sha512-KyrFvnl+J9US63TEzwoiJOQzZBJY7KgBushJA8X61DMbNsH+2ONkDuLDnCnwUiPTF42tLoEmrPyoqbenVA5zrg==", + "node_modules/gauge/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", "dev": true, "license": "MIT", "dependencies": { - "entities": "^4.5.0", - "webidl-conversions": "^7.0.0", - "whatwg-mimetype": "^3.0.0" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" }, "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", + "node_modules/gauge/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" + "ansi-regex": "^2.0.0" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "node_modules/geckodriver": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.2.1.tgz", + "integrity": "sha512-4m/CRk0OI8MaANRuFIahvOxYTSjlNAO2p9JmE14zxueknq6cdtB5M9UGRQ8R9aMV0bLGNVHHDnDXmoXdOwJfWg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" + "hasInstallScript": true, + "license": "MPL-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@wdio/logger": "^8.11.0", + "decamelize": "^6.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.1", + "tar-fs": "^3.0.4", + "unzipper": "^0.10.14", + "which": "^4.0.0" + }, + "bin": { + "geckodriver": "bin/geckodriver.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", "engines": { - "node": ">=8" + "node": "^16.13 || >=18 || >=20" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "node_modules/geckodriver/node_modules/@wdio/logger": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz", + "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "es-define-property": "^1.0.0" + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^7.1.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^16.13 || >=18" } }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "node_modules/geckodriver/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.0" - }, + "optional": true, + "peer": true, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/has-symbol-support-x": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", + "node_modules/geckodriver/node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": "*" + "node": ">= 12" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "node_modules/geckodriver/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, - "license": "MIT", + "license": "ISC", + "optional": true, + "peer": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=16" } }, - "node_modules/has-to-string-tag-x": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "node_modules/geckodriver/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "has-symbol-support-x": "^1.4.1" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" }, "engines": { - "node": "*" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "node_modules/geckodriver/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "has-symbols": "^1.0.3" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "node_modules/geckodriver/node_modules/tar-fs": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", + "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" + "pump": "^3.0.0", + "tar-stream": "^3.1.5" }, - "engines": { - "node": ">= 0.4" + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" } }, - "node_modules/header-case": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", - "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "node_modules/geckodriver/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==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "capital-case": "^1.0.4", - "tslib": "^2.0.3" + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, - "node_modules/headers-polyfill": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", - "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "node_modules/geckodriver/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, - "license": "MIT" + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } }, - "node_modules/highlight-es": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/highlight-es/-/highlight-es-1.0.3.tgz", - "integrity": "sha512-s/SIX6yp/5S1p8aC/NRDC1fwEb+myGIfp8/TzZz0rtAv8fzsdX7vGl3Q1TrXCsczFq8DI3CBFBCySPClfBSdbg==", - "dev": true, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "license": "MIT", - "dependencies": { - "chalk": "^2.4.0", - "is-es2016-keyword": "^1.0.0", - "js-tokens": "^3.0.0" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/highlight-es/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, + "license": "ISC", "engines": { - "node": ">=4" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/highlight-es/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/highlight-es/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==", + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": "*" } }, - "node_modules/highlight-es/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/highlight-es/node_modules/js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "node_modules/get-os-info": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-os-info/-/get-os-info-1.0.2.tgz", + "integrity": "sha512-Nlgt85ph6OHZ4XvTcC8LMLDDFUzf7LAinYJZUwzrnc3WiO+vDEHDmNItTtzixBDLv94bZsvJGrrDRAE6uPs4MQ==", "dev": true, - "license": "MIT" + "license": "ISC", + "dependencies": { + "getos": "^3.2.1", + "macos-release": "^3.0.1", + "os-family": "^1.1.0", + "windows-release": "^5.0.1" + } }, - "node_modules/highlight-es/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, + "optional": true, + "peer": true, "engines": { - "node": ">=4" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, "license": "MIT", "dependencies": { - "whatwg-encoding": "^3.1.1" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=18" + "node": ">= 0.4" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "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==", + "node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", "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" - }, "engines": { - "node": ">= 0.8" + "node": ">=0.10.0" } }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "license": "MIT", "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": ">=8.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/get-uri": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", + "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "agent-base": "^7.1.0", + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4" }, "engines": { "node": ">= 14" } }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "node_modules/getos": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "dev": true, "license": "MIT", "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" + "async": "^3.2.0" } }, - "node_modules/http-status-codes": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", - "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", - "dev": true, - "license": "MIT" - }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "license": "MIT", "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/httpntlm": { - "version": "1.8.13", - "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.8.13.tgz", - "integrity": "sha512-2F2FDPiWT4rewPzNMg3uPhNkP3NExENlUGADRUDPQvuftuUTGW98nLZtGemCIW3G40VhWZYgkIDcQFAwZ3mf2Q==", - "dev": true, - "funding": [ - { - "type": "paypal", - "url": "https://www.paypal.com/donate/?hosted_button_id=2CKNJLZJBW8ZC" - }, - { - "type": "buymeacoffee", - "url": "https://www.buymeacoffee.com/samdecrock" - } - ], - "dependencies": { - "des.js": "^1.0.1", - "httpreq": ">=0.4.22", - "js-md4": "^0.3.2", - "underscore": "~1.12.1" - }, - "engines": { - "node": ">=10.4.0" + "assert-plus": "^1.0.0" } }, - "node_modules/httpreq": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-1.1.1.tgz", - "integrity": "sha512-uhSZLPPD2VXXOSN8Cni3kIsoFHaU2pT/nySEU/fHr/ePbqHYr0jeiQRmUKLEirC09SFPsdMoA7LU7UXMd/w0Kw==", + "node_modules/github-build": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/github-build/-/github-build-1.2.4.tgz", + "integrity": "sha512-1kdMmIrvYH18ITHGMVa5BXOxj/+i/VZzPR4PGMBpLW9h15woU+gpM/mlqOk+jmuD4mmib8Dgb6Xcbyy0v+RqqA==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 6.15.1" + "dependencies": { + "axios": "1.6.0" } }, - "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==", + "node_modules/github-build/node_modules/axios": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", + "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=16.17.0" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, - "node_modules/humanize-duration": { - "version": "3.32.1", - "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.32.1.tgz", - "integrity": "sha512-inh5wue5XdfObhu/IGEMiA1nUXigSGcaKNemcbLRKa7jXYGDZXr3LoT9pTIzq2hPEbld7w/qv9h+ikWGz8fL1g==", + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", "dev": true, - "license": "Unlicense" + "license": "MIT" }, - "node_modules/husky": { - "version": "9.1.7", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", - "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, "bin": { - "husky": "bin.js" + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=18" + "node": "20 || >=22" }, "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "license": "MIT", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, "engines": { - "node": ">= 4" + "node": ">= 6" } }, - "node_modules/iltorb": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/iltorb/-/iltorb-2.4.5.tgz", - "integrity": "sha512-EMCMl3LnnNSZJS5QrxyZmMTaAC4+TJkM5woD+xbpm9RB+mFYCr7C05GFE3TEGCsVQSVHmjX+3sf5AiwsylNInQ==", - "deprecated": "The zlib module provides APIs for brotli compression/decompression starting with Node.js v10.16.0, please use it over iltorb", + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "hasInstallScript": true, "license": "MIT", "dependencies": { - "detect-libc": "^1.0.3", - "nan": "^2.14.0", - "npmlog": "^4.1.2", - "prebuild-install": "^5.3.3", - "which-pm-runs": "^1.0.0" + "balanced-match": "^1.0.0" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "node_modules/glob/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, - "license": "MIT" + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "node_modules/import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "node_modules/globals": { + "version": "15.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", + "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", "dev": true, "license": "MIT", - "dependencies": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - }, "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-lazy": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz", - "integrity": "sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ==", + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/import-meta-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", - "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "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": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/globby/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": ">=0.8.19" + "node": ">=8" } }, - "node_modules/indent-string": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", - "integrity": "sha512-Z1vqf6lDC3f4N2mWqRywY6odjRatPNGDZgUr4DY9MLC14+Fp2/y+CI/RnNGlb8hD6ckscE/8DlZUwHUaiDBshg==", + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, "license": "MIT", - "dependencies": { - "get-stdin": "^4.0.1", - "minimist": "^1.1.0", - "repeating": "^1.1.0" - }, - "bin": { - "indent-string": "cli.js" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true, - "license": "ISC" + "license": "MIT", + "optional": true, + "peer": true }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "license": "MIT" + }, + "node_modules/graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" + "lodash": "^4.17.15" } }, - "node_modules/into-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", - "integrity": "sha512-TcdjPibTksa1NQximqep2r17ISRiNE9fwlfbg3F8ANdvP5/yrFTew86VcO//jk4QTaMlbjypPBq76HN2zaKfZQ==", + "node_modules/graphql": { + "version": "16.10.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", + "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", "dev": true, "license": "MIT", - "dependencies": { - "from2": "^2.1.1", - "p-is-promise": "^1.1.0" - }, "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "node_modules/gzip-size": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-4.1.0.tgz", + "integrity": "sha512-1g6EPVvIHuPmpAdBBpsIVYLgjzGV/QqcFRJXpMyrqEWG10JhOaTjQeCcjMDyX0Iqfm/Q5M9twR/mbDk5f5MqkA==", "dev": true, "license": "MIT", "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" + "duplexer": "^0.1.1", + "pify": "^3.0.0" }, "engines": { - "node": ">= 12" + "node": ">=4" } }, - "node_modules/ip-address/node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true, - "license": "MIT" - }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" }, "engines": { - "node": ">= 0.4" + "node": ">=0.4.7" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-async-function": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", - "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", + "node_modules/happy-dom": { + "version": "15.11.7", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-15.11.7.tgz", + "integrity": "sha512-KyrFvnl+J9US63TEzwoiJOQzZBJY7KgBushJA8X61DMbNsH+2ONkDuLDnCnwUiPTF42tLoEmrPyoqbenVA5zrg==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" + "entities": "^4.5.0", + "webidl-conversions": "^7.0.0", + "whatwg-mimetype": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18.0.0" } }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, + "license": "ISC", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", "dev": true, "license": "MIT", "dependencies": { - "binary-extensions": "^2.0.0" + "ajv": "^6.12.3", + "har-schema": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/is-boolean-object": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", - "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, "engines": { "node": ">= 0.4" }, @@ -12412,39 +10284,36 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "license": "MIT", "dependencies": { - "ci-info": "^1.5.0" + "es-define-property": "^1.0.0" }, - "bin": { - "is-ci": "bin.js" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "dunder-proto": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -12453,17 +10322,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, "engines": { "node": ">= 0.4" }, @@ -12471,15 +10335,14 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -12488,1369 +10351,1361 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "license": "ISC" }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", - "bin": { - "is-docker": "cli.js" + "dependencies": { + "function-bind": "^1.1.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/is-es2016-keyword": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-es2016-keyword/-/is-es2016-keyword-1.0.0.tgz", - "integrity": "sha512-JtZWPUwjdbQ1LIo9OSZ8MdkWEve198ors27vH+RzUUvZXXZkzXCxFnlUhzWYxy5IexQSRiXVw9j2q/tHMmkVYQ==", + "node_modules/headers-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", "dev": true, "license": "MIT" }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/highlight-es": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/highlight-es/-/highlight-es-1.0.3.tgz", + "integrity": "sha512-s/SIX6yp/5S1p8aC/NRDC1fwEb+myGIfp8/TzZz0rtAv8fzsdX7vGl3Q1TrXCsczFq8DI3CBFBCySPClfBSdbg==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "chalk": "^2.4.0", + "is-es2016-keyword": "^1.0.0", + "js-tokens": "^3.0.0" } }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "node_modules/highlight-es/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3" + "color-convert": "^1.9.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "node_modules/highlight-es/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=4" } }, - "node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "node_modules/highlight-es/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": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.0" } }, - "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, + "node_modules/highlight-es/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/highlight-es/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "dev": true, + "license": "MIT" + }, + "node_modules/highlight-es/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "whatwg-encoding": "^3.1.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18" } }, - "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==", - "license": "MIT" - }, - "node_modules/is-natural-number": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", - "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==", + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, "license": "MIT" }, - "node_modules/is-node-process": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", - "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true, - "license": "MIT" + "license": "BSD-2-Clause" }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "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": ">=0.12.0" + "node": ">= 14" } }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.8", + "npm": ">=1.3.7" } }, - "node_modules/is-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", - "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==", + "node_modules/http-status-codes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, - "node_modules/is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==", + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dev": true, "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10.19.0" } }, - "node_modules/is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "node_modules/httpntlm": { + "version": "1.8.13", + "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.8.13.tgz", + "integrity": "sha512-2F2FDPiWT4rewPzNMg3uPhNkP3NExENlUGADRUDPQvuftuUTGW98nLZtGemCIW3G40VhWZYgkIDcQFAwZ3mf2Q==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "paypal", + "url": "https://www.paypal.com/donate/?hosted_button_id=2CKNJLZJBW8ZC" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/samdecrock" + } + ], "dependencies": { - "is-path-inside": "^1.0.0" + "des.js": "^1.0.1", + "httpreq": ">=0.4.22", + "js-md4": "^0.3.2", + "underscore": "~1.12.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.4.0" } }, - "node_modules/is-path-in-cwd/node_modules/is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", + "node_modules/httpreq": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-1.1.1.tgz", + "integrity": "sha512-uhSZLPPD2VXXOSN8Cni3kIsoFHaU2pT/nySEU/fHr/ePbqHYr0jeiQRmUKLEirC09SFPsdMoA7LU7UXMd/w0Kw==", "dev": true, "license": "MIT", - "dependencies": { - "path-is-inside": "^1.0.1" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 6.15.1" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "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": ">=8" + "node": ">= 14" } }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=0.10.0" + "node": ">=16.17.0" } }, - "node_modules/is-podman": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-podman/-/is-podman-1.0.1.tgz", - "integrity": "sha512-+5vbtF5FIg262iUa7gOIseIWTx0740RHiax7oSmJMhbfSoBIMQ/IacKKgfnGj65JGeH9lGEVQcdkDwhn1Em1mQ==", + "node_modules/humanize-duration": { + "version": "3.32.1", + "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.32.1.tgz", + "integrity": "sha512-inh5wue5XdfObhu/IGEMiA1nUXigSGcaKNemcbLRKa7jXYGDZXr3LoT9pTIzq2hPEbld7w/qv9h+ikWGz8fL1g==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", "dev": true, "license": "MIT", "bin": { - "is-podman": "cli.js" + "husky": "bin.js" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "license": "MIT", "dependencies": { - "@types/estree": "*" + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/is-regex": { + "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 4" } }, - "node_modules/is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "node_modules/iltorb": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/iltorb/-/iltorb-2.4.5.tgz", + "integrity": "sha512-EMCMl3LnnNSZJS5QrxyZmMTaAC4+TJkM5woD+xbpm9RB+mFYCr7C05GFE3TEGCsVQSVHmjX+3sf5AiwsylNInQ==", + "deprecated": "The zlib module provides APIs for brotli compression/decompression starting with Node.js v10.16.0, please use it over iltorb", "dev": true, + "hasInstallScript": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "detect-libc": "^1.0.3", + "nan": "^2.14.0", + "npmlog": "^4.1.2", + "prebuild-install": "^5.3.3", + "which-pm-runs": "^1.0.0" } }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "optional": true, + "peer": true }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3" + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "node_modules/import-lazy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz", + "integrity": "sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ==", "dev": true, "license": "MIT", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, + "optional": true, + "peer": true, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.8.19" } }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "node_modules/indent-string": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", + "integrity": "sha512-Z1vqf6lDC3f4N2mWqRywY6odjRatPNGDZgUr4DY9MLC14+Fp2/y+CI/RnNGlb8hD6ckscE/8DlZUwHUaiDBshg==", "dev": true, "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.16" + "get-stdin": "^4.0.1", + "minimist": "^1.1.0", + "repeating": "^1.1.0" }, - "engines": { - "node": ">= 0.4" + "bin": { + "indent-string": "cli.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/is-weakref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", - "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", - "dev": true, - "license": "MIT", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", "dependencies": { - "call-bound": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/is-weakset": { + "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "is-docker": "^2.0.0" + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" }, "engines": { - "node": ">=8" + "node": ">= 12" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "node_modules/ip-address/node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 8.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true, - "license": "MIT" + "optional": true, + "peer": true }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true, "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } + "optional": true, + "peer": true }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } + "license": "MIT" }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "node_modules/is-async-function": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", + "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", "dependencies": { - "semver": "^7.5.3" + "has-bigints": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "has-flag": "^4.0.0" + "binary-extensions": "^2.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "node_modules/is-boolean-object": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/isurl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "node_modules/is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", "dev": true, "license": "MIT", "dependencies": { - "has-to-string-tag-x": "^1.2.0", - "is-object": "^1.0.1" + "ci-info": "^1.5.0" }, - "engines": { - "node": ">= 4" + "bin": { + "is-ci": "bin.js" } }, - "node_modules/jackspeak": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", - "dev": true, - "license": "BlueOak-1.0.0", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "hasown": "^2.0.2" }, "engines": { - "node": "20 || >=22" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jasmine": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.5.0.tgz", - "integrity": "sha512-JKlEVCVD5QBPYLsg/VE+IUtjyseDCrW8rMBu8la+9ysYashDgavMLM9Kotls1FhI6dCJLJ40dBCIfQjGLPZI1Q==", + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "license": "MIT", "dependencies": { - "glob": "^10.2.2", - "jasmine-core": "~5.5.0" + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" }, - "bin": { - "jasmine": "bin/jasmine.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jasmine-core": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.5.0.tgz", - "integrity": "sha512-NHOvoPO6o9gVR6pwqEACTEpbgcH+JJ6QDypyymGbSUIFIFsMMbBJ/xsFNud8MSClfnWclXd7RQlAZBz7yVo5TQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jasmine-expect": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/jasmine-expect/-/jasmine-expect-5.0.0.tgz", - "integrity": "sha512-byn1zq0EQBA9UKs5A+H6gk5TRcanV+TqQMRxrjurGuqKkclaqgjw/vV6aT/jtf5tabXGonTH6VDZJ33Z1pxSxw==", + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "license": "MIT", "dependencies": { - "add-matchers": "0.6.2" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jasmine/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/jasmine/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, + "license": "MIT", "bin": { - "glob": "dist/esm/bin.mjs" + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jasmine/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "node_modules/is-es2016-keyword": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-es2016-keyword/-/is-es2016-keyword-1.0.0.tgz", + "integrity": "sha512-JtZWPUwjdbQ1LIo9OSZ8MdkWEve198ors27vH+RzUUvZXXZkzXCxFnlUhzWYxy5IexQSRiXVw9j2q/tHMmkVYQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "call-bound": "^1.0.3" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">= 0.4" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jasmine/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/jasmine/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" + "license": "MIT", + "engines": { + "node": ">=0.10.0" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jasmine/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/js-cookie": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", - "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, "license": "MIT", "engines": { - "node": ">=14" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/js-md4": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", - "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", - "dev": true, + "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==", "license": "MIT" }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true, "license": "MIT" }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=0.12.0" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsdom": { - "version": "25.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", - "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, "license": "MIT", "dependencies": { - "cssstyle": "^4.1.0", - "data-urls": "^5.0.0", - "decimal.js": "^10.4.3", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.5", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.12", - "parse5": "^7.1.2", - "rrweb-cssom": "^0.7.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.0.0", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^2.11.2" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jsdom/node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "node_modules/is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=0.10.0" } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "node_modules/is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" + "dependencies": { + "is-path-inside": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "license": "MIT" - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "node_modules/is-path-in-cwd/node_modules/is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "path-is-inside": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "license": "(AFL-2.1 OR BSD-3-Clause)" + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" + "node_modules/is-podman": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-podman/-/is-podman-1.0.1.tgz", + "integrity": "sha512-+5vbtF5FIg262iUa7gOIseIWTx0740RHiax7oSmJMhbfSoBIMQ/IacKKgfnGj65JGeH9lGEVQcdkDwhn1Em1mQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-podman": "cli.js" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/json-stable-stringify-without-jsonify": { + "node_modules/is-potential-custom-element-name": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, "license": "MIT" }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "license": "ISC" + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, "license": "MIT", - "bin": { - "json5": "lib/cli.js" + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, "license": "MIT", "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" + "call-bound": "^1.0.3" }, "engines": { - "node": ">=0.6.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/karma": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", - "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "license": "MIT", "dependencies": { - "@colors/colors": "1.5.0", - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.4.1", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.5", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^4.7.2", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.30", - "yargs": "^16.1.1" - }, - "bin": { - "karma": "bin/karma" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">= 10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/karma-chrome-launcher": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz", - "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==", + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "license": "MIT", "dependencies": { - "which": "^1.2.1" - } - }, - "node_modules/karma-chrome-launcher/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, - "bin": { - "which": "bin/which" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/karma-coverage": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.1.tgz", - "integrity": "sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==", + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, "license": "MIT", "dependencies": { - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.1", - "istanbul-reports": "^3.0.5", - "minimatch": "^3.0.4" + "which-typed-array": "^1.1.16" }, "engines": { - "node": ">=10.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/karma-firefox-launcher": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-2.1.3.tgz", - "integrity": "sha512-LMM2bseebLbYjODBOVt7TCPP9OI2vZIXCavIXhkO9m+10Uj5l7u/SKoeRmYx8FYHTVGZSpk6peX+3BMHC1WwNw==", + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, "license": "MIT", - "dependencies": { - "is-wsl": "^2.2.0", - "which": "^3.0.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/karma-firefox-launcher/node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "node_modules/is-weakref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/which.js" + "call-bound": "^1.0.2" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/karma-jasmine": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", - "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "license": "MIT", "dependencies": { - "jasmine-core": "^4.1.0" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, - "peerDependencies": { - "karma": "^6.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/karma-jasmine-html-reporter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz", - "integrity": "sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==", + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true, - "license": "MIT", - "peerDependencies": { - "jasmine-core": "^4.0.0 || ^5.0.0", - "karma": "^6.0.0", - "karma-jasmine": "^5.0.0" - } + "license": "MIT" }, - "node_modules/karma-jasmine-matchers": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/karma-jasmine-matchers/-/karma-jasmine-matchers-5.0.0.tgz", - "integrity": "sha512-XnwKFbeQ8vqCkL/6bLiCFsHR4+TWTIVByuUYFrPM38JBD1hb7TNLP7cPRkQs4QBNWQ1o8z/u4EOetQC2ZFMb8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "jasmine-expect": "5.0.0" - } + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" }, - "node_modules/karma-jasmine/node_modules/jasmine-core": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.6.1.tgz", - "integrity": "sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==", + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true, "license": "MIT" }, - "node_modules/karma-junit-reporter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/karma-junit-reporter/-/karma-junit-reporter-2.0.1.tgz", - "integrity": "sha512-VtcGfE0JE4OE1wn0LK8xxDKaTP7slN8DO3I+4xg6gAi1IoAHAXOJ1V9G/y45Xg6sxdxPOR3THCFtDlAfBo9Afw==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, - "license": "MIT", - "dependencies": { - "path-is-absolute": "^1.0.0", - "xmlbuilder": "12.0.0" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">= 8" - }, - "peerDependencies": { - "karma": ">=0.9" + "node": ">=8" } }, - "node_modules/karma-rollup-preprocessor": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/karma-rollup-preprocessor/-/karma-rollup-preprocessor-7.0.8.tgz", - "integrity": "sha512-WiuBCS9qsatJuR17dghiTARBZ7LF+ml+eb7qJXhw7IbsdY0lTWELDRQC/93J9i6636CsAXVBL3VJF4WtaFLZzA==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "chokidar": "^3.3.1", - "debounce": "^1.2.0" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "rollup": ">= 1.0.0" + "node": ">=10" } }, - "node_modules/karma-safari-launcher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/karma-safari-launcher/-/karma-safari-launcher-1.0.0.tgz", - "integrity": "sha512-qmypLWd6F2qrDJfAETvXDfxHvKDk+nyIjpH9xIeI3/hENr0U3nuqkxaftq73PfXZ4aOuOChA6SnLW4m4AxfRjQ==", + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "license": "MIT", - "peerDependencies": { - "karma": ">=0.9" + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/karma-sauce-launcher": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/karma-sauce-launcher/-/karma-sauce-launcher-4.3.6.tgz", - "integrity": "sha512-Ej62q4mUPFktyAm8g0g8J5qhwEkXwdHrwtiV4pZjKNHNnSs+4qgDyzs3VkpOy3AmNTsTqQXUN/lpiy0tZpDJZQ==", + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { - "global-agent": "^2.1.12", - "saucelabs": "^4.6.3", - "webdriverio": "^6.7.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 10.0.0" + "node": ">=8" } }, - "node_modules/karma-spec-reporter": { - "version": "0.0.36", - "resolved": "https://registry.npmjs.org/karma-spec-reporter/-/karma-spec-reporter-0.0.36.tgz", - "integrity": "sha512-11bvOl1x6ryKZph7kmbmMpbi8vsngEGxGOoeTlIcDaH3ab3j8aPJnZ+r+K/SS0sBSGy5VGkGYO2+hLct7hw/6w==", + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "colors": "1.4.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, - "peerDependencies": { - "karma": ">=0.9" + "engines": { + "node": ">=8" } }, - "node_modules/karma/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "color-convert": "^2.0.1" + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=8" + "node": "20 || >=22" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/karma/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", "dev": true, - "license": "ISC", + "license": "BSD-3-Clause", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" } }, - "node_modules/karma/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">=14" } }, - "node_modules/karma/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", "dev": true, "license": "MIT" }, - "node_modules/karma/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, - "node_modules/karma/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", + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, - "license": "ISC", + "license": "MIT", "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": "*" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/karma/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", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/karma/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "glob": "^7.1.3" + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=18" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/karma/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/jsdom/node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/karma/node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", - "dev": true, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, "engines": { - "node": ">=14.14" + "node": ">=6" } }, - "node_modules/karma/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true, + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "bin": { + "json5": "lib/cli.js" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=6" } }, - "node_modules/karma/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", "dev": true, "license": "MIT", "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" }, "engines": { - "node": ">=10" + "node": ">=0.6.0" } }, - "node_modules/karma/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" + "license": "(MIT OR GPL-3.0-or-later)", + "optional": true, + "peer": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" } }, "node_modules/keyv": { @@ -13901,6 +11756,8 @@ "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "readable-stream": "^2.0.5" }, @@ -13927,6 +11784,8 @@ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "immediate": "~3.0.5" } @@ -13987,7 +11846,9 @@ "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/listr2": { "version": "8.2.5", @@ -14115,6 +11976,8 @@ } ], "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@promptbook/utils": "0.69.5", "type-fest": "4.26.0", @@ -14127,6 +11990,8 @@ "integrity": "sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==", "dev": true, "license": "(MIT OR CC0-1.0)", + "optional": true, + "peer": true, "engines": { "node": ">=16" }, @@ -14160,7 +12025,9 @@ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -14179,7 +12046,9 @@ "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", "integrity": "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/log-update": { "version": "6.1.0", @@ -14457,23 +12326,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/log4js": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", - "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "flatted": "^3.2.7", - "rfdc": "^1.3.0", - "streamroller": "^3.1.5" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/loglevel": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", @@ -14502,16 +12354,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, "node_modules/lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -14635,19 +12477,6 @@ "node": ">=0.8.0" } }, - "node_modules/matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -14658,16 +12487,6 @@ "node": ">= 0.4" } }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -14712,19 +12531,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -14828,7 +12634,9 @@ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/mkdirp": { "version": "0.5.6", @@ -15001,16 +12809,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "license": "MIT" }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -15024,28 +12822,12 @@ "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4.0" } }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, "node_modules/node-abi": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", @@ -15082,6 +12864,8 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=10.5.0" } @@ -15092,6 +12876,8 @@ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -15112,14 +12898,18 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/node-fetch/node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true, - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "optional": true, + "peer": true }, "node_modules/node-fetch/node_modules/whatwg-url": { "version": "5.0.0", @@ -15127,6 +12917,8 @@ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -15151,6 +12943,7 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "license": "MIT", + "optional": true, "engines": { "node": ">=0.10.0" } @@ -15168,20 +12961,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-conf": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", - "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", - "dev": true, - "license": "MIT", - "dependencies": { - "config-chain": "^1.1.11", - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/npm-run-path": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", @@ -15374,19 +13153,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -15436,19 +13202,6 @@ "dev": true, "license": "MIT" }, - "node_modules/os-filter-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", - "integrity": "sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "arch": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -15493,39 +13246,6 @@ "node": ">=8" } }, - "node_modules/p-event": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/p-event/-/p-event-2.3.1.tgz", - "integrity": "sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-timeout": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-is-promise": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", - "integrity": "sha512-zL7VE4JVS2IFSkR2GQKDSPEVxkoH43/p7oEnwpdCndKYJO0HVeRB7fA8TJwuLOTBREtK0ea8eHaxdwcpob5dmg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/p-iteration": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/p-iteration/-/p-iteration-1.1.8.tgz", @@ -15576,19 +13296,6 @@ "node": ">=4" } }, - "node_modules/p-timeout": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", - "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -15605,6 +13312,8 @@ "integrity": "sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.1.2", @@ -15625,6 +13334,8 @@ "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" @@ -15657,18 +13368,9 @@ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true, - "license": "(MIT AND Zlib)" - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } + "license": "(MIT AND Zlib)", + "optional": true, + "peer": true }, "node_modules/parent-module": { "version": "1.0.1", @@ -15727,38 +13429,6 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", - "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -15878,7 +13548,9 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/performance-now": { "version": "2.1.0", @@ -16165,16 +13837,6 @@ "node": ">= 0.8.0" } }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/prettier": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", @@ -16306,6 +13968,8 @@ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.6.0" } @@ -16323,6 +13987,8 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.4.0" } @@ -16351,19 +14017,14 @@ "node": ">= 6" } }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "dev": true, - "license": "ISC" - }, "node_modules/proxy-agent": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", @@ -16384,6 +14045,8 @@ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "engines": { "node": ">=12" } @@ -16411,13 +14074,6 @@ "node": ">= 0.10" } }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true, - "license": "ISC" - }, "node_modules/psl": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", @@ -16452,19 +14108,14 @@ "once": "^1.3.1" } }, - "node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true, - "license": "MIT" - }, "node_modules/puppeteer-core": { "version": "21.11.0", "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-21.11.0.tgz", "integrity": "sha512-ArbnyA3U5SGHokEvkfWjW+O8hOxV1RSJxOgriX/3A4xZRqixt9ZFHD0yPgZQF05Qj0oAqi8H/7stDorjoHY90Q==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@puppeteer/browsers": "1.9.1", "chromium-bidi": "0.5.8", @@ -16483,6 +14134,8 @@ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ms": "2.1.2" }, @@ -16500,14 +14153,18 @@ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1232444.tgz", "integrity": "sha512-pM27vqEfxSxRkTMnF+XCmxSEb6duO5R+t8A9DEEJgy4Wz2RVanje2mmj99B6A3zv2r/qGfYlOvYznUhuokizmg==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "optional": true, + "peer": true }, "node_modules/puppeteer-core/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/puppeteer-core/node_modules/ws": { "version": "8.16.0", @@ -16515,6 +14172,8 @@ "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -16531,16 +14190,6 @@ } } }, - "node_modules/qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.9" - } - }, "node_modules/qrcode-terminal": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.10.0.tgz", @@ -16550,43 +14199,14 @@ "qrcode-terminal": "bin/qrcode-terminal.js" } }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/query-selector-shadow-dom": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz", "integrity": "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==", "dev": true, - "license": "MIT" - }, - "node_modules/query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "dev": true, "license": "MIT", - "dependencies": { - "decode-uri-component": "^0.2.0", - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } + "optional": true, + "peer": true }, "node_modules/querystringify": { "version": "2.2.0", @@ -16620,7 +14240,9 @@ "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/quick-lru": { "version": "5.1.1", @@ -16637,37 +14259,11 @@ }, "node_modules/randombytes": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "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==", - "dev": true, + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" + "safe-buffer": "^5.1.0" } }, "node_modules/rc": { @@ -16755,6 +14351,8 @@ "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "minimatch": "^5.1.0" } @@ -16765,6 +14363,8 @@ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -16775,6 +14375,8 @@ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -16788,6 +14390,7 @@ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -16801,6 +14404,7 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", + "optional": true, "engines": { "node": ">=8.6" }, @@ -17174,6 +14778,8 @@ "integrity": "sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "fast-deep-equal": "^2.0.1" } @@ -17183,7 +14789,9 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/restore-cursor": { "version": "5.1.0", @@ -17240,7 +14848,9 @@ "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.5.tgz", "integrity": "sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/rimraf": { "version": "6.0.1", @@ -17262,31 +14872,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/roarr/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/rollup": { "version": "4.30.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.0.tgz", @@ -17473,7 +15058,9 @@ "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.2.tgz", "integrity": "sha512-4R309+gWflJktzPXBQCobbWEHlzC4aK3a+Ov3tz2Ib2aBxiwd11phkdIBH1l0EO22x24CJMUQkpKFumRriCSRg==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/safe-array-concat": { "version": "1.1.3", @@ -17640,327 +15227,64 @@ "debug": "4" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/sauce-connect-launcher/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/saucelabs": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-4.7.8.tgz", - "integrity": "sha512-K2qaRUixc7+8JiAwpTvEsIQVzzUkYwa0mAfs0akGagRlWXUR1JrsmgJRyz28qkwpERW1KDuByn3Ju96BuW1V7Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bin-wrapper": "^4.1.0", - "change-case": "^4.1.1", - "form-data": "^3.0.0", - "got": "^11.7.0", - "hash.js": "^1.1.7", - "tunnel": "0.0.6", - "yargs": "^16.0.3" - }, - "bin": { - "sl": "bin/sl" - } - }, - "node_modules/saucelabs-connector": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/saucelabs-connector/-/saucelabs-connector-2.0.0.tgz", - "integrity": "sha512-fWZ8hmS/xEgmfGA8itOO1eEzIlLGxpw8PpQ0TpSxhm5xA2LUS3NPeR1b/6GhPbjzCjDTmjKoQgsOuKIkHIRI9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "got": "^11.8.2", - "os-family": "^1.0.0", - "read-file-relative": "^1.2.0", - "sauce-connect-launcher": "^1.2.7", - "webdriver": "^7.31.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/saucelabs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/saucelabs/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/saucelabs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/saucelabs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/saucelabs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/saucelabs/node_modules/form-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz", - "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/saucelabs/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", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/saucelabs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/saucelabs/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/saucelabs/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/saucelabs/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, - "node_modules/seek-bzip": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", - "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^2.8.1" - }, - "bin": { - "seek-bunzip": "bin/seek-bunzip", - "seek-table": "bin/seek-bzip-table" - } - }, - "node_modules/seek-bzip/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "dev": true, - "license": "MIT" - }, - "node_modules/semver-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", - "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/semver-truncate": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-1.1.2.tgz", - "integrity": "sha512-V1fGg9i4CL3qesB6U0L6XAm4xOJiHmt4QAacazumuasc03BvtFGIMCduv01JWQ69Nv+JST9TqhSCiJoxoY031w==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^5.3.0" - }, - "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/semver-truncate/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/sauce-connect-launcher/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, "bin": { - "semver": "bin/semver" + "rimraf": "bin.js" } }, - "node_modules/sentence-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", - "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "node_modules/saucelabs-connector": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/saucelabs-connector/-/saucelabs-connector-2.0.0.tgz", + "integrity": "sha512-fWZ8hmS/xEgmfGA8itOO1eEzIlLGxpw8PpQ0TpSxhm5xA2LUS3NPeR1b/6GhPbjzCjDTmjKoQgsOuKIkHIRI9Q==", "dev": true, "license": "MIT", "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" + "got": "^11.8.2", + "os-family": "^1.0.0", + "read-file-relative": "^1.2.0", + "sauce-connect-launcher": "^1.2.7", + "webdriver": "^7.31.1" + }, + "engines": { + "node": ">=10.0.0" } }, - "node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "type-fest": "^0.13.1" + "xmlchars": "^2.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=v12.22.7" } }, - "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "license": "(MIT OR CC0-1.0)", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/serialize-javascript": { @@ -18040,14 +15364,9 @@ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "dev": true, - "license": "MIT" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true, - "license": "ISC" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/shebang-command": { "version": "2.0.0", @@ -18305,6 +15624,8 @@ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -18316,143 +15637,14 @@ "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==", "license": "MIT" }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dev": true, - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/socket.io": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", - "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", - "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "~4.3.4", - "ws": "~8.17.1" - } - }, - "node_modules/socket.io-adapter/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "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/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/socks": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" @@ -18468,6 +15660,8 @@ "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", @@ -18477,32 +15671,6 @@ "node": ">= 14" } }, - "node_modules/sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sort-keys-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", - "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "sort-keys": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -18547,7 +15715,9 @@ "url": "https://github.com/hejny/spacetrim/blob/main/README.md#%EF%B8%8F-contributing" } ], - "license": "Apache-2.0" + "license": "Apache-2.0", + "optional": true, + "peer": true }, "node_modules/spdx-compare": { "version": "1.0.0", @@ -18627,6 +15797,8 @@ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "engines": { "node": ">= 10.x" } @@ -18829,16 +16001,6 @@ "node": ">=6" } }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/std-env": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", @@ -18856,27 +16018,14 @@ "duplexer": "~0.1.1" } }, - "node_modules/streamroller": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", - "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "fs-extra": "^8.1.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/streamx": { "version": "2.21.1", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "fast-fifo": "^1.3.2", "queue-tick": "^1.0.1", @@ -18893,16 +16042,6 @@ "dev": true, "license": "MIT" }, - "node_modules/strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -19101,26 +16240,6 @@ "node": ">=0.10.0" } }, - "node_modules/strip-dirs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", - "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-natural-number": "^4.0.1" - } - }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -19146,35 +16265,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-outer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-outer/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/strnum": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/supports-color": { "version": "8.1.1", @@ -19285,25 +16383,6 @@ "node": ">=6" } }, - "node_modules/tar-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", - "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^1.0.0", - "buffer-alloc": "^1.2.0", - "end-of-stream": "^1.0.0", - "fs-constants": "^1.0.0", - "readable-stream": "^2.3.0", - "to-buffer": "^1.1.1", - "xtend": "^4.0.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/terser": { "version": "5.37.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", @@ -20702,6 +17781,8 @@ "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "b4a": "^1.6.4" } @@ -20719,24 +17800,14 @@ "dev": true, "license": "MIT" }, - "node_modules/time-limit-promise": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/time-limit-promise/-/time-limit-promise-1.0.4.tgz", - "integrity": "sha512-FLHDDsIDducw7MBcRWlFtW2Tm50DoKOSFf0Nzx17qwXj8REXCte0eUkHrJl9QU3Bl9arG3XNYX0PcHpZ9xyuLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==", + "node_modules/time-limit-promise": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/time-limit-promise/-/time-limit-promise-1.0.4.tgz", + "integrity": "sha512-FLHDDsIDducw7MBcRWlFtW2Tm50DoKOSFf0Nzx17qwXj8REXCte0eUkHrJl9QU3Bl9arG3XNYX0PcHpZ9xyuLw==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.12" } }, "node_modules/tinybench": { @@ -20815,13 +17886,6 @@ "node": ">=0.6.0" } }, - "node_modules/to-buffer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", - "dev": true, - "license": "MIT" - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -20835,16 +17899,6 @@ "node": ">=8.0" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -20897,6 +17951,8 @@ "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", "dev": true, "license": "MIT/X11", + "optional": true, + "peer": true, "engines": { "node": "*" } @@ -20911,29 +17967,6 @@ "tree-kill": "cli.js" } }, - "node_modules/trim-repeated": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", - "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/trim-repeated/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/truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", @@ -20987,16 +18020,6 @@ "dev": true, "license": "0BSD" }, - "node_modules/tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" - } - }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -21051,20 +18074,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -21159,33 +18168,6 @@ "node": ">=4.2.0" } }, - "node_modules/ua-parser-js": { - "version": "0.7.40", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.40.tgz", - "integrity": "sha512-us1E3K+3jJppDBa3Tl0L3MOJiGhe1C6P0+nIvQAFYbxlMAx0h81eOwLmU57xgqToduDDPx3y5QsdjPfDu+FgOQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "license": "MIT", - "bin": { - "ua-parser-js": "script/cli.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -21225,6 +18207,8 @@ "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "buffer": "^5.2.1", "through": "^2.3.8" @@ -21290,26 +18274,6 @@ "dev": true, "license": "ISC" }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/unquote": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", @@ -21323,6 +18287,8 @@ "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "big-integer": "^1.6.17", "binary": "~0.3.0", @@ -21341,7 +18307,9 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/update-browserslist-db": { "version": "1.1.1", @@ -21373,26 +18341,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/upper-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", - "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/upper-case-first": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", - "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -21429,35 +18377,14 @@ "requires-port": "^1.0.0" } }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/url-to-options": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", - "integrity": "sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/urlpattern-polyfill": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/userhome": { "version": "1.0.1", @@ -21465,6 +18392,8 @@ "integrity": "sha512-5cnLm4gseXjAclKowC4IjByaGsjtAoV6PrOQOljplNB54ReUYJP8HdAFq2muHinSDAh09PPX/uXDPfdxRHvuSA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.8.0" } @@ -21483,16 +18412,6 @@ "dev": true, "license": "MIT" }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/uuid": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.4.tgz", @@ -21506,16 +18425,6 @@ "uuid": "dist/esm/bin/uuid" } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -21687,16 +18596,6 @@ } } }, - "node_modules/void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -21736,6 +18635,8 @@ "integrity": "sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "chalk": "^4.1.2", "commander": "^9.3.0", @@ -21754,6 +18655,8 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -21770,6 +18673,8 @@ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -21787,6 +18692,8 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -21799,7 +18706,9 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/wait-port/node_modules/commander": { "version": "9.5.0", @@ -21807,6 +18716,8 @@ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": "^12.20.0 || >=14" } @@ -21817,6 +18728,8 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -21830,6 +18743,8 @@ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 8" } @@ -21878,6 +18793,8 @@ "integrity": "sha512-WlQfw0mUEhTS8DPr+TBSYMhEnqXkFr2dcUwPb5XkffTB+i0wftf+BLXJPSVD9M1PTLyYcFdCIu68pqR54dq5BA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/node": "^22.2.0", "@wdio/config": "8.41.0", @@ -21923,6 +18840,8 @@ "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=14.16" }, @@ -21936,6 +18855,8 @@ "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "defer-to-connect": "^2.0.1" }, @@ -21949,6 +18870,8 @@ "integrity": "sha512-/6Z3sfSyhX5oVde0l01fyHimbqRYIVUDBnhDG2EMSCoC2lsaJX3Bm3IYpYHYHHFsgoDCi3B3Gv++t9dn2eSZZw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@wdio/logger": "8.38.0", "@wdio/types": "8.41.0", @@ -21968,6 +18891,8 @@ "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "chalk": "^5.1.2", "loglevel": "^1.6.0", @@ -21983,7 +18908,9 @@ "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.40.3.tgz", "integrity": "sha512-wK7+eyrB3TAei8RwbdkcyoNk2dPu+mduMBOdPJjp8jf/mavd15nIUXLID1zA+w5m1Qt1DsT1NbvaeO9+aJQ33A==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/webdriverio/node_modules/@wdio/types": { "version": "8.41.0", @@ -21991,6 +18918,8 @@ "integrity": "sha512-t4NaNTvJZci3Xv/yUZPH4eTL0hxrVTf5wdwNnYIBrzMnlRDbNefjQ0P7FM7ZjQCLaH92AEH6t/XanUId7Webug==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/node": "^22.2.0" }, @@ -22004,6 +18933,8 @@ "integrity": "sha512-0TcTjBiax1VxtJQ/iQA0ZyYOSHjjX2ARVmEI0AMo9+AuIq+xBfnY561+v8k9GqOMPKsiH/HrK3xwjx8xCVS03g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@puppeteer/browsers": "^1.6.0", "@wdio/logger": "8.38.0", @@ -22029,6 +18960,8 @@ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=12" }, @@ -22042,6 +18975,8 @@ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -22052,6 +18987,8 @@ "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=14.16" } @@ -22062,6 +18999,8 @@ "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/http-cache-semantics": "^4.0.2", "get-stream": "^6.0.1", @@ -22080,7 +19019,9 @@ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1359167.tgz", "integrity": "sha512-f/9PeTaSH3weS/WAwrQb5/s9R3KMOeTGe+Jkhg5952yInub7iDPjdlzRdrDgpLZfxHbTrBuG9aUkAMM+ocVkXQ==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "optional": true, + "peer": true }, "node_modules/webdriverio/node_modules/get-stream": { "version": "6.0.1", @@ -22088,6 +19029,8 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=10" }, @@ -22101,6 +19044,8 @@ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -22122,6 +19067,8 @@ "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", @@ -22148,6 +19095,8 @@ "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.2.0" @@ -22162,6 +19111,8 @@ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=12" }, @@ -22175,6 +19126,8 @@ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "license": "BlueOak-1.0.0", + "optional": true, + "peer": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -22191,6 +19144,8 @@ "integrity": "sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=14.16" }, @@ -22204,6 +19159,8 @@ "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -22216,7 +19173,9 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/webdriverio/node_modules/mimic-response": { "version": "4.0.0", @@ -22224,6 +19183,8 @@ "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -22237,6 +19198,8 @@ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -22253,6 +19216,8 @@ "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=14.16" }, @@ -22266,6 +19231,8 @@ "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=12.20" } @@ -22276,6 +19243,8 @@ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "license": "BlueOak-1.0.0", + "optional": true, + "peer": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -22293,6 +19262,8 @@ "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "lowercase-keys": "^3.0.0" }, @@ -22309,6 +19280,8 @@ "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "type-fest": "^2.12.2" }, @@ -22325,6 +19298,8 @@ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -22341,6 +19316,8 @@ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, "license": "(MIT OR CC0-1.0)", + "optional": true, + "peer": true, "engines": { "node": ">=12.20" }, @@ -22354,6 +19331,8 @@ "integrity": "sha512-n8OrFnVT4hAaGa0Advr3T8ObJdeKNTRklHIEzM2CYVx/5DZt+2KwaKSxWsURNd4zU7FbsfaJUU4rQWCmvozQLg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/node": "^22.2.0", "@types/ws": "^8.5.3", @@ -23017,16 +19996,6 @@ "node": ">=18" } }, - "node_modules/xmlbuilder": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-12.0.0.tgz", - "integrity": "sha512-lMo8DJ8u6JRWp0/Y4XLa/atVDr75H9litKlb2E5j3V3MesoL50EBgZDWoLT3F/LztVnG67GjPXLZpqcky/UMnQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", @@ -23034,16 +20003,6 @@ "dev": true, "license": "MIT" }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -23140,6 +20099,8 @@ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" @@ -23151,6 +20112,8 @@ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": "*" } @@ -23185,6 +20148,8 @@ "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "archiver-utils": "^5.0.0", "compress-commons": "^6.0.2", @@ -23214,6 +20179,8 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -23225,6 +20192,8 @@ "integrity": "sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", @@ -23255,7 +20224,9 @@ "url": "https://feross.org/support" } ], - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/zip-stream/node_modules/string_decoder": { "version": "1.3.0", @@ -23263,6 +20234,8 @@ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } diff --git a/package.json b/package.json index 51c1b10b8..6d3172cb5 100644 --- a/package.json +++ b/package.json @@ -118,21 +118,7 @@ "handlebars": "^4.7.8", "happy-dom": "^15.11.1", "husky": "^9.1.6", - "jasmine": "^5.4.0", - "jasmine-core": "^5.4.0", "jsdom": "^25.0.1", - "karma": "^6.4.4", - "karma-chrome-launcher": "^3.2.0", - "karma-coverage": "^2.2.1", - "karma-firefox-launcher": "^2.1.3", - "karma-jasmine": "^5.1.0", - "karma-jasmine-html-reporter": "^2.1.0", - "karma-jasmine-matchers": "^5.0.0", - "karma-junit-reporter": "^2.0.1", - "karma-rollup-preprocessor": "^7.0.8", - "karma-safari-launcher": "^1.0.0", - "karma-sauce-launcher": "^4.3.6", - "karma-spec-reporter": "0.0.36", "lint-staged": "^15.2.10", "playwright": "^1.49.1", "prettier": "^3.3.3", @@ -153,11 +139,6 @@ "url-parse": "^1.5.10", "vitest": "^2.1.4" }, - "overrides": { - "karma-sauce-launcher": { - "webdriverio": "^8.35.1" - } - }, "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "^4.24.4" } diff --git a/sauceLabsCapabilities.json b/sauceLabsCapabilities.json deleted file mode 100644 index 073d5e8e2..000000000 --- a/sauceLabsCapabilities.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extendedDebugging": true, - "capturePerformance": true, - "recordScreenshots": false, - "recordVideo": false -} \ No newline at end of file diff --git a/scripts/specs/jasmine.json b/scripts/specs/jasmine.json deleted file mode 100644 index 2e0705610..000000000 --- a/scripts/specs/jasmine.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "spec_dir": "scripts/specs", - "spec_files": ["**/*[sS]pec.js"], - "helpers": [], - "stopSpecOnExpectationFailure": false, - "random": true -} From af65df16870aeace6ced034d575584a8dd5eed50 Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Wed, 8 Jan 2025 13:40:48 -0700 Subject: [PATCH 08/15] Remove karma tests. --- test/unit/constants/uuidV4Regex.js | 13 - test/unit/helpers/assertFunctionCallOrder.js | 24 - test/unit/helpers/cleanUpDomChanges.js | 25 - .../createDecoratePropositionForTest.js | 53 - test/unit/helpers/createMockProposition.js | 29 - test/unit/helpers/describeRequest.js | 53 - test/unit/helpers/describeRequestPayload.js | 91 - test/unit/helpers/describeValidation.js | 49 - test/unit/helpers/flushPromiseChains.js | 30 - test/unit/helpers/pause.js | 15 - test/unit/helpers/removeAllCookies.js | 19 - test/unit/helpers/testConfigValidators.js | 47 - .../attachClickActivityCollector.spec.js | 127 -- .../configValidators.spec.js | 47 - .../createClickActivityStorage.spec.js | 51 - .../createClickedElementProperties.spec.js | 224 --- .../createGetClickedElementProperties.spec.js | 276 --- ...eateInjectClickedElementProperties.spec.js | 141 -- ...lAndInjectClickedElementProperties.spec.js | 84 - .../createStorePageViewProperties.spec.js | 50 - .../ActivityCollector/getLinkName.spec.js | 153 -- .../ActivityCollector/getLinkRegion.spec.js | 113 -- .../utils/activityMapExtensionEnabled.spec.js | 37 - .../utils/createTransientStorage.spec.js | 41 - .../utils/determineLinkType.spec.js | 57 - .../utils/dom/elementHasClickHandler.spec.js | 38 - .../utils/dom/extractDomain.spec.js | 32 - .../utils/dom/findClickableElement.spec.js | 36 - .../getAbsoluteUrlFromAnchorElement.spec.js | 64 - .../utils/dom/isButtonSubmitElement.spec.js | 44 - .../utils/dom/isDownloadLink.spec.js | 46 - .../utils/dom/isExitLink.spec.js | 41 - .../utils/dom/isInputSubmitElement.spec.js | 39 - .../dom/isSupportedAnchorElement.spec.js | 55 - .../utils/dom/isSupportedTextNode.spec.js | 45 - .../utils/hasPageName.spec.js | 41 - .../utils/isDifferentDomains.spec.js | 28 - .../utils/trimQueryFromUrl.spec.js | 31 - .../utils/truncateWhiteSpace.spec.js | 31 - .../utils/urlStartsWithScheme.spec.js | 41 - .../injectProcessDestinations.spec.js | 176 -- .../Audiences/injectProcessResponse.spec.js | 51 - .../Consent/computeConsentHash.spec.js | 62 - .../Consent/configValidators.spec.js | 38 - .../Consent/createComponent.spec.js | 319 ---- .../Consent/createConsentHashStore.spec.js | 65 - .../Consent/createConsentRequest.spec.js | 40 - .../createConsentRequestPayload.spec.js | 64 - .../Consent/createStoredConsent.spec.js | 56 - .../injectSendSetConsentRequest.spec.js | 133 -- .../Consent/parseConsentCookie.spec.js | 22 - .../Consent/validateSetConsentOptions.spec.js | 131 -- .../Context/createComponent.spec.js | 65 - .../Context/implementationDetails.spec.js | 27 - .../components/Context/injectDevice.spec.js | 127 -- .../Context/injectEnvironment.spec.js | 202 --- .../injectHighEntropyUserAgentHints.spec.js | 50 - .../Context/injectPlaceContext.spec.js | 78 - .../Context/injectTimestamp.spec.js | 32 - .../components/Context/injectWeb.spec.js | 36 - .../components/DataCollector/index.spec.js | 189 --- .../validateApplyResponse.spec.js | 41 - .../validateUserEventOptions.spec.js | 62 - .../EventMerge/createComponent.spec.js | 26 - .../EventMerge/createEventMergeId.spec.js | 24 - .../Identity/addEcidToPayload.spec.js | 23 - ...ppendIdentityToUrlOptionsValidator.spec.js | 49 - .../injectAppendIdentityToUrl.spec.js | 47 - .../Identity/configValidators.spec.js | 41 - .../Identity/createComponent.spec.js | 438 ----- .../Identity/createLegacyIdentity.spec.js | 147 -- .../getIdentity/createGetIdentity.spec.js | 187 --- .../createGetIdentityOptionsValidator.spec.js | 104 -- .../getIdentity/createIdentityRequest.spec.js | 40 - .../createIdentityRequestPayload.spec.js | 45 - .../getNamespacesFromResponse.spec.js | 56 - .../injectAddEcidQueryToPayload.spec.js | 55 - .../injectAddLegacyEcidToPayload.spec.js | 54 - ...ectAddQueryStringIdentityToPayload.spec.js | 190 --- .../injectAwaitIdentityCookie.spec.js | 81 - .../injectEnsureSingleIdentity.spec.js | 219 --- .../injectHandleResponseForIdSyncs.spec.js | 36 - .../Identity/injectProcessIdSyncs.spec.js | 86 - ...SetDomainForInitialIdentityPayload.spec.js | 61 - .../visitorService/awaitVisitorOptIn.spec.js | 77 - .../visitorService/getVisitor.spec.js | 34 - .../injectGetEcidFromVisitor.spec.js | 91 - .../components/LibraryInfo/index.spec.js | 39 - .../createGetInstance.spec.js | 275 --- ...reateMediaAnalyticsBridgeComponent.spec.js | 105 -- .../createMediaHelper.spec.js | 259 --- .../createApplyPropositions.spec.js | 296 ---- .../createClickStorage.spec.js | 122 -- .../Personalization/createComponent.spec.js | 192 --- .../createFetchDataHandler.spec.js | 208 --- .../createGetPageLocation.spec.js | 29 - .../createHandleConsentFlicker.spec.js | 57 - .../createInteractionStorage.spec.js | 178 -- .../createNotificationHandler.spec.js | 64 - .../createOnClickHandler.spec.js | 514 ------ .../createOnDecisionHandler.spec.js | 263 --- .../createPersonalizationDetails.spec.js | 478 ------ .../createSetTargetMigration.spec.js | 42 - .../createViewCacheManager.spec.js | 209 --- .../createViewChangeHandler.spec.js | 80 - .../dom-actions/action.spec.js | 11 - .../addNonceToInlineStyleElements.spec.js | 42 - .../dom-actions/appendHtml.spec.js | 73 - .../dom-actions/clicks/collectClicks.spec.js | 158 -- .../clicks/collectInteractions.spec.js | 615 ------- .../dom-actions/collectInteractions.spec.js | 55 - .../dom-actions/createPreprocess.spec.js | 51 - .../dom-actions/createRedirect.spec.js | 41 - .../dom-actions/customCode.spec.js | 77 - .../dom-actions/dom/createFragment.spec.js | 21 - .../dom-actions/dom/getAttribute.spec.js | 32 - .../dom-actions/dom/getChildNodes.spec.js | 36 - .../dom-actions/dom/getChildren.spec.js | 34 - .../dom-actions/dom/getElementById.spec.js | 35 - .../dom-actions/dom/getFirstChild.spec.js | 32 - .../dom-actions/dom/getNextSibling.spec.js | 35 - .../dom-actions/dom/getNonce.spec.js | 35 - .../dom-actions/dom/getParent.spec.js | 40 - .../dom-actions/dom/helperForEq.spec.js | 44 - .../dom-actions/dom/insertAfter.spec.js | 42 - .../dom-actions/dom/insertBefore.spec.js | 14 - .../dom-actions/dom/isDomElement.spec.js | 46 - .../dom/matchesSelectorWithEq.spec.js | 93 - .../dom-actions/dom/removeAttribute.spec.js | 41 - .../dom-actions/dom/selectNodesWithEq.spec.js | 190 --- .../dom-actions/dom/setAttribute.spec.js | 34 - .../dom-actions/dom/setStyle.spec.js | 43 - .../dom-actions/dom/util.spec.js | 29 - .../dom-actions/images.spec.js | 49 - .../dom-actions/initDomActionsModules.spec.js | 69 - .../dom-actions/insertHtmlAfter.spec.js | 75 - .../dom-actions/insertHtmlBefore.spec.js | 79 - .../Personalization/dom-actions/move.spec.js | 89 - .../dom-actions/prependHtml.spec.js | 88 - .../dom-actions/rearrangeChildren.spec.js | 120 -- .../dom-actions/remapCustomCodeOffers.spec.js | 57 - .../dom-actions/remapHeadOffers.spec.js | 13 - .../dom-actions/remove.spec.js | 56 - .../dom-actions/replaceHtml.spec.js | 74 - .../dom-actions/resize.spec.js | 89 - .../dom-actions/scripts.spec.js | 83 - .../dom-actions/setAttributes.spec.js | 63 - .../dom-actions/setHtml.spec.js | 119 -- .../dom-actions/setImageSource.spec.js | 64 - .../dom-actions/setStyles.spec.js | 63 - .../dom-actions/setText.spec.js | 63 - .../dom-actions/swapImage.spec.js | 14 - .../Personalization/flicker/index.spec.js | 56 - .../createDecorateProposition.spec.js | 253 --- .../handlers/createProcessDomAction.spec.js | 196 --- .../handlers/createProcessHtmlContent.spec.js | 137 -- .../createProcessInAppMessage.spec.js | 139 -- .../createProcessPropositions.spec.js | 329 ---- .../handlers/createProcessRedirect.spec.js | 106 -- .../handlers/injectCreateProposition.spec.js | 82 - .../handlers/processDefaultContent.spec.js | 23 - .../actions/displayIframeContent.spec.js | 458 ----- .../initInAppMessageActionsModules.spec.js | 27 - .../in-app-message-actions/utils.spec.js | 40 - .../responsesMock/eventResponses.js | 449 ----- .../Personalization/topLevel/buildAlloy.js | 251 --- .../Personalization/topLevel/buildMocks.js | 95 -- .../topLevel/cartViewDecisions.spec.js | 274 --- .../topLevel/mergedMetricDecisions.spec.js | 121 -- .../topLevel/mixedPropositions.spec.js | 297 ---- ...eDecisionsWithDomActionSchemaItems.spec.js | 182 -- .../topLevel/pageWideScopeDecisions.spec.js | 232 --- ...cisionsWithoutDomActionSchemaItems.spec.js | 105 -- .../topLevel/productsViewDecisions.spec.js | 54 - .../redirectPageWideScopeDecision.spec.js | 71 - .../Personalization/topLevel/resetMocks.js | 29 - .../topLevel/scopesFoo1Foo2Decisions.spec.js | 151 -- .../addRenderAttemptedToDecisions.spec.js | 34 - .../utils/createAsyncArray.spec.js | 53 - .../utils/isAuthoringModeEnabled.spec.js | 33 - .../Personalization/utils/metaUtils.spec.js | 194 --- .../utils/surfaceUtils.spec.js | 256 --- .../validateApplyPropositionsOptions.spec.js | 255 --- .../inAppMessageConsequenceAdapter.spec.js | 63 - .../schemaTypeConsequenceAdapter.spec.js | 70 - .../components/RulesEngine/constants.spec.js | 11 - .../RulesEngine/contextTestUtils.js | 153 -- .../RulesEngine/createApplyResponse.spec.js | 91 - .../createConsequenceAdapter.spec.js | 81 - .../RulesEngine/createContextProvider.spec.js | 156 -- .../RulesEngine/createDecisionHistory.spec.js | 63 - .../createDecisionProvider.spec.js | 474 ------ .../createEvaluableRulesetPayload.spec.js | 354 ---- .../createEvaluateRulesetsCommand.spec.js | 216 --- .../RulesEngine/createEventRegistry.spec.js | 418 ----- .../createOnResponseHandler.spec.js | 394 ----- .../createSubscribeRulesetItems.spec.js | 1494 ----------------- .../decisioningContext.browser.spec.js | 56 - .../decisioningContext.page.spec.js | 349 ---- .../decisioningContext.referringPage.spec.js | 288 ---- .../decisioningContext.sdkVersion.spec.js | 59 - .../decisioningContext.timestamp.spec.js | 367 ---- .../decisioningContext.window.spec.js | 185 -- .../components/RulesEngine/index.spec.js | 203 --- .../components/RulesEngine/utils.spec.js | 219 --- .../StreamingMedia/configValidators.spec.js | 63 - .../createMediaEventManager.spec.js | 148 -- .../StreamingMedia/createMediaRequest.spec.js | 26 - .../createMediaResponseHandler.spec.js | 86 - .../createMediaSessionCacheManager.spec.js | 65 - .../createStreamingMediaComponent.spec.js | 78 - .../createTrackMediaEvent.spec.js | 95 -- .../createTrackMediaSession.spec.js | 127 -- .../validateMediaEventOptions.spec.js | 64 - .../validateMediaSessionOptions.spec.js | 64 - .../specs/core/buildAndValidateConfig.spec.js | 116 -- .../unit/specs/core/componentCreators.spec.js | 36 - .../specs/core/config/createConfig.spec.js | 63 - .../core/config/createCoreConfigs.spec.js | 134 -- .../specs/core/consent/createConsent.spec.js | 75 - .../consent/createConsentStateMachine.spec.js | 192 --- .../core/createComponentRegistry.spec.js | 220 --- .../specs/core/createCookieTransfer.spec.js | 186 -- test/unit/specs/core/createEvent.spec.js | 402 ----- .../specs/core/createEventManager.spec.js | 393 ----- .../specs/core/createInstanceFunction.spec.js | 52 - test/unit/specs/core/createLifecycle.spec.js | 88 - .../specs/core/createLogController.spec.js | 239 --- test/unit/specs/core/createLogger.spec.js | 253 --- .../edgeNetwork/handleRequestFailure.spec.js | 34 - .../edgeNetwork/injectApplyResponse.spec.js | 250 --- .../edgeNetwork/injectExtractEdgeInfo.spec.js | 47 - .../edgeNetwork/injectGetLocationHint.spec.js | 52 - .../injectProcessWarningsAndErrors.spec.js | 125 -- .../injectSendEdgeNetworkRequest.spec.js | 492 ------ .../mergeLifecycleResponses.spec.js | 115 -- .../specs/core/initializeComponents.spec.js | 105 -- .../specs/core/injectCreateResponse.spec.js | 148 -- .../specs/core/injectExecuteCommand.spec.js | 321 ---- .../unit/specs/core/injectHandleError.spec.js | 52 - .../core/injectShouldTransferCookie.spec.js | 58 - .../core/network/getRequestRetryDelay.spec.js | 104 -- .../network/injectSendNetworkRequest.spec.js | 246 --- .../core/network/isRequestRetryable.spec.js | 49 - .../injectSendBeaconRequest.spec.js | 80 - .../injectSendFetchRequest.spec.js | 51 - .../core/requiredComponentCreators.spec.js | 36 - .../specs/core/validateCommandOptions.spec.js | 53 - test/unit/specs/karmaEntry.spec.cjs | 18 - .../utils/assignConcatArrayValues.spec.js | 76 - test/unit/specs/utils/clone.spec.js | 29 - test/unit/specs/utils/cookieJar.spec.js | 14 - test/unit/specs/utils/crc32.spec.js | 674 -------- .../utils/createCallbackAggregator.spec.js | 47 - test/unit/specs/utils/createCollect.spec.js | 51 - .../utils/createLoggingCookieJar.spec.js | 51 - test/unit/specs/utils/createMerger.spec.js | 73 - .../specs/utils/createSubscription.spec.js | 167 -- test/unit/specs/utils/createTaskQueue.spec.js | 152 -- .../utils/decodeUriComponentSafely.spec.js | 27 - .../unit/specs/utils/deduplicateArray.spec.js | 44 - test/unit/specs/utils/deepAssign.spec.js | 78 - test/unit/specs/utils/defer.spec.js | 36 - test/unit/specs/utils/dom/appendNode.spec.js | 30 - test/unit/specs/utils/dom/createNode.spec.js | 47 - .../specs/utils/dom/isShadowSelector.spec.js | 25 - .../specs/utils/dom/matchesSelector.spec.js | 23 - .../specs/utils/dom/querySelectorAll.spec.js | 42 - test/unit/specs/utils/dom/removeNode.spec.js | 30 - test/unit/specs/utils/dom/selectNodes.spec.js | 23 - .../utils/dom/selectNodesWithShadow.spec.js | 206 --- test/unit/specs/utils/event.spec.js | 71 - test/unit/specs/utils/filterObject.spec.js | 43 - test/unit/specs/utils/fireImage.spec.js | 14 - test/unit/specs/utils/flattenArray.spec.js | 73 - test/unit/specs/utils/flattenObject.spec.js | 116 -- test/unit/specs/utils/getApexDomain.spec.js | 71 - .../specs/utils/getLastArrayItems.spec.js | 26 - .../utils/getNamespacedCookieName.spec.js | 20 - .../specs/utils/getNamespacedStorage.spec.js | 27 - test/unit/specs/utils/groupBy.spec.js | 58 - ...hirdPartyCookiesSupportedByDefault.spec.js | 50 - .../injectDoesIdentityCookieExist.spec.js | 37 - .../injectFireReferrerHideableImage.spec.js | 83 - .../unit/specs/utils/injectGetBrowser.spec.js | 66 - test/unit/specs/utils/injectStorage.spec.js | 111 -- test/unit/specs/utils/intersection.spec.js | 25 - test/unit/specs/utils/isBlankString.spec.js | 31 - test/unit/specs/utils/isBoolean.spec.js | 28 - test/unit/specs/utils/isEmptyObject.spec.js | 27 - test/unit/specs/utils/isFunction.spec.js | 27 - test/unit/specs/utils/isInteger.spec.js | 26 - .../utils/isNamespacedCookieName.spec.js | 31 - test/unit/specs/utils/isNil.spec.js | 32 - test/unit/specs/utils/isNonEmptyArray.spec.js | 32 - .../unit/specs/utils/isNonEmptyString.spec.js | 27 - test/unit/specs/utils/isNumber.spec.js | 28 - test/unit/specs/utils/isObject.spec.js | 27 - test/unit/specs/utils/isString.spec.js | 27 - test/unit/specs/utils/isUnique.spec.js | 23 - test/unit/specs/utils/isValidRegExp.spec.js | 26 - test/unit/specs/utils/lazy.spec.js | 54 - test/unit/specs/utils/noop.spec.js | 19 - test/unit/specs/utils/parseUrl.spec.js | 63 - .../prepareConfigOverridesForEdge.spec.js | 71 - test/unit/specs/utils/querystring.spec.js | 14 - .../utils/request/createAddIdentity.spec.js | 80 - .../createDataCollectionRequest.spec.js | 74 - ...createDataCollectionRequestPayload.spec.js | 76 - ...eGetAssuranceValidationTokenParams.spec.js | 67 - .../utils/request/createHasIdentity.spec.js | 43 - .../specs/utils/request/createRequest.spec.js | 17 - .../utils/request/createRequestParams.spec.js | 65 - .../request/createRequestPayload.spec.js | 17 - .../utils/sanitizeOrgIdForCookieName.spec.js | 20 - test/unit/specs/utils/stackError.spec.js | 33 - test/unit/specs/utils/stringToBoolean.spec.js | 26 - test/unit/specs/utils/toArray.spec.js | 35 - test/unit/specs/utils/toError.spec.js | 28 - .../unit/specs/utils/toISOStringLocal.spec.js | 38 - test/unit/specs/utils/toInteger.spec.js | 42 - .../specs/utils/updateErrorMessage.spec.js | 35 - test/unit/specs/utils/uuid.spec.js | 14 - .../utils/validateConfigOverride.spec.js | 68 - .../specs/utils/validateIdentityMap.spec.js | 36 - .../validation/anythingValidator.spec.js | 50 - .../utils/validation/booleanValidator.spec.js | 43 - .../validation/callbackValidator.spec.js | 40 - .../validation/createAnyOfValidator.spec.js | 57 - .../validation/createArrayOfValidator.spec.js | 52 - .../validation/createDefaultValidator.spec.js | 23 - .../createDeprecatedValidator.spec.js | 65 - .../validation/createLiteralValidator.spec.js | 31 - .../createMapOfValuesValidator.spec.js | 45 - .../validation/createMaximumValidator.spec.js | 44 - .../validation/createMinimumValidator.spec.js | 45 - .../createNoUnknownFieldsValidator.spec.js | 47 - .../createNonEmptyValidator.spec.js | 37 - .../createObjectOfValidator.spec.js | 74 - .../validation/createRenamedValidator.spec.js | 51 - .../createUniqueItemsValidator.spec.js | 53 - .../validation/createUniqueValidator.spec.js | 47 - .../utils/validation/domainValidator.spec.js | 24 - .../utils/validation/enumOfValidator.spec.js | 41 - .../utils/validation/integerValidator.spec.js | 37 - .../validation/matchesRegexpValidator.spec.js | 39 - .../utils/validation/numberValidator.spec.js | 40 - .../utils/validation/regexpValidator.spec.js | 38 - .../validation/requiredValidator.spec.js | 23 - .../utils/validation/stringValidator.spec.js | 37 - .../unit/specs/utils/validation/utils.spec.js | 88 - 351 files changed, 35355 deletions(-) delete mode 100644 test/unit/constants/uuidV4Regex.js delete mode 100644 test/unit/helpers/assertFunctionCallOrder.js delete mode 100644 test/unit/helpers/cleanUpDomChanges.js delete mode 100644 test/unit/helpers/createDecoratePropositionForTest.js delete mode 100644 test/unit/helpers/createMockProposition.js delete mode 100644 test/unit/helpers/describeRequest.js delete mode 100644 test/unit/helpers/describeRequestPayload.js delete mode 100644 test/unit/helpers/describeValidation.js delete mode 100644 test/unit/helpers/flushPromiseChains.js delete mode 100644 test/unit/helpers/pause.js delete mode 100644 test/unit/helpers/removeAllCookies.js delete mode 100644 test/unit/helpers/testConfigValidators.js delete mode 100644 test/unit/specs/components/ActivityCollector/attachClickActivityCollector.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/configValidators.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/createClickActivityStorage.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/createClickedElementProperties.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/createGetClickedElementProperties.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/createInjectClickedElementProperties.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/createRecallAndInjectClickedElementProperties.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/createStorePageViewProperties.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/getLinkName.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/getLinkRegion.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/activityMapExtensionEnabled.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/createTransientStorage.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/determineLinkType.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/dom/elementHasClickHandler.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/dom/extractDomain.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/dom/findClickableElement.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/dom/isButtonSubmitElement.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/dom/isDownloadLink.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/dom/isExitLink.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/dom/isInputSubmitElement.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/dom/isSupportedAnchorElement.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/dom/isSupportedTextNode.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/hasPageName.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/isDifferentDomains.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/trimQueryFromUrl.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/truncateWhiteSpace.spec.js delete mode 100644 test/unit/specs/components/ActivityCollector/utils/urlStartsWithScheme.spec.js delete mode 100644 test/unit/specs/components/Audiences/injectProcessDestinations.spec.js delete mode 100644 test/unit/specs/components/Audiences/injectProcessResponse.spec.js delete mode 100644 test/unit/specs/components/Consent/computeConsentHash.spec.js delete mode 100644 test/unit/specs/components/Consent/configValidators.spec.js delete mode 100644 test/unit/specs/components/Consent/createComponent.spec.js delete mode 100644 test/unit/specs/components/Consent/createConsentHashStore.spec.js delete mode 100644 test/unit/specs/components/Consent/createConsentRequest.spec.js delete mode 100644 test/unit/specs/components/Consent/createConsentRequestPayload.spec.js delete mode 100644 test/unit/specs/components/Consent/createStoredConsent.spec.js delete mode 100644 test/unit/specs/components/Consent/injectSendSetConsentRequest.spec.js delete mode 100644 test/unit/specs/components/Consent/parseConsentCookie.spec.js delete mode 100644 test/unit/specs/components/Consent/validateSetConsentOptions.spec.js delete mode 100644 test/unit/specs/components/Context/createComponent.spec.js delete mode 100644 test/unit/specs/components/Context/implementationDetails.spec.js delete mode 100644 test/unit/specs/components/Context/injectDevice.spec.js delete mode 100644 test/unit/specs/components/Context/injectEnvironment.spec.js delete mode 100644 test/unit/specs/components/Context/injectHighEntropyUserAgentHints.spec.js delete mode 100644 test/unit/specs/components/Context/injectPlaceContext.spec.js delete mode 100644 test/unit/specs/components/Context/injectTimestamp.spec.js delete mode 100644 test/unit/specs/components/Context/injectWeb.spec.js delete mode 100644 test/unit/specs/components/DataCollector/index.spec.js delete mode 100644 test/unit/specs/components/DataCollector/validateApplyResponse.spec.js delete mode 100644 test/unit/specs/components/DataCollector/validateUserEventOptions.spec.js delete mode 100644 test/unit/specs/components/EventMerge/createComponent.spec.js delete mode 100644 test/unit/specs/components/EventMerge/createEventMergeId.spec.js delete mode 100644 test/unit/specs/components/Identity/addEcidToPayload.spec.js delete mode 100644 test/unit/specs/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.spec.js delete mode 100644 test/unit/specs/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.spec.js delete mode 100644 test/unit/specs/components/Identity/configValidators.spec.js delete mode 100644 test/unit/specs/components/Identity/createComponent.spec.js delete mode 100644 test/unit/specs/components/Identity/createLegacyIdentity.spec.js delete mode 100644 test/unit/specs/components/Identity/getIdentity/createGetIdentity.spec.js delete mode 100644 test/unit/specs/components/Identity/getIdentity/createGetIdentityOptionsValidator.spec.js delete mode 100644 test/unit/specs/components/Identity/getIdentity/createIdentityRequest.spec.js delete mode 100644 test/unit/specs/components/Identity/getIdentity/createIdentityRequestPayload.spec.js delete mode 100644 test/unit/specs/components/Identity/getNamespacesFromResponse.spec.js delete mode 100644 test/unit/specs/components/Identity/injectAddEcidQueryToPayload.spec.js delete mode 100644 test/unit/specs/components/Identity/injectAddLegacyEcidToPayload.spec.js delete mode 100644 test/unit/specs/components/Identity/injectAddQueryStringIdentityToPayload.spec.js delete mode 100644 test/unit/specs/components/Identity/injectAwaitIdentityCookie.spec.js delete mode 100644 test/unit/specs/components/Identity/injectEnsureSingleIdentity.spec.js delete mode 100644 test/unit/specs/components/Identity/injectHandleResponseForIdSyncs.spec.js delete mode 100644 test/unit/specs/components/Identity/injectProcessIdSyncs.spec.js delete mode 100644 test/unit/specs/components/Identity/injectSetDomainForInitialIdentityPayload.spec.js delete mode 100644 test/unit/specs/components/Identity/visitorService/awaitVisitorOptIn.spec.js delete mode 100644 test/unit/specs/components/Identity/visitorService/getVisitor.spec.js delete mode 100644 test/unit/specs/components/Identity/visitorService/injectGetEcidFromVisitor.spec.js delete mode 100644 test/unit/specs/components/LibraryInfo/index.spec.js delete mode 100644 test/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js delete mode 100644 test/unit/specs/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.spec.js delete mode 100644 test/unit/specs/components/MediaAnalyticsBridge/createMediaHelper.spec.js delete mode 100644 test/unit/specs/components/Personalization/createApplyPropositions.spec.js delete mode 100644 test/unit/specs/components/Personalization/createClickStorage.spec.js delete mode 100644 test/unit/specs/components/Personalization/createComponent.spec.js delete mode 100644 test/unit/specs/components/Personalization/createFetchDataHandler.spec.js delete mode 100644 test/unit/specs/components/Personalization/createGetPageLocation.spec.js delete mode 100644 test/unit/specs/components/Personalization/createHandleConsentFlicker.spec.js delete mode 100644 test/unit/specs/components/Personalization/createInteractionStorage.spec.js delete mode 100644 test/unit/specs/components/Personalization/createNotificationHandler.spec.js delete mode 100644 test/unit/specs/components/Personalization/createOnClickHandler.spec.js delete mode 100644 test/unit/specs/components/Personalization/createOnDecisionHandler.spec.js delete mode 100644 test/unit/specs/components/Personalization/createPersonalizationDetails.spec.js delete mode 100644 test/unit/specs/components/Personalization/createSetTargetMigration.spec.js delete mode 100644 test/unit/specs/components/Personalization/createViewCacheManager.spec.js delete mode 100644 test/unit/specs/components/Personalization/createViewChangeHandler.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/action.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/addNonceToInlineStyleElements.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/clicks/collectInteractions.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/collectInteractions.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/createPreprocess.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/customCode.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/createFragment.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/getAttribute.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/getChildNodes.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/getChildren.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/getElementById.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/getFirstChild.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/getNextSibling.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/getNonce.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/getParent.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/helperForEq.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/insertAfter.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/insertBefore.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/removeAttribute.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/setAttribute.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/setStyle.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/dom/util.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/images.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/move.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/remapCustomCodeOffers.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/remapHeadOffers.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/remove.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/resize.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/scripts.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/setHtml.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/setStyles.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/setText.spec.js delete mode 100644 test/unit/specs/components/Personalization/dom-actions/swapImage.spec.js delete mode 100644 test/unit/specs/components/Personalization/flicker/index.spec.js delete mode 100644 test/unit/specs/components/Personalization/handlers/createDecorateProposition.spec.js delete mode 100644 test/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js delete mode 100644 test/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js delete mode 100644 test/unit/specs/components/Personalization/handlers/createProcessInAppMessage.spec.js delete mode 100644 test/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js delete mode 100644 test/unit/specs/components/Personalization/handlers/createProcessRedirect.spec.js delete mode 100644 test/unit/specs/components/Personalization/handlers/injectCreateProposition.spec.js delete mode 100644 test/unit/specs/components/Personalization/handlers/processDefaultContent.spec.js delete mode 100644 test/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js delete mode 100644 test/unit/specs/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.spec.js delete mode 100644 test/unit/specs/components/Personalization/in-app-message-actions/utils.spec.js delete mode 100644 test/unit/specs/components/Personalization/responsesMock/eventResponses.js delete mode 100644 test/unit/specs/components/Personalization/topLevel/buildAlloy.js delete mode 100644 test/unit/specs/components/Personalization/topLevel/buildMocks.js delete mode 100644 test/unit/specs/components/Personalization/topLevel/cartViewDecisions.spec.js delete mode 100644 test/unit/specs/components/Personalization/topLevel/mergedMetricDecisions.spec.js delete mode 100644 test/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js delete mode 100644 test/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js delete mode 100644 test/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js delete mode 100644 test/unit/specs/components/Personalization/topLevel/pageWideScopeDecisionsWithoutDomActionSchemaItems.spec.js delete mode 100644 test/unit/specs/components/Personalization/topLevel/productsViewDecisions.spec.js delete mode 100644 test/unit/specs/components/Personalization/topLevel/redirectPageWideScopeDecision.spec.js delete mode 100644 test/unit/specs/components/Personalization/topLevel/resetMocks.js delete mode 100644 test/unit/specs/components/Personalization/topLevel/scopesFoo1Foo2Decisions.spec.js delete mode 100644 test/unit/specs/components/Personalization/utils/addRenderAttemptedToDecisions.spec.js delete mode 100644 test/unit/specs/components/Personalization/utils/createAsyncArray.spec.js delete mode 100644 test/unit/specs/components/Personalization/utils/isAuthoringModeEnabled.spec.js delete mode 100644 test/unit/specs/components/Personalization/utils/metaUtils.spec.js delete mode 100644 test/unit/specs/components/Personalization/utils/surfaceUtils.spec.js delete mode 100644 test/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/constants.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/contextTestUtils.js delete mode 100644 test/unit/specs/components/RulesEngine/createApplyResponse.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/createConsequenceAdapter.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/createContextProvider.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/createDecisionHistory.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/createDecisionProvider.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/createEvaluableRulesetPayload.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/createEvaluateRulesetsCommand.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/createEventRegistry.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/createOnResponseHandler.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/createSubscribeRulesetItems.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/decisioningContext.browser.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/decisioningContext.page.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/decisioningContext.referringPage.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/decisioningContext.sdkVersion.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/decisioningContext.timestamp.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/decisioningContext.window.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/index.spec.js delete mode 100644 test/unit/specs/components/RulesEngine/utils.spec.js delete mode 100644 test/unit/specs/components/StreamingMedia/configValidators.spec.js delete mode 100644 test/unit/specs/components/StreamingMedia/createMediaEventManager.spec.js delete mode 100644 test/unit/specs/components/StreamingMedia/createMediaRequest.spec.js delete mode 100644 test/unit/specs/components/StreamingMedia/createMediaResponseHandler.spec.js delete mode 100644 test/unit/specs/components/StreamingMedia/createMediaSessionCacheManager.spec.js delete mode 100644 test/unit/specs/components/StreamingMedia/createStreamingMediaComponent.spec.js delete mode 100644 test/unit/specs/components/StreamingMedia/createTrackMediaEvent.spec.js delete mode 100644 test/unit/specs/components/StreamingMedia/createTrackMediaSession.spec.js delete mode 100644 test/unit/specs/components/StreamingMedia/validateMediaEventOptions.spec.js delete mode 100644 test/unit/specs/components/StreamingMedia/validateMediaSessionOptions.spec.js delete mode 100644 test/unit/specs/core/buildAndValidateConfig.spec.js delete mode 100644 test/unit/specs/core/componentCreators.spec.js delete mode 100644 test/unit/specs/core/config/createConfig.spec.js delete mode 100644 test/unit/specs/core/config/createCoreConfigs.spec.js delete mode 100644 test/unit/specs/core/consent/createConsent.spec.js delete mode 100644 test/unit/specs/core/consent/createConsentStateMachine.spec.js delete mode 100644 test/unit/specs/core/createComponentRegistry.spec.js delete mode 100644 test/unit/specs/core/createCookieTransfer.spec.js delete mode 100644 test/unit/specs/core/createEvent.spec.js delete mode 100644 test/unit/specs/core/createEventManager.spec.js delete mode 100644 test/unit/specs/core/createInstanceFunction.spec.js delete mode 100644 test/unit/specs/core/createLifecycle.spec.js delete mode 100644 test/unit/specs/core/createLogController.spec.js delete mode 100644 test/unit/specs/core/createLogger.spec.js delete mode 100644 test/unit/specs/core/edgeNetwork/handleRequestFailure.spec.js delete mode 100644 test/unit/specs/core/edgeNetwork/injectApplyResponse.spec.js delete mode 100644 test/unit/specs/core/edgeNetwork/injectExtractEdgeInfo.spec.js delete mode 100644 test/unit/specs/core/edgeNetwork/injectGetLocationHint.spec.js delete mode 100644 test/unit/specs/core/edgeNetwork/injectProcessWarningsAndErrors.spec.js delete mode 100644 test/unit/specs/core/edgeNetwork/injectSendEdgeNetworkRequest.spec.js delete mode 100644 test/unit/specs/core/edgeNetwork/mergeLifecycleResponses.spec.js delete mode 100644 test/unit/specs/core/initializeComponents.spec.js delete mode 100644 test/unit/specs/core/injectCreateResponse.spec.js delete mode 100644 test/unit/specs/core/injectExecuteCommand.spec.js delete mode 100644 test/unit/specs/core/injectHandleError.spec.js delete mode 100644 test/unit/specs/core/injectShouldTransferCookie.spec.js delete mode 100644 test/unit/specs/core/network/getRequestRetryDelay.spec.js delete mode 100644 test/unit/specs/core/network/injectSendNetworkRequest.spec.js delete mode 100644 test/unit/specs/core/network/isRequestRetryable.spec.js delete mode 100644 test/unit/specs/core/network/requestMethods/injectSendBeaconRequest.spec.js delete mode 100644 test/unit/specs/core/network/requestMethods/injectSendFetchRequest.spec.js delete mode 100644 test/unit/specs/core/requiredComponentCreators.spec.js delete mode 100644 test/unit/specs/core/validateCommandOptions.spec.js delete mode 100644 test/unit/specs/karmaEntry.spec.cjs delete mode 100644 test/unit/specs/utils/assignConcatArrayValues.spec.js delete mode 100644 test/unit/specs/utils/clone.spec.js delete mode 100644 test/unit/specs/utils/cookieJar.spec.js delete mode 100644 test/unit/specs/utils/crc32.spec.js delete mode 100644 test/unit/specs/utils/createCallbackAggregator.spec.js delete mode 100644 test/unit/specs/utils/createCollect.spec.js delete mode 100644 test/unit/specs/utils/createLoggingCookieJar.spec.js delete mode 100644 test/unit/specs/utils/createMerger.spec.js delete mode 100644 test/unit/specs/utils/createSubscription.spec.js delete mode 100644 test/unit/specs/utils/createTaskQueue.spec.js delete mode 100644 test/unit/specs/utils/decodeUriComponentSafely.spec.js delete mode 100644 test/unit/specs/utils/deduplicateArray.spec.js delete mode 100644 test/unit/specs/utils/deepAssign.spec.js delete mode 100644 test/unit/specs/utils/defer.spec.js delete mode 100644 test/unit/specs/utils/dom/appendNode.spec.js delete mode 100644 test/unit/specs/utils/dom/createNode.spec.js delete mode 100644 test/unit/specs/utils/dom/isShadowSelector.spec.js delete mode 100644 test/unit/specs/utils/dom/matchesSelector.spec.js delete mode 100644 test/unit/specs/utils/dom/querySelectorAll.spec.js delete mode 100644 test/unit/specs/utils/dom/removeNode.spec.js delete mode 100644 test/unit/specs/utils/dom/selectNodes.spec.js delete mode 100644 test/unit/specs/utils/dom/selectNodesWithShadow.spec.js delete mode 100644 test/unit/specs/utils/event.spec.js delete mode 100644 test/unit/specs/utils/filterObject.spec.js delete mode 100644 test/unit/specs/utils/fireImage.spec.js delete mode 100644 test/unit/specs/utils/flattenArray.spec.js delete mode 100644 test/unit/specs/utils/flattenObject.spec.js delete mode 100644 test/unit/specs/utils/getApexDomain.spec.js delete mode 100644 test/unit/specs/utils/getLastArrayItems.spec.js delete mode 100644 test/unit/specs/utils/getNamespacedCookieName.spec.js delete mode 100644 test/unit/specs/utils/getNamespacedStorage.spec.js delete mode 100644 test/unit/specs/utils/groupBy.spec.js delete mode 100644 test/unit/specs/utils/injectAreThirdPartyCookiesSupportedByDefault.spec.js delete mode 100644 test/unit/specs/utils/injectDoesIdentityCookieExist.spec.js delete mode 100644 test/unit/specs/utils/injectFireReferrerHideableImage.spec.js delete mode 100644 test/unit/specs/utils/injectGetBrowser.spec.js delete mode 100644 test/unit/specs/utils/injectStorage.spec.js delete mode 100644 test/unit/specs/utils/intersection.spec.js delete mode 100644 test/unit/specs/utils/isBlankString.spec.js delete mode 100644 test/unit/specs/utils/isBoolean.spec.js delete mode 100644 test/unit/specs/utils/isEmptyObject.spec.js delete mode 100644 test/unit/specs/utils/isFunction.spec.js delete mode 100644 test/unit/specs/utils/isInteger.spec.js delete mode 100644 test/unit/specs/utils/isNamespacedCookieName.spec.js delete mode 100644 test/unit/specs/utils/isNil.spec.js delete mode 100644 test/unit/specs/utils/isNonEmptyArray.spec.js delete mode 100644 test/unit/specs/utils/isNonEmptyString.spec.js delete mode 100644 test/unit/specs/utils/isNumber.spec.js delete mode 100644 test/unit/specs/utils/isObject.spec.js delete mode 100644 test/unit/specs/utils/isString.spec.js delete mode 100644 test/unit/specs/utils/isUnique.spec.js delete mode 100644 test/unit/specs/utils/isValidRegExp.spec.js delete mode 100644 test/unit/specs/utils/lazy.spec.js delete mode 100644 test/unit/specs/utils/noop.spec.js delete mode 100644 test/unit/specs/utils/parseUrl.spec.js delete mode 100644 test/unit/specs/utils/prepareConfigOverridesForEdge.spec.js delete mode 100644 test/unit/specs/utils/querystring.spec.js delete mode 100644 test/unit/specs/utils/request/createAddIdentity.spec.js delete mode 100644 test/unit/specs/utils/request/createDataCollectionRequest.spec.js delete mode 100644 test/unit/specs/utils/request/createDataCollectionRequestPayload.spec.js delete mode 100644 test/unit/specs/utils/request/createGetAssuranceValidationTokenParams.spec.js delete mode 100644 test/unit/specs/utils/request/createHasIdentity.spec.js delete mode 100644 test/unit/specs/utils/request/createRequest.spec.js delete mode 100644 test/unit/specs/utils/request/createRequestParams.spec.js delete mode 100644 test/unit/specs/utils/request/createRequestPayload.spec.js delete mode 100644 test/unit/specs/utils/sanitizeOrgIdForCookieName.spec.js delete mode 100644 test/unit/specs/utils/stackError.spec.js delete mode 100644 test/unit/specs/utils/stringToBoolean.spec.js delete mode 100644 test/unit/specs/utils/toArray.spec.js delete mode 100644 test/unit/specs/utils/toError.spec.js delete mode 100644 test/unit/specs/utils/toISOStringLocal.spec.js delete mode 100644 test/unit/specs/utils/toInteger.spec.js delete mode 100644 test/unit/specs/utils/updateErrorMessage.spec.js delete mode 100644 test/unit/specs/utils/uuid.spec.js delete mode 100644 test/unit/specs/utils/validateConfigOverride.spec.js delete mode 100644 test/unit/specs/utils/validateIdentityMap.spec.js delete mode 100644 test/unit/specs/utils/validation/anythingValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/booleanValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/callbackValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createAnyOfValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createArrayOfValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createDefaultValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createDeprecatedValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createLiteralValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createMapOfValuesValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createMaximumValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createMinimumValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createNoUnknownFieldsValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createNonEmptyValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createObjectOfValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createRenamedValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createUniqueItemsValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/createUniqueValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/domainValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/enumOfValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/integerValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/matchesRegexpValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/numberValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/regexpValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/requiredValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/stringValidator.spec.js delete mode 100644 test/unit/specs/utils/validation/utils.spec.js diff --git a/test/unit/constants/uuidV4Regex.js b/test/unit/constants/uuidV4Regex.js deleted file mode 100644 index bf9c0bd8b..000000000 --- a/test/unit/constants/uuidV4Regex.js +++ /dev/null @@ -1,13 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -export default /^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i; diff --git a/test/unit/helpers/assertFunctionCallOrder.js b/test/unit/helpers/assertFunctionCallOrder.js deleted file mode 100644 index 9135c7b94..000000000 --- a/test/unit/helpers/assertFunctionCallOrder.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -/** - * Asserts that functions were called in a particular order. - * @param {Array} orderedFunctions The array of functions in the order - * they should have been called. - */ -export default (orderedFunctions) => { - for (let i = 0; i < orderedFunctions.length - 1; i += 1) { - const fn = orderedFunctions[i]; - const nextFn = orderedFunctions[i + 1]; - expect(fn).toHaveBeenCalledBefore(nextFn); - } -}; diff --git a/test/unit/helpers/cleanUpDomChanges.js b/test/unit/helpers/cleanUpDomChanges.js deleted file mode 100644 index 2413a6725..000000000 --- a/test/unit/helpers/cleanUpDomChanges.js +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { selectNodes, removeNode } from "../../../src/utils/dom/index.js"; - -/** - * Removes container DOM nodes used for all the - * personalization actions - */ -export default (id) => { - selectNodes(`#${id}`).forEach(removeNode); - selectNodes("style").forEach((node) => { - if (node.textContent.indexOf(id) !== -1) { - removeNode(node); - } - }); -}; diff --git a/test/unit/helpers/createDecoratePropositionForTest.js b/test/unit/helpers/createDecoratePropositionForTest.js deleted file mode 100644 index 43528d0d9..000000000 --- a/test/unit/helpers/createDecoratePropositionForTest.js +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - ADOBE_JOURNEY_OPTIMIZER, - ADOBE_TARGET, -} from "../../../src/constants/decisionProvider.js"; -import createInteractionStorage from "../../../src/components/Personalization/createInteractionStorage.js"; -import createDecorateProposition from "../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { - ALWAYS, - NEVER, -} from "../../../src/constants/propositionInteractionType.js"; - -export default ({ - autoCollectPropositionInteractions = { - [ADOBE_JOURNEY_OPTIMIZER]: ALWAYS, - [ADOBE_TARGET]: NEVER, - }, - type, - propositionId = "propositionID", - itemId = "itemId", - trackingLabel = "trackingLabel", - scopeType = "page", - notification = { - id: "notifyId", - scope: "web://mywebsite.com", - scopeDetails: { - something: true, - decisionProvider: ADOBE_JOURNEY_OPTIMIZER, - }, - }, -} = {}) => { - const { storeInteractionMeta } = createInteractionStorage(); - return createDecorateProposition( - autoCollectPropositionInteractions, - type, - propositionId, - itemId, - trackingLabel, - scopeType, - notification, - storeInteractionMeta, - ); -}; diff --git a/test/unit/helpers/createMockProposition.js b/test/unit/helpers/createMockProposition.js deleted file mode 100644 index dafab212f..000000000 --- a/test/unit/helpers/createMockProposition.js +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import injectCreateProposition from "../../../src/components/Personalization/handlers/injectCreateProposition.js"; - -const createProposition = injectCreateProposition({ - preprocess: (data) => data, - isPageWideSurface: () => false, -}); - -export default (item, scopeDetails = {}) => { - return createProposition({ - id: "id", - scope: "scope", - scopeDetails: { - decisionProvider: "AJO", - ...scopeDetails, - }, - items: [item], - }); -}; diff --git a/test/unit/helpers/describeRequest.js b/test/unit/helpers/describeRequest.js deleted file mode 100644 index 34193403a..000000000 --- a/test/unit/helpers/describeRequest.js +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import uuidV4Regex from "../constants/uuidV4Regex.js"; - -/** - * Tests the base methods that all types of requests share. - */ -export default (createRequest) => { - describe("base request functionality", () => { - let payload; - let request; - - beforeEach(() => { - payload = {}; - request = createRequest({ payload }); - }); - - // getAction and getUseSendBeacon will be covered in the tests - // for the request modules that leverage this base request. - - it("provides an ID", () => { - expect(request.getId()).toMatch(uuidV4Regex); - }); - - it("provides payload", () => { - expect(request.getPayload()).toBe(payload); - }); - - it("provides useThirdPartyDomain", () => { - expect(request.getUseIdThirdPartyDomain()).toBeFalse(); - request.setUseIdThirdPartyDomain(); - expect(request.getUseIdThirdPartyDomain()).toBeTrue(); - }); - - it("sets isIdentityEstablished", () => { - // We only test that isIdentityEstablished is a function. - // It sets an internal variable that's passed into - // getAction and getUseSendBeacon. This part will be covered in the tests - // for the request modules that leverage this base request. - expect(request.setIsIdentityEstablished).toEqual(jasmine.any(Function)); - }); - }); -}; diff --git a/test/unit/helpers/describeRequestPayload.js b/test/unit/helpers/describeRequestPayload.js deleted file mode 100644 index 98e066424..000000000 --- a/test/unit/helpers/describeRequestPayload.js +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -/** - * Tests the base methods that all types of request payloads share. - */ -export default (createPayload) => { - describe("base request payload functionality", () => { - let payload; - - beforeEach(() => { - payload = createPayload(); - }); - - it("merges state", () => { - payload.mergeState({ - fruit: { - name: "banana", - }, - vegetable: { - name: "carrot", - }, - }); - payload.mergeState({ - fruit: { - name: "apple", - calories: 105, - }, - vegetable: { - calories: 25, - }, - }); - - // We don't evaluate the entire `state` object because the request - // modules that leverage this base request may have added other things - // to `state` as well. For that reason, we just evaluate the fruit - // and vegetable. - const postSerializationPayload = JSON.parse(JSON.stringify(payload)); - expect(postSerializationPayload.meta.state.fruit).toEqual({ - name: "apple", - calories: 105, - }); - expect(postSerializationPayload.meta.state.vegetable).toEqual({ - name: "carrot", - calories: 25, - }); - }); - - it("merges query", () => { - payload.mergeQuery({ - fruit: { - name: "banana", - }, - vegetable: { - name: "carrot", - }, - }); - payload.mergeQuery({ - fruit: { - name: "apple", - calories: 105, - }, - vegetable: { - calories: 25, - }, - }); - // We don't evaluate the entire `query` object because the request - // modules that leverage this base request may have added other things - // to `query` as well. For that reason, we just evaluate the fruit - // and vegetable. - const postSerializationPayload = JSON.parse(JSON.stringify(payload)); - expect(postSerializationPayload.query.fruit).toEqual({ - name: "apple", - calories: 105, - }); - expect(postSerializationPayload.query.vegetable).toEqual({ - name: "carrot", - calories: 25, - }); - }); - }); -}; diff --git a/test/unit/helpers/describeValidation.js b/test/unit/helpers/describeValidation.js deleted file mode 100644 index 19e5f69d6..000000000 --- a/test/unit/helpers/describeValidation.js +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -export default (description, validator, specObjects) => { - describe(description, () => { - specObjects.forEach( - ({ value, expected = value, error = false, warning = false }) => { - if (error) { - it(`rejects ${JSON.stringify(value)}`, () => { - const logger = jasmine.createSpyObj("logger", ["warn"]); - expect(() => - validator.call({ logger }, value, "mykey"), - ).toThrowMatching((e) => { - return /'mykey[^']*'(:| is)/.test(e.message); - }); - if (warning) { - expect(logger.warn).toHaveBeenCalled(); - } else { - expect(logger.warn).not.toHaveBeenCalled(); - } - }); - } else { - it(`transforms \`${JSON.stringify(value)}\` to \`${JSON.stringify( - expected, - )}\``, () => { - const logger = jasmine.createSpyObj("logger", ["warn"]); - expect(validator.call({ logger }, value, "mykey")).toEqual( - expected, - ); - if (warning) { - expect(logger.warn).toHaveBeenCalled(); - } else { - expect(logger.warn).not.toHaveBeenCalled(); - } - }); - } - }, - ); - }); -}; diff --git a/test/unit/helpers/flushPromiseChains.js b/test/unit/helpers/flushPromiseChains.js deleted file mode 100644 index aeb6eea8b..000000000 --- a/test/unit/helpers/flushPromiseChains.js +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -/** - * Returns a promise that will be resolved after all outstanding promise chains - * have been flushed. This assumes (1) that the promise chains to be flushed - * resolve their promises promptly (rather than doing something like - * setTimeout(..., 100) somewhere in the chain) and (2) that the chains - * are no longer than 10 promises long. - * @returns {Promise} - */ -export default () => { - let promise; - - for (let i = 0; i < 10; i += 1) { - promise = promise - ? promise.then(() => Promise.resolve()) - : Promise.resolve(); - } - - return promise; -}; diff --git a/test/unit/helpers/pause.js b/test/unit/helpers/pause.js deleted file mode 100644 index 11156a675..000000000 --- a/test/unit/helpers/pause.js +++ /dev/null @@ -1,15 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -export default (ms) => - new Promise((resolve) => { - setTimeout(resolve, ms); - }); diff --git a/test/unit/helpers/removeAllCookies.js b/test/unit/helpers/removeAllCookies.js deleted file mode 100644 index 9d77539ca..000000000 --- a/test/unit/helpers/removeAllCookies.js +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { cookieJar } from "../../../src/utils/index.js"; - -export default () => { - Object.keys(cookieJar.get()).forEach((cookieName) => { - cookieJar.remove(cookieName); - }); -}; diff --git a/test/unit/helpers/testConfigValidators.js b/test/unit/helpers/testConfigValidators.js deleted file mode 100644 index 05ef4fb32..000000000 --- a/test/unit/helpers/testConfigValidators.js +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -export default ({ - configValidators, - validConfigurations, - invalidConfigurations, - deprecatedConfigurations = [], - defaultValues, -}) => { - validConfigurations.forEach((cfg, i) => { - it(`validates configuration (${i})`, () => { - configValidators(cfg); - }); - }); - - invalidConfigurations.forEach((cfg, i) => { - it(`invalidates configuration (${i})`, () => { - expect(() => { - configValidators(cfg); - }).toThrowError(); - }); - }); - - it("provides default values", () => { - const config = configValidators({}); - Object.keys(defaultValues).forEach((key) => { - expect(config[key]).toBe(defaultValues[key]); - }); - }); - - deprecatedConfigurations.forEach((cfg, i) => { - it(`outputs messages for deprecated fields (${i})`, () => { - const logger = jasmine.createSpyObj("logger", ["warn"]); - configValidators.call({ logger }, cfg); - expect(logger.warn).toHaveBeenCalled(); - }); - }); -}; diff --git a/test/unit/specs/components/ActivityCollector/attachClickActivityCollector.spec.js b/test/unit/specs/components/ActivityCollector/attachClickActivityCollector.spec.js deleted file mode 100644 index 52637f8fb..000000000 --- a/test/unit/specs/components/ActivityCollector/attachClickActivityCollector.spec.js +++ /dev/null @@ -1,127 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import attachClickActivityCollector from "../../../../../src/components/ActivityCollector/attachClickActivityCollector.js"; -import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; - -describe("ActivityCollector::attachClickActivityCollector", () => { - const config = {}; - let eventManager; - let lifecycle; - let clickHandler; - let handleError; - beforeEach(() => { - config.clickCollectionEnabled = true; - eventManager = jasmine.createSpyObj("eventManager", { - createEvent: { - isEmpty: () => false, - documentMayUnload: () => false, - }, - sendEvent: Promise.resolve(), - }); - lifecycle = jasmine.createSpyObj("lifecycle", { - onClick: Promise.resolve(), - }); - // eslint-disable-next-line no-unused-vars - spyOn(document, "addEventListener").and.callFake((name, handler, type) => { - clickHandler = handler; - }); - handleError = jasmine.createSpy("handleError"); - }); - - const build = () => { - attachClickActivityCollector({ - config, - eventManager, - lifecycle, - handleError, - }); - }; - - it("Attaches click handler if clickCollectionEnabled is set to true", () => { - build(); - expect(document.addEventListener).toHaveBeenCalled(); - }); - - it("Attaches click handler if clickCollectionEnabled is set to false", () => { - config.clickCollectionEnabled = false; - build(); - expect(document.addEventListener).toHaveBeenCalled(); - }); - - it("Publishes onClick lifecycle events at clicks when clickCollectionEnabled is set to true", () => { - build(); - clickHandler({}); - expect(lifecycle.onClick).toHaveBeenCalled(); - }); - - it("Does not publish onClick lifecycle events for AppMeasurement repropagated click-events", () => { - build(); - const clickEvent = { - s_fe: 1, - }; - clickHandler(clickEvent); - expect(lifecycle.onClick).not.toHaveBeenCalled(); - }); - - it("Handles errors inside onClick lifecycle", () => { - const error = new Error("Bad thing happened."); - lifecycle.onClick.and.returnValue(Promise.reject(error)); - build(); - return clickHandler({}) - .then(() => { - return flushPromiseChains(); - }) - .then(() => { - expect(handleError).toHaveBeenCalledWith(error, "click collection"); - }); - }); - - it("Sends populated events", () => { - build(); - return clickHandler({}) - .then(() => { - return flushPromiseChains(); - }) - .then(() => { - expect(eventManager.sendEvent).toHaveBeenCalled(); - }); - }); - - it("Does not send empty events", () => { - eventManager.createEvent.and.returnValue({ - isEmpty: () => true, - documentMayUnload: () => false, - }); - build(); - return clickHandler({}) - .then(() => { - return flushPromiseChains(); - }) - .then(() => { - expect(eventManager.sendEvent).not.toHaveBeenCalled(); - }); - }); - - it("handles errors thrown in sendEvent", () => { - const error = new Error("Network Error"); - eventManager.sendEvent.and.returnValue(Promise.reject(error)); - build(); - return clickHandler({}) - .then(() => { - return flushPromiseChains(); - }) - .then(() => { - expect(handleError).toHaveBeenCalledWith(error, "click collection"); - }); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/configValidators.spec.js b/test/unit/specs/components/ActivityCollector/configValidators.spec.js deleted file mode 100644 index bce60ec05..000000000 --- a/test/unit/specs/components/ActivityCollector/configValidators.spec.js +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import configValidators from "../../../../../src/components/ActivityCollector/configValidators.js"; -import testConfigValidators from "../../../helpers/testConfigValidators.js"; - -describe("ActivityCollector config validators", () => { - testConfigValidators({ - configValidators, - validConfigurations: [ - {}, - { - clickCollectionEnabled: false, - }, - { - clickCollectionEnabled: false, - downloadLinkQualifier: "", - }, - ], - invalidConfigurations: [ - { clickCollectionEnabled: "" }, - { - clickCollectionEnabled: true, - downloadLinkQualifier: "[", - }, - ], - defaultValues: { - clickCollectionEnabled: true, - downloadLinkQualifier: - "\\.(exe|zip|wav|mp3|mov|mpg|avi|wmv|pdf|doc|docx|xls|xlsx|ppt|pptx)$", - }, - deprecatedConfigurations: [ - { - onBeforeLinkClickSend: () => undefined, - }, - ], - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/createClickActivityStorage.spec.js b/test/unit/specs/components/ActivityCollector/createClickActivityStorage.spec.js deleted file mode 100644 index 9706ec871..000000000 --- a/test/unit/specs/components/ActivityCollector/createClickActivityStorage.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createClickActivityStorage from "../../../../../src/components/ActivityCollector/createClickActivityStorage.js"; -import { CLICK_ACTIVITY_DATA } from "../../../../../src/constants/sessionDataKeys.js"; - -describe("ActivityCollector::createClickActivityStorage", () => { - let storage; - let clickActivityStorage; - beforeEach(() => { - storage = jasmine.createSpyObj("storage", [ - "getItem", - "setItem", - "removeItem", - ]); - clickActivityStorage = createClickActivityStorage({ storage }); - }); - - it("saves data", () => { - clickActivityStorage.save({ key: "value" }); - expect(storage.setItem).toHaveBeenCalledWith( - CLICK_ACTIVITY_DATA, - '{"key":"value"}', - ); - }); - - it("loads data", () => { - storage.getItem.and.returnValue('{"key":"value"}'); - const data = clickActivityStorage.load(); - expect(data).toEqual({ key: "value" }); - }); - - it("loads null when no data is present", () => { - const data = clickActivityStorage.load(); - expect(data).toBeNull(); - }); - - it("removes data", () => { - clickActivityStorage.remove(); - expect(storage.removeItem).toHaveBeenCalledWith(CLICK_ACTIVITY_DATA); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/createClickedElementProperties.spec.js b/test/unit/specs/components/ActivityCollector/createClickedElementProperties.spec.js deleted file mode 100644 index 6e4720367..000000000 --- a/test/unit/specs/components/ActivityCollector/createClickedElementProperties.spec.js +++ /dev/null @@ -1,224 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createClickedElementProperties from "../../../../../src/components/ActivityCollector/createClickedElementProperties.js"; - -describe("ActivityCollector::createClickedElementProperties", () => { - let properties; - beforeEach(() => { - properties = { - pageName: "testPageName", - linkName: "testLinkName", - linkRegion: "testLinkRegion", - linkType: "testLinkType", - linkUrl: "testLinkUrl", - pageIDType: 0, - }; - }); - it("Should return object with the init properties", () => { - const props = createClickedElementProperties({ properties }); - expect(props.properties).toEqual(properties); - }); - it("Can determine it holds valid link properties", () => { - const props = createClickedElementProperties({ properties }); - expect(props.isValidLink()).toBe(true); - }); - it("Can determine it holds invalid link properties", () => { - let props = createClickedElementProperties({}); - expect(props.isValidLink()).toBe(false); - props = createClickedElementProperties({ properties }); - props.linkName = ""; - expect(props.isValidLink()).toBe(false); - }); - it("Can determine it holds internal link properties", () => { - const props = createClickedElementProperties({ properties }); - expect(props.isInternalLink()).toBe(false); - props.linkType = "other"; - expect(props.isInternalLink()).toBe(true); - }); - it("Can determine it holds valid ActivityMap properties", () => { - const props = createClickedElementProperties({ properties }); - expect(props.isValidActivityMapData()).toBe(true); - props.pageName = ""; - expect(props.isValidActivityMapData()).toBe(false); - }); - it("Can convert properties to a populated DATA Analytics schema with ActivityMap data", () => { - const props = createClickedElementProperties({ properties }); - const data = props.data; - expect(data).toEqual({ - __adobe: { - analytics: { - contextData: { - a: { - activitymap: { - page: "testPageName", - link: "testLinkName", - region: "testLinkRegion", - pageIDType: 0, - }, - }, - }, - }, - }, - }); - }); - it("Can convert properties to a populated XDM Analytics schema with ActivityMap data", () => { - const props = createClickedElementProperties({ properties }); - const data = props.xdm; - expect(data).toEqual({ - eventType: "web.webinteraction.linkClicks", - web: { - webInteraction: { - name: "testLinkName", - region: "testLinkRegion", - type: "testLinkType", - URL: "testLinkUrl", - linkClicks: { - value: 1, - }, - }, - }, - }); - }); - it("Can populate properties from options", () => { - const props = createClickedElementProperties(); - const options = { - xdm: { - web: { - webInteraction: { - name: "xdmName", - region: "xdmRegion", - type: "xdmType", - URL: "xdmUrl", - linkClicks: { - value: 2, - }, - }, - }, - }, - data: { - __adobe: { - analytics: { - contextData: { - a: { - activitymap: { - page: "dataPage", - link: "dataLink", - region: "dataRegion", - pageIDType: 1, - }, - }, - }, - }, - }, - }, - clickedElement: {}, - }; - props.options = options; - // The DATA portion takes priority - expect(props.properties).toEqual({ - pageName: "dataPage", - linkName: "dataLink", - linkRegion: "dataRegion", - linkType: "xdmType", - linkUrl: "xdmUrl", - pageIDType: 1, - }); - }); - it("Can apply a property filter", () => { - const props = createClickedElementProperties({ properties }); - // Need a clickedElement for the filter to be executed - props.clickedElement = {}; - const filter = (p) => { - p.linkType = "filtered"; - }; - props.applyPropertyFilter(filter); - expect(props.linkType).toBe("filtered"); - }); - it("Prints message when filter rejects properties", () => { - const logger = jasmine.createSpyObj("logger", ["info"]); - const props = createClickedElementProperties({ properties, logger }); - // Need a clickedElement for the filter to be executed - props.clickedElement = {}; - const filter = (p) => { - p.linkType = "filtered"; - return false; - }; - props.applyPropertyFilter(filter); - expect(logger.info).toHaveBeenCalledWith( - jasmine.stringMatching( - /^Clicked element properties were rejected by filter function/, - ), - ); - }); - it("Can apply a property filter for all properties", () => { - const props = createClickedElementProperties({ properties }); - props.clickedElement = {}; - const filter = (p) => { - p.pageName = "filtered"; - p.linkName = "filtered"; - p.linkRegion = "filtered"; - p.linkType = "filtered"; - p.linkUrl = "filtered"; - p.pageIDType = 1; - }; - props.applyPropertyFilter(filter); - expect(props.linkType).toBe("filtered"); - expect(props.pageName).toBe("filtered"); - }); - it("Can apply an options property filter", () => { - const props = createClickedElementProperties({ properties }); - // Need a clickedElement for the filter to be executed - props.clickedElement = {}; - const filter = (options) => { - options.xdm.web.webInteraction.type = "filtered"; - }; - props.applyOptionsFilter(filter); - expect(props.linkType).toBe("filtered"); - }); - it("Can apply an options property filter for all properties", () => { - const props = createClickedElementProperties({ properties }); - props.clickedElement = {}; - const filter = (options) => { - if ( - options && - options.xdm && - options.xdm.web && - options.xdm.web.webInteraction - ) { - const webInteraction = options.xdm.web.webInteraction; - webInteraction.name = "filtered"; // Link name - webInteraction.region = "filtered"; // Link region - webInteraction.type = "filtered"; // Link type - webInteraction.URL = "filtered"; // Link URL - } - /* eslint no-underscore-dangle: 0 */ - if ( - options && - options.data && - options.data.__adobe && - options.data.__adobe.analytics - ) { - const { contextData } = options.data.__adobe.analytics; - if (contextData && contextData.a && contextData.a.activitymap) { - const activitymap = contextData.a.activitymap; - activitymap.page = "filtered"; // Page name - activitymap.link = "filtered"; // Link name - activitymap.region = "filtered"; // Link region - } - } - }; - props.applyOptionsFilter(filter); - expect(props.linkType).toBe("filtered"); - expect(props.pageName).toBe("filtered"); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/createGetClickedElementProperties.spec.js b/test/unit/specs/components/ActivityCollector/createGetClickedElementProperties.spec.js deleted file mode 100644 index cc5628e83..000000000 --- a/test/unit/specs/components/ActivityCollector/createGetClickedElementProperties.spec.js +++ /dev/null @@ -1,276 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createGetClickedElementProperties from "../../../../../src/components/ActivityCollector/createGetClickedElementProperties.js"; -import createClickActivityStorage from "../../../../../src/components/ActivityCollector/createClickActivityStorage.js"; - -describe("ActivityCollector::createGetClickedElementProperties", () => { - const mockWindow = { - location: { - protocol: "https:", - host: "example.com", - hostname: "example.com", - pathname: "/", - href: "https://example.com/", - }, - }; - const supportedLinkElement = { - tagName: "A", - href: "index.html", - nodeType: 1, - }; - - let getLinkName; - let getLinkRegion; - let getAbsoluteUrlFromAnchorElement; - let findClickableElement; - let determineLinkType; - let logger; - let clickActivityStorage; - beforeEach(() => { - getLinkName = jasmine.createSpy("getLinkName"); - getLinkRegion = jasmine.createSpy("getLinkRegion"); - getAbsoluteUrlFromAnchorElement = jasmine.createSpy( - "getAbsoluteUrlFromAnchorElement", - ); - findClickableElement = jasmine.createSpy("findClickableElement"); - determineLinkType = jasmine.createSpy("determineLinkType"); - logger = jasmine.createSpyObj("logger", ["info"]); - clickActivityStorage = createClickActivityStorage({ - storage: { - getItem: () => {}, - setItem: () => {}, - removeItem: () => {}, - }, - }); - }); - - it("Returns complete linkDetails when it is a supported anchor element", () => { - const config = { - onBeforeLinkClickSend: (options) => { - options.data.custom = "test data field"; - return true; - }, - clickCollection: { - externalLink: true, - }, - }; - getLinkRegion.and.returnValue("root"); - getLinkName.and.returnValue("Go to cart"); - getAbsoluteUrlFromAnchorElement.and.returnValue("http://blah.com"); - findClickableElement.and.returnValue(supportedLinkElement); - determineLinkType.and.returnValue("exit"); - - const getClickedElementProperties = createGetClickedElementProperties({ - getLinkRegion, - getLinkName, - getAbsoluteUrlFromAnchorElement, - findClickableElement, - determineLinkType, - window: mockWindow, - }); - - const result = getClickedElementProperties({ - clickedElement: {}, - config, - logger, - clickActivityStorage, - }); - // I have to set this manually because of passing in {} as the clickedElement - result.pageIDType = 0; - - expect(result.options).toEqual({ - xdm: { - eventType: "web.webinteraction.linkClicks", - web: { - webInteraction: { - name: "Go to cart", - region: "root", - type: "exit", - URL: "http://blah.com", - linkClicks: { - value: 1, - }, - }, - }, - }, - data: { - __adobe: { - analytics: { - contextData: { - a: { - activitymap: { - page: "https://example.com/", - link: "Go to cart", - region: "root", - pageIDType: 0, - }, - }, - }, - }, - }, - custom: "test data field", - }, - clickedElement: {}, - }); - }); - - it("Returns undefined when the customer callback returns false", () => { - const config = { - onBeforeLinkClickSend: () => { - return false; - }, - clickCollection: { - externalLink: true, - }, - }; - getLinkRegion.and.returnValue("root"); - getLinkName.and.returnValue("Go to cart"); - getAbsoluteUrlFromAnchorElement.and.returnValue("http://blah.com"); - findClickableElement.and.returnValue(supportedLinkElement); - determineLinkType.and.returnValue("exit"); - - const getClickedElementProperties = createGetClickedElementProperties({ - getLinkRegion, - getLinkName, - getAbsoluteUrlFromAnchorElement, - findClickableElement, - determineLinkType, - window: mockWindow, - }); - - const result = getClickedElementProperties({ - clickedElement: {}, - config, - logger, - clickActivityStorage, - }); - expect(result.options).toEqual(undefined); - }); - - it("Returns undefined when not supported anchor element", () => { - const config = { - onBeforeLinkClickSend: () => { - return true; - }, - clickCollection: { - externalLink: true, - }, - }; - getLinkRegion.and.returnValue(undefined); - getLinkName.and.returnValue("Go to cart"); - getAbsoluteUrlFromAnchorElement.and.returnValue("http://blah.com"); - findClickableElement.and.returnValue(undefined); - determineLinkType.and.returnValue("exit"); - - const getClickedElementProperties = createGetClickedElementProperties({ - getLinkRegion, - getLinkName, - getAbsoluteUrlFromAnchorElement, - findClickableElement, - determineLinkType, - window: mockWindow, - }); - - const result = getClickedElementProperties({ - clickedElement: {}, - config, - logger, - clickActivityStorage, - }); - expect(result.options).toEqual(undefined); - }); - - it("Returns only options with data element if clickable element is missing href", () => { - const config = { - onBeforeLinkClickSend: () => { - return true; - }, - clickCollection: { - externalLink: true, - }, - }; - getLinkRegion.and.returnValue("root"); - getLinkName.and.returnValue("Go to cart"); - getAbsoluteUrlFromAnchorElement.and.returnValue(undefined); - findClickableElement.and.returnValue(supportedLinkElement); - determineLinkType.and.returnValue("exit"); - - const getClickedElementProperties = createGetClickedElementProperties({ - getLinkRegion, - getLinkName, - getAbsoluteUrlFromAnchorElement, - findClickableElement, - determineLinkType, - window: mockWindow, - }); - - const result = getClickedElementProperties({ - clickedElement: {}, - config, - logger, - clickActivityStorage, - }); - // I have to set this manually because of passing in {} as the clickedElement - result.pageIDType = 0; - expect(result.options).toEqual({ - data: { - __adobe: { - analytics: { - contextData: { - a: { - activitymap: { - page: "https://example.com/", - link: "Go to cart", - region: "root", - pageIDType: 0, - }, - }, - }, - }, - }, - }, - clickedElement: {}, - }); - }); - - it("Returns the object with link details when callback does not return explicit false ", () => { - const config = { - onBeforeLinkClickSend: () => {}, - clickCollection: { - externalLink: true, - }, - }; - getLinkRegion.and.returnValue("root"); - getLinkName.and.returnValue("Go to cart"); - getAbsoluteUrlFromAnchorElement.and.returnValue("http://blah.com"); - findClickableElement.and.returnValue(supportedLinkElement); - determineLinkType.and.returnValue("exit"); - - const getClickedElementProperties = createGetClickedElementProperties({ - getLinkRegion, - getLinkName, - getAbsoluteUrlFromAnchorElement, - findClickableElement, - determineLinkType, - window: mockWindow, - }); - - const result = getClickedElementProperties({ - clickedElement: {}, - config, - logger, - clickActivityStorage, - }); - expect(result).not.toBe(undefined); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/createInjectClickedElementProperties.spec.js b/test/unit/specs/components/ActivityCollector/createInjectClickedElementProperties.spec.js deleted file mode 100644 index 1fd528766..000000000 --- a/test/unit/specs/components/ActivityCollector/createInjectClickedElementProperties.spec.js +++ /dev/null @@ -1,141 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createInjectClickedElementProperties from "../../../../../src/components/ActivityCollector/createInjectClickedElementProperties.js"; -import createEvent from "../../../../../src/core/createEvent.js"; -import { downloadLinkQualifier as dlwValidator } from "../../../../../src/components/ActivityCollector/configValidators.js"; - -describe("ActivityCollector::createInjectClickedElementProperties", () => { - const getClickedElementProperties = jasmine.createSpy( - "getClickedElementProperties", - ); - const clickActivityStorage = jasmine.createSpyObj("clickActivityStorage", [ - "save", - ]); - const downloadLinkQualifier = dlwValidator(); - - it("Extends event XDM data with link information for supported anchor elements when clickCollectionEnabled", () => { - const config = { - downloadLinkQualifier, - clickCollectionEnabled: true, - clickCollection: {}, - }; - const injectClickedElementProperties = createInjectClickedElementProperties( - { getClickedElementProperties, clickActivityStorage, config }, - ); - const event = createEvent(); - getClickedElementProperties.and.returnValue({ - xdm: { - web: { - webInteraction: { - name: "test1", - }, - }, - }, - data: {}, - isValidLink: () => true, - isInternalLink: () => false, - isValidActivityMapData: () => true, - }); - injectClickedElementProperties({ event, clickedElement: {} }); - expect(event.isEmpty()).toBe(false); - }); - - it("Does not extend event XDM data when clickCollectionEnabled is false", () => { - const event = createEvent(); - const config = { - downloadLinkQualifier, - clickCollectionEnabled: false, - }; - const injectClickedElementProperties = createInjectClickedElementProperties( - { - getClickedElementProperties, - config, - }, - ); - getClickedElementProperties.and.returnValue({ - xdm: { - web: { - webInteraction: { - name: "test1", - }, - }, - }, - data: {}, - }); - injectClickedElementProperties({ clickedElement: {}, event }); - expect(event.isEmpty()).toBe(true); - }); - - it("Does not extend event XDM data with link information for unsupported anchor elements", () => { - const event = createEvent(); - const config = { - downloadLinkQualifier, - clickCollectionEnabled: true, - clickCollection: {}, - }; - const injectClickedElementProperties = createInjectClickedElementProperties( - { - getClickedElementProperties, - clickActivityStorage, - config, - }, - ); - getClickedElementProperties.and.returnValue({ - data: {}, - isValidLink: () => false, - isInternalLink: () => false, - isValidActivityMapData: () => true, - }); - injectClickedElementProperties({ clickedElement: {}, event }); - expect(event.isEmpty()).toBe(true); - }); - - it("Does not save click data to storage if onBeforeLinkClickSend is defined", () => { - const config = { - clickCollectionEnabled: true, - clickCollection: { - internalLinkEnabled: true, - eventGroupingEnabled: true, - }, - onBeforeLinkClickSend: () => {}, - }; - const logger = jasmine.createSpyObj("logger", ["info"]); - getClickedElementProperties.and.returnValue({ - isValidLink: () => true, - isInternalLink: () => true, - pageName: "testPage", - pageIDType: 1, - linkName: "testLink", - linkType: "other", - }); - const injectClickedElementProperties = createInjectClickedElementProperties( - { - config, - logger, - getClickedElementProperties, - clickActivityStorage, - }, - ); - const event = jasmine.createSpyObj("event", ["mergeXdm", "mergeData"]); - injectClickedElementProperties({ clickedElement: {}, event }); - // No click data should be saved to storage, only the page data. - expect(clickActivityStorage.save).toHaveBeenCalledWith({ - pageName: "testPage", - pageIDType: 1, - }); - // If mergeXdm and mergeData are called, it means that the click data was not saved and - // will instead go out with the event. - expect(event.mergeXdm).toHaveBeenCalled(); - expect(event.mergeData).toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/createRecallAndInjectClickedElementProperties.spec.js b/test/unit/specs/components/ActivityCollector/createRecallAndInjectClickedElementProperties.spec.js deleted file mode 100644 index 20428cdd4..000000000 --- a/test/unit/specs/components/ActivityCollector/createRecallAndInjectClickedElementProperties.spec.js +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createRecallAndInjectClickedElementProperties from "../../../../../src/components/ActivityCollector/createRecallAndInjectClickedElementProperties.js"; - -describe("ActivityCollector::createRecallAndInjectClickedElementProperties", () => { - let props; - let clickActivityStorage; - let event; - - beforeEach(() => { - props = {}; - clickActivityStorage = { - load: jasmine.createSpy().and.returnValue(props), - save: jasmine.createSpy(), - }; - event = { - mergeXdm: jasmine.createSpy(), - mergeData: jasmine.createSpy(), - }; - }); - - it("should return a function", () => { - const recallAndInjectClickedElementProperties = - createRecallAndInjectClickedElementProperties({ clickActivityStorage }); - expect(recallAndInjectClickedElementProperties).toEqual( - jasmine.any(Function), - ); - }); - - it("should merge stored clicked element properties to event XDM and DATA", () => { - const recallClickElementProperties = - createRecallAndInjectClickedElementProperties({ clickActivityStorage }); - props.pageName = "examplePage"; - props.linkName = "example"; - props.linkRegion = "exampleRegion"; - props.linkType = "external"; - props.linkUrl = "https://example.com"; - props.pageIDType = 1; - recallClickElementProperties(event); - expect(event.mergeXdm).toHaveBeenCalledWith({ - web: { - webInteraction: { - name: "example", - region: "exampleRegion", - type: "external", - URL: "https://example.com", - linkClicks: { - value: 1, - }, - }, - }, - }); - expect(event.mergeData).toHaveBeenCalledWith({ - __adobe: { - analytics: { - contextData: { - a: { - activitymap: { - page: "examplePage", - link: "example", - region: "exampleRegion", - pageIDType: 1, - }, - }, - }, - }, - }, - }); - expect(clickActivityStorage.save).toHaveBeenCalledWith({ - pageName: "examplePage", - pageIDType: 1, - }); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/createStorePageViewProperties.spec.js b/test/unit/specs/components/ActivityCollector/createStorePageViewProperties.spec.js deleted file mode 100644 index fac787e2e..000000000 --- a/test/unit/specs/components/ActivityCollector/createStorePageViewProperties.spec.js +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createStorePageViewProperties from "../../../../../src/components/ActivityCollector/createStorePageViewProperties.js"; - -describe("ActivityCollector::createStorePageViewProperties", () => { - let clickActivityStorage; - beforeEach(() => { - clickActivityStorage = { - save: jasmine.createSpy(), - }; - }); - - it("should return a function", () => { - const storePageViewProperties = createStorePageViewProperties({ - clickActivityStorage, - }); - expect(storePageViewProperties).toEqual(jasmine.any(Function)); - }); - - it("stores page view properties when available in event", () => { - const storePageViewProperties = createStorePageViewProperties({ - clickActivityStorage, - }); - storePageViewProperties({ - getContent: () => ({ - xdm: { - web: { - webPageDetails: { - name: "testPageName", - }, - }, - }, - }), - }); - expect(clickActivityStorage.save).toHaveBeenCalledWith({ - pageName: "testPageName", - pageIDType: 1, - }); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/getLinkName.spec.js b/test/unit/specs/components/ActivityCollector/getLinkName.spec.js deleted file mode 100644 index ba9c492b1..000000000 --- a/test/unit/specs/components/ActivityCollector/getLinkName.spec.js +++ /dev/null @@ -1,153 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getLinkName from "../../../../../src/components/ActivityCollector/getLinkName.js"; - -const createNodeWithAttribute = (nodeName, attributeName, attributeValue) => { - const node = { - nodeName, - getAttribute: (name) => { - if (name === attributeName) { - return attributeValue; - } - return null; - }, - }; - node[attributeName] = attributeValue; - return node; -}; - -describe("ActivityCollector::getLinkName", () => { - it("Returns empty string if no link-name can be constructed", () => { - expect(getLinkName({})).toBe(""); - }); - - it("Prioritizes node innerText over textContent", () => { - // The innerText always takes priority when determining the link-name - // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText - expect( - getLinkName({ - nodeName: "A", - innerText: "inner", - textContent: "content", - }), - ).toBe("inner"); - // Child nodes are ignored unless they include non-supported elements - // which can end up corrupting the innerText value - expect( - getLinkName({ - nodeName: "A", - innerText: "inner", - textContent: "content", - childNodes: [createNodeWithAttribute("IMG", "src", "image.jpg")], - }), - ).toBe("inner"); - expect( - getLinkName({ - nodeName: "A", - textContent: "content", - }), - ).toBe("content"); - }); - // this is an use case when in the children nodes there is an unsupported node, - // thus the innerText might contain some unrelated data - it("Excludes unsupported nodes", () => { - expect( - getLinkName({ - innerText: "Not picked due to non-supported child nodes", - nodeName: "A", - childNodes: [ - // Title attributes would contribute to the link-name - // However LINK is a non-supported element - createNodeWithAttribute("LINK", "title", "Link Title"), - createNodeWithAttribute("IMG", "src", "image.jpg"), - ], - }), - ).toBe("image.jpg"); - }); - - // This test could look like: Click Here - it("Prioritizes nodeValue over other element properties", () => { - expect( - getLinkName({ - nodeName: "A", - childNodes: [ - createNodeWithAttribute("#text", "nodeValue", "Click Here"), - createNodeWithAttribute("IMG", "src", "image.jpg"), - ], - }), - ).toBe("Click Here"); - }); - - it("Select link-name based on a property hierarchy", () => { - expect( - getLinkName({ - nodeName: "A", - childNodes: [ - createNodeWithAttribute("IMG", "title", "title"), - createNodeWithAttribute("IMG", "src", "image.jpg"), - createNodeWithAttribute("IMG", "alt", "alt"), - createNodeWithAttribute("INPUT", "value", "input"), - ], - }), - ).toBe("alt"); - expect( - getLinkName({ - nodeName: "A", - childNodes: [ - createNodeWithAttribute("IMG", "title", "title"), - createNodeWithAttribute("IMG", "src", "image.jpg"), - createNodeWithAttribute("INPUT", "value", "input"), - ], - }), - ).toBe("title"); - expect( - getLinkName({ - nodeName: "A", - childNodes: [ - createNodeWithAttribute("IMG", "src", "image.jpg"), - createNodeWithAttribute("INPUT", "value", "input"), - ], - }), - ).toBe("input"); - expect( - getLinkName({ - nodeName: "A", - childNodes: [createNodeWithAttribute("IMG", "src", "image.jpg")], - }), - ).toBe("image.jpg"); - }); - - it("Truncates excess whitespace in link-name", () => { - expect( - getLinkName({ - nodeName: "A", - innerText: " ab c", - textContent: "content", - }), - ).toBe("ab c"); - }); -}); - -it("Ignores the spaces attributes", () => { - expect( - getLinkName({ - nodeName: "A", - childNodes: [ - createNodeWithAttribute("IMG", "title", "title"), - createNodeWithAttribute("IMG", "src", "image.jpg"), - createNodeWithAttribute("IMG", "alt", " "), - createNodeWithAttribute("IMG", "alt", "alt"), - createNodeWithAttribute("INPUT", "value", "input"), - ], - }), - ).toBe("alt"); -}); diff --git a/test/unit/specs/components/ActivityCollector/getLinkRegion.spec.js b/test/unit/specs/components/ActivityCollector/getLinkRegion.spec.js deleted file mode 100644 index dc8e3f13a..000000000 --- a/test/unit/specs/components/ActivityCollector/getLinkRegion.spec.js +++ /dev/null @@ -1,113 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getLinkRegion from "../../../../../src/components/ActivityCollector/getLinkRegion.js"; - -const createChildElement = (element) => { - return { - parentNode: element, - }; -}; - -describe("ActivityCollector::getLinkRegion", () => { - it("Returns BODY if no region is found", () => { - expect(getLinkRegion({})).toBe("BODY"); - }); - - it("Picks region properties based on priority", () => { - const tests = [ - { - element: { - id: "id", - role: "region", - "aria-label": "aria", - nodeName: "HEADER", - }, - result: "id", - }, - { - element: { - role: "region", - "aria-label": "aria", - nodeName: "HEADER", - }, - result: "aria", - }, - { - element: { - nodeName: "HEADER", - }, - result: "HEADER", - }, - ]; - tests.forEach((test) => { - const anchor = createChildElement(test.element); - expect(getLinkRegion(anchor)).toBe(test.result); - }); - expect(getLinkRegion({})).toBe("BODY"); - }); - - it("Traverses up the DOM to find a region", () => { - const element = { - id: "3-levels", - }; - const anchor = createChildElement(createChildElement(element)); - expect(getLinkRegion(anchor)).toBe("3-levels"); - }); - - it("Supports setting region as semantic element name for supported elements", () => { - const tests = [ - { - element: { - nodeName: "HEADER", - }, - result: "HEADER", - }, - { - element: { - nodeName: "MAIN", - }, - result: "MAIN", - }, - { - element: { - nodeName: "FOOTER", - }, - result: "FOOTER", - }, - { - element: { - nodeName: "NAV", - }, - result: "NAV", - }, - { - element: { - nodeName: "SECTION", - }, - result: "BODY", - }, - ]; - tests.forEach((test) => { - const anchor = createChildElement(test.element); - expect(getLinkRegion(anchor)).toBe(test.result); - }); - expect(getLinkRegion({})).toBe("BODY"); - }); - - it("Truncates excess whitespace in region", () => { - const element = { - id: " ab c", - }; - const anchor = createChildElement(element); - expect(getLinkRegion(anchor)).toBe("ab c"); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/activityMapExtensionEnabled.spec.js b/test/unit/specs/components/ActivityCollector/utils/activityMapExtensionEnabled.spec.js deleted file mode 100644 index 9b35a233c..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/activityMapExtensionEnabled.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import activityMapExtensionEnabled from "../../../../../../src/components/ActivityCollector/utils/activityMapExtensionEnabled.js"; - -const ACTIVITY_MAP_EXTENSION_ID = "cppXYctnr"; - -describe("ActivityCollector::activityMapExtensionEnabled", () => { - it("should return true if the activity map extension is enabled", () => { - const context = { - getElementById: jasmine.createSpy().and.returnValue({}), - }; - expect(activityMapExtensionEnabled(context)).toBeTrue(); - expect(context.getElementById).toHaveBeenCalledWith( - ACTIVITY_MAP_EXTENSION_ID, - ); - }); - - it("should return false if the activity map extension is not enabled", () => { - const context = { - getElementById: jasmine.createSpy().and.returnValue(null), - }; - expect(activityMapExtensionEnabled(context)).toBeFalse(); - expect(context.getElementById).toHaveBeenCalledWith( - ACTIVITY_MAP_EXTENSION_ID, - ); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/createTransientStorage.spec.js b/test/unit/specs/components/ActivityCollector/utils/createTransientStorage.spec.js deleted file mode 100644 index 0010f8576..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/createTransientStorage.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createTransientStorage from "../../../../../../src/components/ActivityCollector/utils/createTransientStorage.js"; - -describe("ActivityCollector::createTransientStorage", () => { - it("should return an object with the expected methods", () => { - const transientStorage = createTransientStorage(window); - expect(transientStorage).toEqual({ - setItem: jasmine.any(Function), - getItem: jasmine.any(Function), - removeItem: jasmine.any(Function), - }); - }); - - it("should support storing and retrieving values", () => { - const transientStorage = createTransientStorage(window); - transientStorage.setItem("key1", "value1"); - transientStorage.setItem("key2", "value2"); - expect(transientStorage.getItem("key1")).toBe("value1"); - expect(transientStorage.getItem("key2")).toBe("value2"); - }); - - it("should support removing values", () => { - const transientStorage = createTransientStorage(window); - transientStorage.setItem("key1", "value1"); - transientStorage.setItem("key2", "value2"); - transientStorage.removeItem("key1"); - expect(transientStorage.getItem("key1")).toBeFalsy(); - expect(transientStorage.getItem("key2")).toBe("value2"); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/determineLinkType.spec.js b/test/unit/specs/components/ActivityCollector/utils/determineLinkType.spec.js deleted file mode 100644 index a1d5926fc..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/determineLinkType.spec.js +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import determineLinkType from "../../../../../../src/components/ActivityCollector/utils/determineLinkType.js"; - -describe("ActivityCollector::determineLinkType", () => { - let window; - let config; - let linkUrl; - let clickedObj; - - beforeEach(() => { - window = {}; - config = {}; - linkUrl = ""; - clickedObj = {}; - }); - - it("returns 'other' if linkUrl is an empty string", () => { - const result = determineLinkType(window, config, linkUrl, clickedObj); - expect(result).toBe("other"); - }); - - it("returns 'download' if linkUrl qualify as download link", () => { - linkUrl = "https://example.com/download.pdf"; - config.downloadLinkQualifier = /\.pdf$/; - const result = determineLinkType(window, config, linkUrl, clickedObj); - expect(result).toBe("download"); - }); - - it("returns 'exit' if linkUrl is an exit link", () => { - linkUrl = "https://adobe.com"; - window.location = { - hostname: "example.com", - }; - const result = determineLinkType(window, config, linkUrl, clickedObj); - expect(result).toBe("exit"); - }); - - it("returns 'other' if linkUrl is not a download or exit link", () => { - linkUrl = "https://example.com"; - window.location = { - hostname: "example.com", - }; - const result = determineLinkType(window, config, linkUrl, clickedObj); - expect(result).toBe("other"); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/dom/elementHasClickHandler.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/elementHasClickHandler.spec.js deleted file mode 100644 index 1dd04feb8..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/dom/elementHasClickHandler.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import elementHasClickHandler from "../../../../../../../src/components/ActivityCollector/utils/dom/elementHasClickHandler.js"; - -describe("ActivityCollector::elementHasClickHandler", () => { - it("should handle invalid elements", () => { - const invalidElements = [ - { element: null }, - { element: undefined }, - { element: {} }, - { element: { onclick: null } }, - { element: { onclick: undefined } }, - ]; - invalidElements.forEach(({ element }) => { - expect(elementHasClickHandler(element)).toBe(false); - }); - }); - - it("should handle elements with click handlers", () => { - const clickHandlerElements = [ - { element: { onclick: () => {} } }, - { element: { onclick() {} } }, - ]; - clickHandlerElements.forEach(({ element }) => { - expect(elementHasClickHandler(element)).toBe(true); - }); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/dom/extractDomain.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/extractDomain.spec.js deleted file mode 100644 index 7ed0ee0c6..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/dom/extractDomain.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2024 example. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import extractDomain from "../../../../../../../src/components/ActivityCollector/utils/dom/extractDomain.js"; - -describe("ActivityCollector::extractDomain", () => { - it("should extract the domain from a URL", () => { - expect(extractDomain("www.example.com")).toBe("www.example.com"); - expect(extractDomain("http://www.example.com")).toBe("www.example.com"); - expect(extractDomain("https://www.example.com")).toBe("www.example.com"); - expect(extractDomain("https://www.example.com/")).toBe("www.example.com"); - expect(extractDomain("https://www.example.com/cool/page")).toBe( - "www.example.com", - ); - }); - - it("should handle URLs without a protocol", () => { - expect(extractDomain("example.com")).toBe("example.com"); - expect(extractDomain("www.example.com")).toBe("www.example.com"); - expect(extractDomain("www.example.com/")).toBe("www.example.com"); - expect(extractDomain("www.example.com/cool/page")).toBe("www.example.com"); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/dom/findClickableElement.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/findClickableElement.spec.js deleted file mode 100644 index 6ed8c86d6..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/dom/findClickableElement.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import findClickableElement from "../../../../../../../src/components/ActivityCollector/utils/dom/findClickableElement.js"; - -describe("ActivityCollector::findClickableElement", () => { - it("returns null if no clickable element is found", () => { - const element = document.createElement("div"); - const parentElement = document.createElement("div"); - parentElement.appendChild(element); - expect(findClickableElement(element)).toBeNull(); - }); - - it("returns the target element if it is clickable", () => { - const element = document.createElement("a"); - element.href = "http://www.example.com"; - expect(findClickableElement(element)).toBe(element); - }); - - it("returns the target element's parent if it is not clickable", () => { - const element = document.createElement("div"); - const parentElement = document.createElement("a"); - parentElement.href = "http://www.example.com"; - parentElement.appendChild(element); - expect(findClickableElement(element)).toBe(parentElement); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.spec.js deleted file mode 100644 index 8796c4773..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.spec.js +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getAbsoluteUrlFromAnchorElement from "../../../../../../../src/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.js"; - -const initAnchorState = (window, element, anchorState) => { - element.href = anchorState["element.href"]; - element.protocol = anchorState["element.protocol"]; - element.host = anchorState["element.host"]; - window.location.protocol = anchorState["window.location.protocol"]; - window.location.host = anchorState["window.location.host"]; - window.location.pathname = anchorState["window.location.pathname"]; -}; - -describe("ActivityCollector::getAbsoluteUrlFromAnchorElement", () => { - it("Makes best attempt to constructs absolute URLs", () => { - const window = { - location: { - protocol: "", - host: "", - pathname: "", - }, - }; - const element = { - protocol: "", - host: "", - }; - const anchorStates = [ - { - "element.href": "http://example.com/example.html", - "element.protocol": "", - "element.host": "", - "window.location.protocol": "http:", - "window.location.host": "example.com", - "window.location.pathname": "/", - expectedResult: "http://example.com/example.html", - }, - { - "element.href": "example.html", - "element.protocol": "", - "element.host": "", - "window.location.protocol": "https:", - "window.location.host": "example.com", - "window.location.pathname": "/", - expectedResult: "https://example.com/example.html", - }, - ]; - anchorStates.forEach((anchorState) => { - initAnchorState(window, element, anchorState); - expect(getAbsoluteUrlFromAnchorElement(window, element)).toBe( - anchorState.expectedResult, - ); - }); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/dom/isButtonSubmitElement.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/isButtonSubmitElement.spec.js deleted file mode 100644 index 86e4d310a..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/dom/isButtonSubmitElement.spec.js +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isButtonSubmitElement from "../../../../../../../src/components/ActivityCollector/utils/dom/isButtonSubmitElement.js"; - -describe("ActivityCollector::isButtonSubmitElement", () => { - it("should return true for submit button", () => { - const button = document.createElement("button"); - button.type = "submit"; - expect(isButtonSubmitElement(button)).toBe(true); - }); - - it("should return true for button with no type", () => { - // This is the default type for a button element - const button = document.createElement("button"); - expect(isButtonSubmitElement(button)).toBe(true); - }); - - it("should return false for non-submit button", () => { - const button = document.createElement("button"); - button.type = "button"; - expect(isButtonSubmitElement(button)).toBe(false); - }); - - it("should return false for input element", () => { - const input = document.createElement("input"); - input.type = "submit"; - expect(isButtonSubmitElement(input)).toBe(false); - }); - - it("should return false for non-button element", () => { - const div = document.createElement("div"); - expect(isButtonSubmitElement(div)).toBe(false); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/dom/isDownloadLink.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/isDownloadLink.spec.js deleted file mode 100644 index d597fa7e8..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/dom/isDownloadLink.spec.js +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isDownloadLink from "../../../../../../../src/components/ActivityCollector/utils/dom/isDownloadLink.js"; -import { downloadLinkQualifier } from "../../../../../../../src/components/ActivityCollector/configValidators.js"; - -describe("ActivityCollector::isDownloadLink", () => { - it("Returns true if the clicked element has a download attribute", () => { - const clickedElement = { - download: "filename", - }; - expect(isDownloadLink(null, "https://example.com/", clickedElement)).toBe( - true, - ); - }); - it("Returns true if the link matches the download link qualifying regular expression", () => { - const downloadLinks = [ - "download.pdf", - "http://example.com/download.zip", - "https://example.com/download.docx", - ]; - // this runs the validator with undefined input which returns the default regex - downloadLinks.forEach((downloadLink) => { - expect(isDownloadLink(downloadLinkQualifier(), downloadLink, {})).toBe( - true, - ); - }); - }); - it("Returns false if the link does not match the download link qualifying regular expression", () => { - const downloadLinks = ["download.mod", "http://example.com/download.png"]; - downloadLinks.forEach((downloadLink) => { - expect(isDownloadLink(downloadLinkQualifier(), downloadLink, {})).toBe( - false, - ); - }); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/dom/isExitLink.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/isExitLink.spec.js deleted file mode 100644 index 19e3029d1..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/dom/isExitLink.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isExitLink from "../../../../../../../src/components/ActivityCollector/utils/dom/isExitLink.js"; - -describe("ActivityCollector::isExitLink", () => { - it("Returns true if the link leads away from the current hostname", () => { - const mockWindow = { - location: { - hostname: "adobe.com", - }, - }; - const clickedLinks = [ - "https://example.com", - "http://example.com/index.html", - ]; - clickedLinks.forEach((clickedLink) => { - expect(isExitLink(mockWindow, clickedLink)).toBe(true); - }); - }); - it("Returns false if the link leads to the current hostname", () => { - const mockWindow = { - location: { - hostname: "adobe.com", - }, - }; - const clickedLinks = ["https://adobe.com", "http://adobe.com/index.html"]; - clickedLinks.forEach((clickedLink) => { - expect(isExitLink(mockWindow, clickedLink)).toBe(false); - }); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/dom/isInputSubmitElement.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/isInputSubmitElement.spec.js deleted file mode 100644 index 64749d054..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/dom/isInputSubmitElement.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isInputSubmitElement from "../../../../../../../src/components/ActivityCollector/utils/dom/isInputSubmitElement.js"; - -describe("ActivityCollector::isInputSubmitElement", () => { - it("should return true for submit input", () => { - const input = document.createElement("input"); - input.type = "submit"; - expect(isInputSubmitElement(input)).toBe(true); - }); - - it("should return true for image input", () => { - const input = document.createElement("input"); - input.type = "image"; - input.src = "https://example.com/image.png"; - expect(isInputSubmitElement(input)).toBe(true); - }); - - it("should return false for non-submit input", () => { - const input = document.createElement("input"); - input.type = "text"; - expect(isInputSubmitElement(input)).toBe(false); - }); - - it("should return false for non-input element", () => { - const div = document.createElement("div"); - expect(isInputSubmitElement(div)).toBe(false); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/dom/isSupportedAnchorElement.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/isSupportedAnchorElement.spec.js deleted file mode 100644 index 19136fae0..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/dom/isSupportedAnchorElement.spec.js +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isSupportedAnchorElement from "../../../../../../../src/components/ActivityCollector/utils/dom/isSupportedAnchorElement.js"; - -describe("ActivityCollector::isSupportedAnchorElement", () => { - it("Returns true for supported anchor elements", () => { - const validAnchorElements = [ - { - href: "http://example.com", - tagName: "A", - }, - { - href: "http://example.com", - tagName: "AREA", - }, - ]; - validAnchorElements.forEach((element) => { - expect(isSupportedAnchorElement(element)).toBe(true); - }); - }); - it("Returns false for unsupported anchor elements", () => { - const invalidAnchorElements = [ - {}, - { - href: "", - }, - { - href: "http://example.com", - }, - { - href: "http://example.com", - tagName: "LINK", - }, - { - href: "http://example.com", - tagName: "A", - onclick: "example();", - protocol: " javascript:", - }, - ]; - invalidAnchorElements.forEach((element) => { - expect(isSupportedAnchorElement(element)).toBe(false); - }); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/dom/isSupportedTextNode.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/isSupportedTextNode.spec.js deleted file mode 100644 index d523a1cff..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/dom/isSupportedTextNode.spec.js +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isSupportedTextNode from "../../../../../../../src/components/ActivityCollector/utils/dom/isSupportedTextNode.js"; - -describe("ActivityCollector::isSupportedTextNode", () => { - it("should return true for text node", () => { - const textNode = document.createTextNode("text"); - expect(isSupportedTextNode(textNode)).toBe(true); - }); - - it("should return false for comment node", () => { - const commentNode = document.createComment("comment"); - expect(isSupportedTextNode(commentNode)).toBe(false); - }); - - it("should return true for a paragraph node", () => { - const paragraphNode = document.createElement("p"); - expect(isSupportedTextNode(paragraphNode)).toBe(true); - }); - - it("should return false for a script node", () => { - const scriptNode = document.createElement("script"); - expect(isSupportedTextNode(scriptNode)).toBe(false); - }); - - it("should return false for a style node", () => { - const styleNode = document.createElement("style"); - expect(isSupportedTextNode(styleNode)).toBe(false); - }); - - it("should return false for a link node", () => { - const linkNode = document.createElement("link"); - expect(isSupportedTextNode(linkNode)).toBe(false); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/hasPageName.spec.js b/test/unit/specs/components/ActivityCollector/utils/hasPageName.spec.js deleted file mode 100644 index ae535ebbb..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/hasPageName.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import hasPageName from "../../../../../../src/components/ActivityCollector/utils/hasPageName.js"; - -describe("ActivityCollector::hasPageName", () => { - it("should return true if event has page name", () => { - const event = { - getContent: () => ({ - xdm: { - web: { - webPageDetails: { - name: "test", - }, - }, - }, - }), - }; - expect(hasPageName(event)).toBe(true); - }); - - it("should return false if event does not have page name", () => { - const event = { - getContent: () => ({ - xdm: { - web: {}, - }, - }), - }; - expect(hasPageName(event)).toBe(false); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/isDifferentDomains.spec.js b/test/unit/specs/components/ActivityCollector/utils/isDifferentDomains.spec.js deleted file mode 100644 index 94a8470f6..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/isDifferentDomains.spec.js +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright 2024 example. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isDifferentDomains from "../../../../../../src/components/ActivityCollector/utils/isDifferentDomains.js"; - -describe("ActivityCollector::isDifferentDomains", () => { - it("should return true if the domains are different", () => { - expect(isDifferentDomains("www.example.com", "www.example.org")).toBe(true); - }); - - it("should return false if the domains are the same", () => { - expect( - isDifferentDomains("https://www.example.com", "www.example.com"), - ).toBe(false); - expect(isDifferentDomains("www.example.com", "www.example.com")).toBe( - false, - ); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/trimQueryFromUrl.spec.js b/test/unit/specs/components/ActivityCollector/utils/trimQueryFromUrl.spec.js deleted file mode 100644 index 01198eb9b..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/trimQueryFromUrl.spec.js +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import trimQueryFromUrl from "../../../../../../src/components/ActivityCollector/utils/trimQueryFromUrl.js"; - -describe("ActivityCollector::trimQueryFromUrl", () => { - it("Removes query portion from URL", () => { - const urls = [ - ["http://example.com", "http://example.com"], - [ - "https://example.com:123/example?example=123", - "https://example.com:123/example", - ], - ["file://example.txt", "file://example.txt"], - ["http://example.com/?example=123", "http://example.com/"], - ["http://example.com/#example", "http://example.com/"], - ]; - urls.forEach((url) => { - expect(trimQueryFromUrl(url[0])).toBe(url[1]); - }); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/truncateWhiteSpace.spec.js b/test/unit/specs/components/ActivityCollector/utils/truncateWhiteSpace.spec.js deleted file mode 100644 index ba82f9554..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/truncateWhiteSpace.spec.js +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import truncateWhiteSpace from "../../../../../../src/components/ActivityCollector/utils/truncateWhiteSpace.js"; - -describe("ActivityCollector::truncateWhiteSpace", () => { - it("it trims leading and trailing white spaces and limits contained white space to one character", () => { - const testCases = [ - [" hello world ", "hello world"], - [" hello world ", "hello world"], - ["hello world", "hello world"], - ["hello world ", "hello world"], - [" hello world", "hello world"], - ["", ""], - [" ", ""], - [" ", ""], - ]; - testCases.forEach((testCase) => { - expect(truncateWhiteSpace(testCase[0])).toBe(testCase[1]); - }); - }); -}); diff --git a/test/unit/specs/components/ActivityCollector/utils/urlStartsWithScheme.spec.js b/test/unit/specs/components/ActivityCollector/utils/urlStartsWithScheme.spec.js deleted file mode 100644 index 43943bf06..000000000 --- a/test/unit/specs/components/ActivityCollector/utils/urlStartsWithScheme.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import urlStartsWithScheme from "../../../../../../src/components/ActivityCollector/utils/urlStartsWithScheme.js"; - -describe("ActivityCollector::urlStartsWithScheme", () => { - it("Returns true for URLs that starts with a scheme", () => { - const urlsThatStartsWithScheme = [ - "http://example.com", - "https://example.com", - "https://example.com:123/example?example=123", - "file://example.txt", - ]; - urlsThatStartsWithScheme.forEach((url) => { - expect(urlStartsWithScheme(url)).toBe(true); - }); - }); - it("Returns false for URLs that does not start with a scheme", () => { - const urlsThatDoesNotStartWithScheme = [ - "example.com", - "example.txt/http://example", - "https:", - "//example.html", - "", - null, - undefined, - ]; - urlsThatDoesNotStartWithScheme.forEach((url) => { - expect(urlStartsWithScheme(url)).toBe(false); - }); - }); -}); diff --git a/test/unit/specs/components/Audiences/injectProcessDestinations.spec.js b/test/unit/specs/components/Audiences/injectProcessDestinations.spec.js deleted file mode 100644 index dd0503c9a..000000000 --- a/test/unit/specs/components/Audiences/injectProcessDestinations.spec.js +++ /dev/null @@ -1,176 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import injectProcessDestinations from "../../../../../src/components/Audiences/injectProcessDestinations.js"; - -describe("Audiences::injectProcessDestinations", () => { - let fireReferrerHideableImage; - let cookieJar; - let logger; - let isPageSsl; - let processDestinations; - - beforeEach(() => { - fireReferrerHideableImage = jasmine - .createSpy() - .and.returnValue(Promise.resolve()); - logger = jasmine.createSpyObj("logger", ["info", "error"]); - cookieJar = jasmine.createSpyObj("cookieJar", ["set"]); - isPageSsl = true; - }); - - const inject = () => { - processDestinations = injectProcessDestinations({ - fireReferrerHideableImage, - logger, - cookieJar, - isPageSsl, - }); - }; - - const SAMPLE_DESTINATIONS1 = [ - { - type: "url", - id: 2097728, - spec: { - url: "http://test.abc", - hideReferrer: true, - }, - }, - { - type: "cookie", - spec: { - name: "audlabcookie", - value: "dgtest\u003ddevicegraphtestdestination1", - }, - }, - { - type: "cookie", - spec: { - name: "testCookieDestination", - value: "destination\u003ds2", - domain: "adobe.com", - ttlDays: 30, - }, - }, - ]; - - it("sets cookie destinations", () => { - inject(); - return processDestinations(SAMPLE_DESTINATIONS1).then(() => { - expect(cookieJar.set).toHaveBeenCalledWith( - "audlabcookie", - "dgtest\u003ddevicegraphtestdestination1", - { - domain: "", - expires: 10, - sameSite: "none", - secure: true, - }, - ); - expect(cookieJar.set).toHaveBeenCalledWith( - "testCookieDestination", - "destination\u003ds2", - { - domain: "adobe.com", - expires: 30, - sameSite: "none", - secure: true, - }, - ); - }); - }); - - it("sets cookie destinations without sameSite flag", () => { - isPageSsl = false; - inject(); - return processDestinations(SAMPLE_DESTINATIONS1).then(() => { - expect(cookieJar.set).toHaveBeenCalledWith( - "audlabcookie", - "dgtest\u003ddevicegraphtestdestination1", - { - domain: "", - expires: 10, - }, - ); - expect(cookieJar.set).toHaveBeenCalledWith( - "testCookieDestination", - "destination\u003ds2", - { - domain: "adobe.com", - expires: 30, - }, - ); - }); - }); - - it("calls fireReferrerHideableImage for all destinations of type URL and logs results", () => { - inject(); - fireReferrerHideableImage.and.callFake(({ url }) => { - return url === "http://test.zyx" ? Promise.resolve() : Promise.reject(); - }); - return processDestinations([ - { - type: "url", - id: 2097728, - spec: { - url: "http://test.abc", - hideReferrer: true, - }, - }, - { - type: "cookie", - spec: { - name: "testCookieDestination", - value: "destination\u003ds2", - domain: "", - ttlDays: 30, - }, - }, - { - type: "url", - id: 2097729, - spec: { - url: "http://test.zyx", - hideReferrer: false, - }, - }, - ]).then(() => { - expect(fireReferrerHideableImage).toHaveBeenCalledWith({ - url: "http://test.abc", - hideReferrer: true, - }); - expect(fireReferrerHideableImage).toHaveBeenCalledWith({ - url: "http://test.zyx", - hideReferrer: false, - }); - expect(logger.info).toHaveBeenCalledWith( - "URL destination succeeded: http://test.zyx", - ); - }); - }); - it("doesn't return a value", () => { - inject(); - const destinations = [ - { - type: "url", - id: 2097728, - spec: { - url: "http://test.abc", - hideReferrer: true, - }, - }, - ]; - return expectAsync(processDestinations(destinations)).toBeResolvedTo( - undefined, - ); - }); -}); diff --git a/test/unit/specs/components/Audiences/injectProcessResponse.spec.js b/test/unit/specs/components/Audiences/injectProcessResponse.spec.js deleted file mode 100644 index 004f6ecbc..000000000 --- a/test/unit/specs/components/Audiences/injectProcessResponse.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2021 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectProcessResponse from "../../../../../src/components/Audiences/injectProcessResponse.js"; - -describe("injectProcessResponse", () => { - let response; - let processResponse; - let processDestinations; - - beforeEach(() => { - processDestinations = jasmine - .createSpy("processDestinations") - .and.returnValue(Promise.resolve()); - processResponse = injectProcessResponse({ processDestinations }); - - response = jasmine.createSpyObj("response", { - getPayloadsByType: ["An Edge Destination"], - }); - }); - - it("fetches destinations from the response", () => { - return processResponse({ response }).then((result) => { - expect(processDestinations).toHaveBeenCalled(); - expect(result).toEqual({ - destinations: ["An Edge Destination"], - }); - }); - }); - - it("returns [] if no destinations were found", () => { - const responseWithNoDestinations = jasmine.createSpyObj("response", { - getPayloadsByType: [], - }); - return processResponse({ response: responseWithNoDestinations }).then( - (result) => { - expect(result).toEqual({ - destinations: [], - }); - }, - ); - }); -}); diff --git a/test/unit/specs/components/Consent/computeConsentHash.spec.js b/test/unit/specs/components/Consent/computeConsentHash.spec.js deleted file mode 100644 index 06701ea5b..000000000 --- a/test/unit/specs/components/Consent/computeConsentHash.spec.js +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import computeConsentHash from "../../../../../src/components/Consent/computeConsentHash.js"; - -describe("computeConsentHash", () => { - it("works", () => { - expect( - computeConsentHash([ - { - standard: "Adobe", - version: "1.0", - value: { - general: "in", - }, - }, - ]), - ).toBe(2905535662); - }); - - [ - [ - { a: 1, b: 2 }, - { b: 2, a: 1 }, - ], - [[{ a: 1, b: 2 }], [{ b: 2, a: 1 }]], - [{ a: { b: 2, c: 3 } }, { a: { c: 3, b: 2 } }], - [ - { a: [1], b: [2] }, - { b: [2], a: [1] }, - ], - [{ a: undefined }, {}], - ].forEach(([a, b], index) => { - it(`computes the same hash ${index}`, () => { - expect(computeConsentHash(a)).toBe(computeConsentHash(b)); - }); - }); - - [ - [ - [1, 2], - [2, 1], - ], - ["1", 1], - [{ a: null }, { a: undefined }], - [{ "xdm:key": "value" }, { xdm: "key:value" }], - [null, {}], - ].forEach(([a, b], index) => { - it(`computes a different hash ${index}`, () => { - expect(computeConsentHash(a)).not.toBe(computeConsentHash(b)); - }); - }); -}); diff --git a/test/unit/specs/components/Consent/configValidators.spec.js b/test/unit/specs/components/Consent/configValidators.spec.js deleted file mode 100644 index 12b3b689f..000000000 --- a/test/unit/specs/components/Consent/configValidators.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -import configValidators from "../../../../../src/components/Consent/configValidators.js"; - -describe("defaultConsent", () => { - it("validates defaultConsent=undefined", () => { - const config = configValidators({}); - expect(config.defaultConsent).toEqual("in"); - }); - it("validates defaultConsent={}", () => { - expect(() => { - configValidators({ - defaultConsent: {}, - }); - }).toThrowError(); - }); - it("validates defaultConsent='in'", () => { - const config = configValidators({ - defaultConsent: "in", - }); - expect(config.defaultConsent).toEqual("in"); - }); - it("validates defaultConsent='pending'", () => { - const config = configValidators({ - defaultConsent: "pending", - }); - expect(config.defaultConsent).toEqual("pending"); - }); - it("validates defaultConsent=123", () => { - expect(() => { - configValidators({ defaultConsent: 123 }); - }).toThrowError(); - }); - it("validates defaultConsent='out'", () => { - const config = configValidators({ - defaultConsent: "out", - }); - expect(config.defaultConsent).toEqual("out"); - }); -}); diff --git a/test/unit/specs/components/Consent/createComponent.spec.js b/test/unit/specs/components/Consent/createComponent.spec.js deleted file mode 100644 index 88c27be0e..000000000 --- a/test/unit/specs/components/Consent/createComponent.spec.js +++ /dev/null @@ -1,319 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createComponent from "../../../../../src/components/Consent/createComponent.js"; -import { createTaskQueue, defer } from "../../../../../src/utils/index.js"; -import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; - -const createConsent = (generalConsent) => ({ - consent: [ - { - standard: "Adobe", - version: "1.0", - value: { - general: generalConsent, - }, - }, - ], -}); -const CONSENT_IN = createConsent("in"); -const CONSENT_OUT = createConsent("out"); - -describe("consent:createComponent", () => { - let storedConsent; - let taskQueue; - let defaultConsent; - let consent; - let sendSetConsentRequest; - let validateSetConsentOptions; - let consentHashStore; - let consentHashes; - let doesIdentityCookieExist; - let requestFailureError; - let component; - let config; - - const setIdentityCookie = () => { - doesIdentityCookieExist.and.returnValue(true); - }; - - const clearIdentityCookie = () => { - doesIdentityCookieExist.and.returnValue(false); - }; - - beforeEach(() => { - storedConsent = jasmine.createSpyObj("storedConsent", ["read", "clear"]); - taskQueue = createTaskQueue(); - defaultConsent = "in"; - consent = jasmine.createSpyObj("consent", [ - "initializeConsent", - "setConsent", - "suspend", - ]); - sendSetConsentRequest = jasmine.createSpy("sendSetConsentRequest"); - validateSetConsentOptions = jasmine - .createSpy("validateSetConsentOptions") - .and.callFake((options) => options); - consentHashStore = jasmine.createSpyObj("consentHashStore", [ - "clear", - "lookup", - ]); - consentHashes = jasmine.createSpyObj("consentHashes", ["isNew", "save"]); - doesIdentityCookieExist = jasmine.createSpy("doesIdentityCookieExist"); - consentHashStore.lookup.and.returnValue(consentHashes); - setIdentityCookie(); - consentHashes.isNew.and.returnValue(true); - requestFailureError = new Error("Request for setting test consent failed."); - config = { - edgeConfigOverrides: {}, - }; - }); - - const build = () => { - component = createComponent({ - storedConsent, - taskQueue, - defaultConsent, - consent, - sendSetConsentRequest, - validateSetConsentOptions, - consentHashStore, - doesIdentityCookieExist, - config, - }); - }; - - const clearConsentCookie = function clearConsentCookie() { - storedConsent.read.and.returnValue({}); - }; - - const setConsentCookieIn = function setConsentCookieIn() { - storedConsent.read.and.returnValue({ general: "in" }); - }; - - const setConsentCookieOut = function setConsentCookieOut() { - storedConsent.read.and.returnValue({ general: "out" }); - }; - - const mockSetConsent = () => { - const deferred = defer(); - sendSetConsentRequest.and.returnValue(deferred.promise); - // ensure that there will be no uncaught promise rejections - deferred.promise.catch(() => {}); - return { - respondWithIn() { - setConsentCookieIn(); - deferred.resolve(); - }, - respondWithOut() { - setConsentCookieOut(); - deferred.resolve(); - }, - respondWithNoCookie() { - deferred.resolve(); - }, - respondWithError() { - deferred.reject(requestFailureError); - }, - }; - }; - - it("initializes consent", () => { - defaultConsent = "mydefaultconsent"; - storedConsent.read.and.returnValue({ general: "myinitialconsent" }); - build(); - expect(consent.initializeConsent).toHaveBeenCalledWith( - { general: "mydefaultconsent" }, - { general: "myinitialconsent" }, - ); - }); - - it("handles the setConsent command", () => { - defaultConsent = "pending"; - clearConsentCookie(); - build(); - const setConsentMock = mockSetConsent(); - const onResolved = jasmine.createSpy("onResolved"); - component.commands.setConsent - .run({ identityMap: { my: "map" }, ...CONSENT_IN }) - .then(onResolved); - expect(consent.suspend).toHaveBeenCalled(); - setConsentMock.respondWithIn(); - return flushPromiseChains().then(() => { - expect(sendSetConsentRequest).toHaveBeenCalledWith( - jasmine.objectContaining({ - consentOptions: CONSENT_IN.consent, - identityMap: { my: "map" }, - }), - ); - expect(consent.setConsent).toHaveBeenCalledWith({ general: "in" }); - expect(onResolved).toHaveBeenCalledWith(undefined); - }); - }); - - it("handles the setConsent command with overrides, if provided", () => { - defaultConsent = "pending"; - clearConsentCookie(); - build(); - const setConsentMock = mockSetConsent(); - const onResolved = jasmine.createSpy("onResolved"); - component.commands.setConsent - .run({ - identityMap: { my: "map" }, - edgeConfigOverrides: { - com_adobe_identity: { - idSyncContainerId: "1234", - }, - }, - ...CONSENT_IN, - }) - .then(onResolved); - expect(consent.suspend).toHaveBeenCalled(); - setConsentMock.respondWithIn(); - return flushPromiseChains().then(() => { - expect(sendSetConsentRequest).toHaveBeenCalledWith({ - consentOptions: CONSENT_IN.consent, - identityMap: { my: "map" }, - edgeConfigOverrides: { - com_adobe_identity: { - idSyncContainerId: "1234", - }, - }, - }); - expect(consent.setConsent).toHaveBeenCalledWith({ general: "in" }); - expect(onResolved).toHaveBeenCalledWith(undefined); - }); - }); - - it("updates the consent object even after a request failure", () => { - defaultConsent = "pending"; - clearConsentCookie(); - build(); - const setConsentMock = mockSetConsent(); - const onRejected = jasmine.createSpy("onRejected"); - component.commands.setConsent.run(CONSENT_IN).catch(onRejected); - setConsentCookieIn(); - setConsentMock.respondWithError(); - return flushPromiseChains().then(() => { - expect(consent.setConsent).toHaveBeenCalledWith({ general: "in" }); - expect(onRejected).toHaveBeenCalledWith(requestFailureError); - }); - }); - - it("only updates the consent object after the response returns", () => { - defaultConsent = "pending"; - clearConsentCookie(); - build(); - const setConsentMock = mockSetConsent(); - component.commands.setConsent.run(CONSENT_IN); - return flushPromiseChains() - .then(() => { - expect(sendSetConsentRequest).toHaveBeenCalledWith( - jasmine.objectContaining({ - consentOptions: CONSENT_IN.consent, - }), - ); - expect(consent.setConsent).not.toHaveBeenCalledWith({ general: "in" }); - setConsentMock.respondWithIn(); - return flushPromiseChains(); - }) - .then(() => { - expect(consent.setConsent).toHaveBeenCalledWith({ general: "in" }); - }); - }); - - it("only calls setConsent once with multiple consent requests", () => { - defaultConsent = "pending"; - clearConsentCookie(); - consentHashes.isNew.and.returnValue(true); - build(); - const setConsentMock1 = mockSetConsent(); - let setConsentMock2; - component.commands.setConsent.run(CONSENT_IN); - return flushPromiseChains() - .then(() => { - expect(sendSetConsentRequest).toHaveBeenCalledWith( - jasmine.objectContaining({ - consentOptions: CONSENT_IN.consent, - }), - ); - setConsentMock2 = mockSetConsent(); - component.commands.setConsent.run(CONSENT_OUT); - setConsentMock1.respondWithIn(); - return flushPromiseChains(); - }) - .then(() => { - expect(sendSetConsentRequest).toHaveBeenCalledWith( - jasmine.objectContaining({ - consentOptions: CONSENT_OUT.consent, - }), - ); - setConsentMock2.respondWithOut(); - return flushPromiseChains(); - }) - .then(() => { - expect(consent.setConsent).not.toHaveBeenCalledWith({ general: "in" }); - expect(consent.setConsent).toHaveBeenCalledTimes(1); - expect(consent.setConsent).toHaveBeenCalledWith({ general: "out" }); - }); - }); - - it("checks the cookie after an event", () => { - clearConsentCookie(); - build(); - setConsentCookieOut(); - component.lifecycle.onResponse(); - expect(consent.setConsent).toHaveBeenCalledWith({ general: "out" }); - }); - - it("checks the cookie after an error response", () => { - clearConsentCookie(); - build(); - setConsentCookieOut(); - component.lifecycle.onRequestFailure(); - expect(consent.setConsent).toHaveBeenCalledWith({ general: "out" }); - }); - - it("clears storage when the identity cookie is missing", () => { - setConsentCookieIn(); - clearIdentityCookie(); - build(); - expect(consentHashStore.clear).toHaveBeenCalled(); - expect(storedConsent.clear).toHaveBeenCalled(); - expect(consent.initializeConsent).toHaveBeenCalledWith( - { general: "in" }, - {}, - ); - }); - - it("clears storage when the consent cookie is missing", () => { - clearConsentCookie(); - setIdentityCookie(); - build(); - expect(consentHashStore.clear).toHaveBeenCalled(); - expect(storedConsent.clear).not.toHaveBeenCalled(); - }); - - it("doesn't call setConsent when there is no cookie after onResponse", () => { - clearConsentCookie(); - build(); - component.lifecycle.onResponse(); - expect(consent.setConsent).not.toHaveBeenCalled(); - }); - - it("doesn't call setConsent when there is no cookie after onRequestFailure", () => { - clearConsentCookie(); - build(); - component.lifecycle.onRequestFailure(); - expect(consent.setConsent).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Consent/createConsentHashStore.spec.js b/test/unit/specs/components/Consent/createConsentHashStore.spec.js deleted file mode 100644 index 833623787..000000000 --- a/test/unit/specs/components/Consent/createConsentHashStore.spec.js +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createConsentHashStore from "../../../../../src/components/Consent/createConsentHashStore.js"; - -const CONSENT_IN = { - standard: "Adobe", - version: "1.0", - value: { general: "in" }, -}; - -const CONSENT_OUT = { - standard: "Adobe", - version: "1.0", - value: { general: "out" }, -}; - -describe("createConsentHashStore", () => { - let storage; - let subject; - beforeEach(() => { - storage = jasmine.createSpyObj("storage", ["getItem", "setItem", "clear"]); - subject = createConsentHashStore({ storage }); - }); - - it("clears", () => { - subject.clear(); - expect(storage.clear).toHaveBeenCalled(); - }); - - it("is new when storage is empty", () => { - storage.getItem.and.returnValue(null); - const consentHashes = subject.lookup([CONSENT_IN]); - expect(consentHashes.isNew()).toBe(true); - }); - - it("saves the hash", () => { - const consentHashes = subject.lookup([CONSENT_IN]); - consentHashes.save(); - expect(storage.setItem).toHaveBeenCalledWith("Adobe.1.0", "3165644325"); - }); - - it("is not new when lookup is the same", () => { - storage.getItem.and.returnValue("3165644325"); - const consentHashes = subject.lookup([CONSENT_IN]); - expect(consentHashes.isNew()).toBe(false); - expect(storage.getItem).toHaveBeenCalledWith("Adobe.1.0"); - }); - - it("is new when lookup is different", () => { - storage.getItem.and.returnValue("3165644325"); - const consentHashes = subject.lookup([CONSENT_OUT]); - expect(consentHashes.isNew()).toBe(true); - expect(storage.getItem).toHaveBeenCalledWith("Adobe.1.0"); - }); -}); diff --git a/test/unit/specs/components/Consent/createConsentRequest.spec.js b/test/unit/specs/components/Consent/createConsentRequest.spec.js deleted file mode 100644 index 940b2166c..000000000 --- a/test/unit/specs/components/Consent/createConsentRequest.spec.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import describeRequest from "../../../helpers/describeRequest.js"; -import createConsentRequest from "../../../../../src/components/Consent/createConsentRequest.js"; - -describe("createConsentRequest", () => { - describeRequest(createConsentRequest); - - it("provides the appropriate action", () => { - const payload = {}; - const request = createConsentRequest({ payload }); - expect(request.getAction()).toBe("privacy/set-consent"); - }); - - it("never uses sendBeacon", () => { - const payload = {}; - const request = createConsentRequest({ payload }); - expect(request.getUseSendBeacon()).toBeFalse(); - }); - - it("passes the datastreamIdOverride to the request", () => { - const payload = {}; - const datastreamIdOverride = "my-edge-config-id-override"; - const request = createConsentRequest({ - payload, - datastreamIdOverride, - }); - expect(request.getDatastreamIdOverride()).toBe(datastreamIdOverride); - }); -}); diff --git a/test/unit/specs/components/Consent/createConsentRequestPayload.spec.js b/test/unit/specs/components/Consent/createConsentRequestPayload.spec.js deleted file mode 100644 index d794e09bd..000000000 --- a/test/unit/specs/components/Consent/createConsentRequestPayload.spec.js +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createConsentRequestPayload from "../../../../../src/components/Consent/createConsentRequestPayload.js"; -import describeRequestPayload from "../../../helpers/describeRequestPayload.js"; - -describe("createConsentRequestPayload", () => { - describeRequestPayload(createConsentRequestPayload); - - it("adds an identity", () => { - const payload = createConsentRequestPayload(); - payload.addIdentity("IDNS", { - id: "ABC123", - }); - payload.addIdentity("IDNS", { - id: "DEF456", - }); - expect(JSON.parse(JSON.stringify(payload))).toEqual({ - identityMap: { - IDNS: [ - { - id: "ABC123", - }, - { - id: "DEF456", - }, - ], - }, - }); - }); - - it("sets consent", () => { - const payload = createConsentRequestPayload(); - payload.setConsent([ - { - standard: "Adobe", - version: "1.0", - value: { - general: "in", - }, - }, - ]); - expect(JSON.parse(JSON.stringify(payload))).toEqual({ - consent: [ - { - standard: "Adobe", - version: "1.0", - value: { - general: "in", - }, - }, - ], - }); - }); -}); diff --git a/test/unit/specs/components/Consent/createStoredConsent.spec.js b/test/unit/specs/components/Consent/createStoredConsent.spec.js deleted file mode 100644 index 508acd50b..000000000 --- a/test/unit/specs/components/Consent/createStoredConsent.spec.js +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createStoredConsent from "../../../../../src/components/Consent/createStoredConsent.js"; - -describe("Consent:createStoredConsent", () => { - let parseConsentCookie; - const orgId = "myorgid@mycompany"; - let cookieJar; - let storedConsent; - - beforeEach(() => { - parseConsentCookie = jasmine.createSpy("parseConsentCookie"); - cookieJar = jasmine.createSpyObj("cookieJar", ["get", "remove"]); - storedConsent = createStoredConsent({ - parseConsentCookie, - orgId, - cookieJar, - }); - }); - - it("gets the cookie", () => { - cookieJar.get.and.returnValue("cookieValue"); - parseConsentCookie.and.returnValue("parsedConsentValue"); - expect(storedConsent.read()).toEqual("parsedConsentValue"); - expect(parseConsentCookie).toHaveBeenCalledWith("cookieValue"); - }); - - it("returns empty object if the cookie is not there", () => { - cookieJar.get.and.returnValue(undefined); - expect(storedConsent.read()).toEqual({}); - expect(parseConsentCookie).not.toHaveBeenCalled(); - }); - - it("uses the correct cookie name", () => { - storedConsent.read(); - expect(cookieJar.get).toHaveBeenCalledWith( - "kndctr_myorgid_mycompany_consent", - ); - }); - - it("removes the cookie", () => { - storedConsent.clear(); - expect(cookieJar.remove).toHaveBeenCalledWith( - "kndctr_myorgid_mycompany_consent", - ); - }); -}); diff --git a/test/unit/specs/components/Consent/injectSendSetConsentRequest.spec.js b/test/unit/specs/components/Consent/injectSendSetConsentRequest.spec.js deleted file mode 100644 index 4cbd86bd5..000000000 --- a/test/unit/specs/components/Consent/injectSendSetConsentRequest.spec.js +++ /dev/null @@ -1,133 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import injectSendSetConsentRequest from "../../../../../src/components/Consent/injectSendSetConsentRequest.js"; - -describe("Consent:injectSendSetConsentRequest", () => { - let sendEdgeNetworkRequest; - let requestPayload; - let request; - let createConsentRequestPayload; - let createConsentRequest; - let sendSetConsentRequest; - let globalEdgeConfigOverrides; - - beforeEach(() => { - sendEdgeNetworkRequest = jasmine.createSpy("sendEdgeNetworkRequest"); - requestPayload = jasmine.createSpyObj("requestPayload", [ - "setConsent", - "addIdentity", - "mergeConfigOverride", - ]); - createConsentRequestPayload = jasmine - .createSpy("createConsentRequestPayload") - .and.returnValue(requestPayload); - request = { - getPayload() { - return requestPayload; - }, - }; - createConsentRequest = jasmine - .createSpy("createConsentRequest") - .and.returnValue(request); - globalEdgeConfigOverrides = {}; - sendSetConsentRequest = injectSendSetConsentRequest({ - createConsentRequestPayload, - createConsentRequest, - sendEdgeNetworkRequest, - edgeConfigOverrides: globalEdgeConfigOverrides, - }); - }); - - it("sets consent level and on requestPayload and sends the request", () => { - sendEdgeNetworkRequest.and.returnValue(Promise.resolve()); - return sendSetConsentRequest({ - consentOptions: "anything", - identityMap: { - a: [{ id: "1" }, { id: "2" }], - b: [{ id: "3" }], - }, - }).then((resolvedValue) => { - expect(requestPayload.setConsent).toHaveBeenCalledWith("anything"); - expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ - request, - }); - expect(resolvedValue).toBeUndefined(); - expect(requestPayload.addIdentity).toHaveBeenCalledWith("a", { id: "1" }); - expect(requestPayload.addIdentity).toHaveBeenCalledWith("a", { id: "2" }); - expect(requestPayload.addIdentity).toHaveBeenCalledWith("b", { id: "3" }); - }); - }); - - it("sets the configuration overrides on the payload, if provided", () => { - sendEdgeNetworkRequest.and.returnValue(Promise.resolve()); - return sendSetConsentRequest({ - consentOptions: "anything", - identityMap: { - a: [{ id: "1" }, { id: "2" }], - b: [{ id: "3" }], - }, - edgeConfigOverrides: { - com_adobe_identity: { - idSyncContainerId: "123", - }, - }, - }).then(() => { - expect(requestPayload.setConsent).toHaveBeenCalledWith("anything"); - expect(requestPayload.mergeConfigOverride).toHaveBeenCalledWith({ - com_adobe_identity: { - idSyncContainerId: "123", - }, - }); - }); - }); - - it("sets the configuration overrides on the payload, if provided, from the global config", () => { - sendEdgeNetworkRequest.and.returnValue(Promise.resolve()); - globalEdgeConfigOverrides.com_adobe_identity = { - idSyncContainerId: "123", - }; - return sendSetConsentRequest({ - consentOptions: "anything", - identityMap: { - a: [{ id: "1" }, { id: "2" }], - b: [{ id: "3" }], - }, - }).then(() => { - expect(requestPayload.setConsent).toHaveBeenCalledWith("anything"); - expect(requestPayload.mergeConfigOverride).toHaveBeenCalledWith({ - com_adobe_identity: { - idSyncContainerId: "123", - }, - }); - }); - }); - - it("sets the override for the datastreamId, if provided", () => { - sendEdgeNetworkRequest.and.returnValue(Promise.resolve()); - return sendSetConsentRequest({ - consentOptions: "anything", - identityMap: { - a: [{ id: "1" }, { id: "2" }], - b: [{ id: "3" }], - }, - edgeConfigOverrides: { - datastreamId: "123", - }, - }).then(() => { - expect(requestPayload.setConsent).toHaveBeenCalledWith("anything"); - expect(createConsentRequest).toHaveBeenCalledWith({ - payload: jasmine.any(Object), - datastreamIdOverride: "123", - }); - }); - }); -}); diff --git a/test/unit/specs/components/Consent/parseConsentCookie.spec.js b/test/unit/specs/components/Consent/parseConsentCookie.spec.js deleted file mode 100644 index c0d6def50..000000000 --- a/test/unit/specs/components/Consent/parseConsentCookie.spec.js +++ /dev/null @@ -1,22 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import parseConsentCookie from "../../../../../src/components/Consent/parseConsentCookie.js"; - -describe("parseConsentCookie", () => { - it("returns preferences by purpose", () => { - expect(parseConsentCookie("foo=in;bar=out")).toEqual({ - foo: "in", - bar: "out", - }); - }); -}); diff --git a/test/unit/specs/components/Consent/validateSetConsentOptions.spec.js b/test/unit/specs/components/Consent/validateSetConsentOptions.spec.js deleted file mode 100644 index f82ffb38a..000000000 --- a/test/unit/specs/components/Consent/validateSetConsentOptions.spec.js +++ /dev/null @@ -1,131 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import validateSetConsentOptions from "../../../../../src/components/Consent/validateSetConsentOptions.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -const validGeneralConsent = [ - { standard: "Adobe", version: "1.0", value: { general: "in" } }, -]; - -describeValidation( - "Consent:validateSetConsentOptions", - validateSetConsentOptions, - [ - { - value: { - consent: [ - { standard: "Adobe", version: "1.0", value: { general: "in" } }, - ], - }, - }, - { value: { consent: [] }, error: true }, - { value: { consent: null }, error: true }, - { value: { consent: undefined }, error: true }, - { value: "in", error: true }, - { value: undefined, error: true }, - { value: null, error: true }, - { - value: { - consent: [ - { - standard: "IAB", - version: "2.0", - value: "1234abcd", - gdprApplies: true, - }, - ], - }, - }, - { - value: { - consent: [ - { - standard: "IAB", - version: "2.0", - value: "1234abcd", - gdprApplies: true, - }, - { standard: "Adobe", version: "1.0", value: { general: "in" } }, - ], - }, - }, - { - value: { - consent: validGeneralConsent, - identityMap: { - HYP: [{}], - }, - }, - }, - { - value: { - consent: validGeneralConsent, - identityMap: { - HYP: [ - { - id: "1234", - authenticatedState: "ambiguous", - }, - ], - }, - }, - }, - { - value: { - consent: validGeneralConsent, - identityMap: { - HYP: [ - { - blah: "1234", - }, - ], - }, - }, - error: true, - }, - { - value: { - consent: validGeneralConsent, - identityMap: [], - }, - error: true, - }, - { - value: { - consent: validGeneralConsent, - identityMap: { - email: [], - }, - }, - }, - { - value: { - consent: validGeneralConsent, - identityMap: { - email: [[]], - }, - }, - error: true, - }, - { - value: { - consent: validGeneralConsent, - edgeConfigOverrides: { - identity: { - idSyncContainerId: "123", - }, - }, - }, - }, - { value: { consent: validGeneralConsent, edgeConfigOverrides: {} } }, - ], -); diff --git a/test/unit/specs/components/Context/createComponent.spec.js b/test/unit/specs/components/Context/createComponent.spec.js deleted file mode 100644 index b24eb48a0..000000000 --- a/test/unit/specs/components/Context/createComponent.spec.js +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createComponent from "../../../../../src/components/Context/createComponent.js"; -import createConfig from "../../../../../src/core/config/createConfig.js"; - -describe("Context::createComponent", () => { - const logger = { - log() {}, - warn() {}, - }; - const context1 = (xdm) => { - xdm.a = "1"; - }; - const context2 = (xdm) => { - xdm.b = "2"; - }; - const requiredContext = (xdm) => { - xdm.c = "3"; - }; - const availableContexts = { context1, context2 }; - let event; - - beforeEach(() => { - event = jasmine.createSpyObj("event", ["mergeXdm"]); - }); - - it("enables the configured contexts", async () => { - const config = createConfig({ context: ["context1", "context2"] }); - const component = createComponent(config, logger, availableContexts, [ - requiredContext, - ]); - await component.lifecycle.onBeforeEvent({ event }); - - expect(event.mergeXdm).toHaveBeenCalledWith({ a: "1", b: "2", c: "3" }); - }); - it("ignores unknown contexts", async () => { - const config = createConfig({ context: ["unknowncontext", "context1"] }); - const component = createComponent(config, logger, availableContexts, [ - requiredContext, - ]); - await component.lifecycle.onBeforeEvent({ event }); - - expect(event.mergeXdm).toHaveBeenCalledWith({ a: "1", c: "3" }); - }); - - it("can disable non-required contexts", async () => { - const config = createConfig({ context: [] }); - const component = createComponent(config, logger, availableContexts, [ - requiredContext, - ]); - await component.lifecycle.onBeforeEvent({ event }); - - expect(event.mergeXdm).toHaveBeenCalledWith({ c: "3" }); - }); -}); diff --git a/test/unit/specs/components/Context/implementationDetails.spec.js b/test/unit/specs/components/Context/implementationDetails.spec.js deleted file mode 100644 index 4a1d4d912..000000000 --- a/test/unit/specs/components/Context/implementationDetails.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import implementationDetails from "../../../../../src/components/Context/implementationDetails.js"; - -describe("Context::implementationDetails", () => { - it("works", () => { - const xdm = {}; - implementationDetails(xdm); - expect(xdm).toEqual({ - implementationDetails: { - name: "https://ns.adobe.com/experience/alloy", - version: "__VERSION__", - environment: "browser", - }, - }); - }); -}); diff --git a/test/unit/specs/components/Context/injectDevice.spec.js b/test/unit/specs/components/Context/injectDevice.spec.js deleted file mode 100644 index 5d82c4441..000000000 --- a/test/unit/specs/components/Context/injectDevice.spec.js +++ /dev/null @@ -1,127 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import injectDevice from "../../../../../src/components/Context/injectDevice.js"; - -describe("Context::injectDevice", () => { - let window; - - beforeEach(() => { - window = { - screen: { - width: 600, - height: 800, - }, - }; - }); - - const run = () => { - const xdm = {}; - injectDevice(window)(xdm); - return xdm; - }; - - it("handles the happy path", () => { - window.screen.orientation = { type: "landscape-primary" }; - expect(run()).toEqual({ - device: { - screenHeight: 800, - screenWidth: 600, - screenOrientation: "landscape", - }, - }); - }); - - it("handles portrait orientation type", () => { - window.screen.orientation = { type: "portrait-secondary" }; - expect(run()).toEqual({ - device: { - screenHeight: 800, - screenWidth: 600, - screenOrientation: "portrait", - }, - }); - }); - - it("handles matchMedia queries: portrait", () => { - window.matchMedia = (query) => ({ - matches: query === "(orientation: portrait)", - }); - expect(run()).toEqual({ - device: { - screenHeight: 800, - screenWidth: 600, - screenOrientation: "portrait", - }, - }); - }); - - it("handles matchMedia queries: landscape", () => { - window.matchMedia = (query) => ({ - matches: query === "(orientation: landscape)", - }); - expect(run()).toEqual({ - device: { - screenHeight: 800, - screenWidth: 600, - screenOrientation: "landscape", - }, - }); - }); - - it("handles string values for the height and width", () => { - window = { - screen: { - width: "600", - height: "800", - }, - }; - expect(run()).toEqual({ - device: { - screenHeight: 800, - screenWidth: 600, - }, - }); - }); - it("handles no good values", () => { - window = { - screen: { - width: null, - height: undefined, - }, - }; - expect(run()).toEqual({}); - }); - - [ - undefined, - null, - {}, - { type: "foo" }, - { type: "a-b" }, - { type: null }, - ].forEach((orientation) => { - it(`handles a bad screen orientation: ${JSON.stringify( - orientation, - )}`, () => { - if (orientation !== undefined) { - window.screen.orientation = orientation; - } - window.matchMedia = () => ({ matches: false }); - expect(run()).toEqual({ - device: { - screenHeight: 800, - screenWidth: 600, - }, - }); - }); - }); -}); diff --git a/test/unit/specs/components/Context/injectEnvironment.spec.js b/test/unit/specs/components/Context/injectEnvironment.spec.js deleted file mode 100644 index 137a097e8..000000000 --- a/test/unit/specs/components/Context/injectEnvironment.spec.js +++ /dev/null @@ -1,202 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import injectEnvironment from "../../../../../src/components/Context/injectEnvironment.js"; - -describe("Context::injectEnvironment", () => { - const run = (description, mywindow, expectedXdm) => { - it(description, () => { - const xdm = {}; - injectEnvironment(mywindow)(xdm); - expect(xdm).toEqual(expectedXdm); - }); - }; - - run( - "uses the correct width and height", - { - screen: { width: 1001, height: 1002 }, - innerWidth: 1003, - innerHeight: 1004, - document: { - documentElement: { - clientWidth: 1005, - clientHeight: 1006, - }, - }, - }, - { - environment: { - type: "browser", - browserDetails: { - viewportWidth: 1005, - viewportHeight: 1006, - }, - }, - }, - ); - - run( - "handles negative width and height", - { - document: { - documentElement: { - clientWidth: -10, - clientHeight: -20, - }, - }, - }, - { - environment: { - type: "browser", - }, - }, - ); - - run( - "handles negative width", - { - document: { - documentElement: { - clientWidth: -10, - clientHeight: -42, - }, - }, - }, - { - environment: { - type: "browser", - }, - }, - ); - - run( - "handles missing width and height", - { - document: { - documentElement: {}, - }, - }, - { - environment: { - type: "browser", - }, - }, - ); - - run( - "handles missing documentElement", - { - document: {}, - }, - { - environment: { - type: "browser", - }, - }, - ); - - run( - "handles 0 height and width", - { - document: { - documentElement: { - clientWidth: 0, - clientHeight: 0, - }, - }, - }, - { - environment: { - type: "browser", - browserDetails: { - viewportWidth: 0, - viewportHeight: 0, - }, - }, - }, - ); - run( - "handles floating point height and width", - { - document: { - documentElement: { - clientWidth: 10, - clientHeight: 4.2, - }, - }, - }, - { - environment: { - type: "browser", - browserDetails: { - viewportWidth: 10, - viewportHeight: 4, - }, - }, - }, - ); - - run( - "handles null values", - { - document: { - documentElement: { - clientWidth: null, - clientHeight: null, - }, - }, - }, - { - environment: { - type: "browser", - }, - }, - ); - - run( - "handles only width", - { - document: { - documentElement: { - clientWidth: 1234, - }, - }, - }, - { - environment: { - type: "browser", - browserDetails: { - viewportWidth: 1234, - }, - }, - }, - ); - - run( - "handles only height", - { - document: { - documentElement: { - clientHeight: "1234.5", - }, - }, - }, - { - environment: { - type: "browser", - browserDetails: { - viewportHeight: 1235, - }, - }, - }, - ); -}); diff --git a/test/unit/specs/components/Context/injectHighEntropyUserAgentHints.spec.js b/test/unit/specs/components/Context/injectHighEntropyUserAgentHints.spec.js deleted file mode 100644 index 66dde9e2b..000000000 --- a/test/unit/specs/components/Context/injectHighEntropyUserAgentHints.spec.js +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectHighEntropyUserAgentHints from "../../../../../src/components/Context/injectHighEntropyUserAgentHints.js"; - -describe("Context::injectHighEntropyUserAgentHints", () => { - const navigator = { - userAgentData: { - getHighEntropyValues() { - return Promise.resolve({ - architecture: "x86", - bitness: "64", - model: "alloy", - platformVersion: "1.2.3", - wow64: false, - invalidHint: true, - }); - }, - }, - }; - - it("works", (done) => { - const xdm = {}; - injectHighEntropyUserAgentHints(navigator)(xdm, console).then(() => { - expect(xdm).toEqual({ - environment: { - browserDetails: { - userAgentClientHints: { - architecture: "x86", - bitness: "64", - model: "alloy", - platformVersion: "1.2.3", - wow64: false, - }, - }, - }, - }); - done(); - }); - }); -}); diff --git a/test/unit/specs/components/Context/injectPlaceContext.spec.js b/test/unit/specs/components/Context/injectPlaceContext.spec.js deleted file mode 100644 index 931f8bc70..000000000 --- a/test/unit/specs/components/Context/injectPlaceContext.spec.js +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectPlaceContext from "../../../../../src/components/Context/injectPlaceContext.js"; - -describe("Context::injectPlaceContext", () => { - it("adds placeContext", () => { - const date = new Date("March 25, 2019 21:56:18"); - spyOn(date, "getTimezoneOffset").and.returnValue(7 * 60); - const xdm = {}; - injectPlaceContext(() => date)(xdm); - expect(xdm).toEqual({ - placeContext: { - localTime: "2019-03-25T21:56:18.000-07:00", - localTimezoneOffset: 7 * 60, - }, - }); - }); - - it("handles string values from timezoneOffset", () => { - const date = new Date("May 19, 2022 13:43:42"); - spyOn(date, "getTimezoneOffset").and.returnValue("55.1"); - const xdm = {}; - injectPlaceContext(() => date)(xdm); - expect(xdm).toEqual({ - placeContext: { - localTime: "2022-05-19T13:43:42.000-00:55", - localTimezoneOffset: 55, - }, - }); - }); - - it("handles NaN timezoneOffsets", () => { - const date = new Date("May 19, 2022 13:43:42"); - spyOn(date, "getTimezoneOffset").and.returnValue("foo"); - const xdm = {}; - injectPlaceContext(() => date)(xdm); - expect(xdm).toEqual({ - placeContext: { - localTime: "2022-05-19T13:43:42.000+00:00", - }, - }); - }); - - it("handles large timezoneOffsets 1", () => { - const date = new Date("October 28, 2022 11:57:42"); - spyOn(date, "getTimezoneOffset").and.returnValue(-5999); - const xdm = {}; - injectPlaceContext(() => date)(xdm); - expect(xdm).toEqual({ - placeContext: { - localTime: "2022-10-28T11:57:42.000+99:59", - localTimezoneOffset: -5999, - }, - }); - }); - - it("handles large timezoneOffsets 2", () => { - const date = new Date("October 28, 2022 11:57:42"); - spyOn(date, "getTimezoneOffset").and.returnValue(-6000); - const xdm = {}; - injectPlaceContext(() => date)(xdm); - expect(xdm).toEqual({ - placeContext: { - localTimezoneOffset: -6000, - }, - }); - }); -}); diff --git a/test/unit/specs/components/Context/injectTimestamp.spec.js b/test/unit/specs/components/Context/injectTimestamp.spec.js deleted file mode 100644 index be06b6ba7..000000000 --- a/test/unit/specs/components/Context/injectTimestamp.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectTimestamp from "../../../../../src/components/Context/injectTimestamp.js"; - -describe("Context::injectTimestamp", () => { - let dateProvider; - const date = new Date("November 25, 2019 10:09:42 UTC"); - - beforeEach(() => { - dateProvider = () => { - return date; - }; - }); - - it("adds timestamp", () => { - const xdm = {}; - injectTimestamp(dateProvider)(xdm); - expect(xdm).toEqual({ - timestamp: "2019-11-25T10:09:42.000Z", - }); - }); -}); diff --git a/test/unit/specs/components/Context/injectWeb.spec.js b/test/unit/specs/components/Context/injectWeb.spec.js deleted file mode 100644 index e4b42d506..000000000 --- a/test/unit/specs/components/Context/injectWeb.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import injectWeb from "../../../../../src/components/Context/injectWeb.js"; - -describe("Context::injectWeb", () => { - const window = { - location: { href: "http://mylocation.com" }, - document: { - referrer: "http://myreferrer.com", - }, - }; - - it("works", () => { - const xdm = {}; - injectWeb(window)(xdm); - expect(xdm).toEqual({ - web: { - webPageDetails: { - URL: "http://mylocation.com", - }, - webReferrer: { - URL: "http://myreferrer.com", - }, - }, - }); - }); -}); diff --git a/test/unit/specs/components/DataCollector/index.spec.js b/test/unit/specs/components/DataCollector/index.spec.js deleted file mode 100644 index 5f1ec5215..000000000 --- a/test/unit/specs/components/DataCollector/index.spec.js +++ /dev/null @@ -1,189 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createDataCollector from "../../../../../src/components/DataCollector/index.js"; -import { noop } from "../../../../../src/utils/index.js"; - -describe("Event Command", () => { - let event; - let eventManager; - let logger; - let sendEventCommand; - beforeEach(() => { - event = jasmine.createSpyObj("event", [ - "documentMayUnload", - "setUserData", - "setUserXdm", - "mergeXdm", - "mergeMeta", - "mergeConfigOverride", - ]); - logger = jasmine.createSpyObj("logger", ["warn"]); - - eventManager = { - createEvent() { - return event; - }, - sendEvent: jasmine - .createSpy() - .and.callFake((_event, { applyUserProvidedData = noop }) => { - applyUserProvidedData(); - return Promise.resolve("sendEventResult"); - }), - }; - - const dataCollector = createDataCollector({ - eventManager, - logger, - }); - sendEventCommand = dataCollector.commands.sendEvent; - }); - - it("sends event", () => { - const xdm = { a: "b" }; - const data = { c: "d" }; - const options = { - otherSetting: "foo", - type: "test", - xdm, - data, - documentUnloading: true, - }; - - return sendEventCommand.run(options).then((result) => { - expect(event.documentMayUnload).toHaveBeenCalled(); - expect(event.setUserXdm).toHaveBeenCalledWith(xdm); - expect(event.setUserData).toHaveBeenCalledWith(data); - expect(eventManager.sendEvent).toHaveBeenCalledWith(event, { - otherSetting: "foo", - }); - expect(result).toEqual("sendEventResult"); - }); - }); - - it("sends event with decisionScopes parameter when decisionScopes is not empty", () => { - const options = { - renderDecisions: true, - decisionScopes: ["Foo1"], - personalization: { - decisionScopes: ["Foo2"], - }, - }; - - return sendEventCommand.run(options).then((result) => { - expect(eventManager.sendEvent).toHaveBeenCalledWith(event, { - renderDecisions: true, - decisionScopes: ["Foo1"], - personalization: { - decisionScopes: ["Foo2"], - }, - }); - expect(result).toEqual("sendEventResult"); - }); - }); - - it("sends event with surfaces parameter when surfaces is not empty", () => { - const options = { - renderDecisions: true, - personalization: { - surfaces: ["Foo1", "Foo2"], - }, - }; - - return sendEventCommand.run(options).then((result) => { - expect(eventManager.sendEvent).toHaveBeenCalledWith(event, { - renderDecisions: true, - personalization: { - surfaces: ["Foo1", "Foo2"], - }, - }); - expect(result).toEqual("sendEventResult"); - }); - }); - - it("does not call documentMayUnload if documentUnloading is not defined", () => { - return sendEventCommand.run({}).then(() => { - expect(event.documentMayUnload).not.toHaveBeenCalled(); - }); - }); - - it("merges eventType", () => { - return sendEventCommand - .run({ - type: "mytype", - }) - .then(() => { - expect(event.mergeXdm).toHaveBeenCalledWith({ - eventType: "mytype", - }); - }); - }); - - it("merges eventMergeID", () => { - return sendEventCommand - .run({ - mergeId: "mymergeid", - }) - .then(() => { - expect(event.mergeXdm).toHaveBeenCalledWith({ - eventMergeId: "mymergeid", - }); - }); - }); - - it("merges datasetId into the override configuration", () => { - const datasetId = "mydatasetId"; - return sendEventCommand - .run({ - datasetId, - }) - .then(() => { - expect(eventManager.sendEvent).toHaveBeenCalledWith( - jasmine.any(Object), - { - edgeConfigOverrides: { - com_adobe_experience_platform: { - datasets: { - event: { datasetId }, - }, - }, - }, - }, - ); - expect(logger.warn).toHaveBeenCalled(); - }); - }); - - it("includes configuration if provided", () => { - return sendEventCommand - .run({ - renderDecisions: true, - edgeConfigOverrides: { - target: { - propertyToken: "hello", - }, - }, - }) - .then(() => { - expect(eventManager.sendEvent).toHaveBeenCalledWith( - jasmine.any(Object), - { - renderDecisions: true, - edgeConfigOverrides: { - target: { - propertyToken: "hello", - }, - }, - }, - ); - }); - }); -}); diff --git a/test/unit/specs/components/DataCollector/validateApplyResponse.spec.js b/test/unit/specs/components/DataCollector/validateApplyResponse.spec.js deleted file mode 100644 index 7a5112536..000000000 --- a/test/unit/specs/components/DataCollector/validateApplyResponse.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import validateApplyResponse from "../../../../../src/components/DataCollector/validateApplyResponse.js"; - -describe("DataCollector::validateApplyResponse", () => { - it("does not throw error for valid options", () => { - expect(() => { - validateApplyResponse({ - options: { - responseBody: { - handle: [ - { - type: "something:special", - payload: {}, - }, - ], - }, - }, - }); - }).not.toThrowError(); - }); - - it("throws error for invalid options", () => { - expect(() => { - validateApplyResponse({ - options: { - who_dis: true, - }, - }); - }).toThrowError(); - }); -}); diff --git a/test/unit/specs/components/DataCollector/validateUserEventOptions.spec.js b/test/unit/specs/components/DataCollector/validateUserEventOptions.spec.js deleted file mode 100644 index ddbc1a4f0..000000000 --- a/test/unit/specs/components/DataCollector/validateUserEventOptions.spec.js +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import validateUserEventOptions from "../../../../../src/components/DataCollector/validateUserEventOptions.js"; - -describe("DataCollector::validateUserEventOptions", () => { - it("throws error for invalid options", () => { - [ - { - xdm: { - eventType: "test", - identityMap: "123", - }, - }, - { - xdm: { - eventType: "test", - identityMap: { - namespace1: { id: "123", primary: true }, - }, - }, - }, - { - xdm: { - eventType: "test", - identityMap: { - namespace1: {}, - }, - }, - }, - { - xdm: { - eventType: "test", - identityMap: { - namespace1: [ - { - id: "123", - primary: true, - authenticatedState: "loggedIn", - }, - ], - }, - }, - }, - { - decisionScopes: ["item1", "item1"], - }, - ].forEach((options) => { - expect(() => { - validateUserEventOptions({ options }); - }).toThrowError(); - }); - }); -}); diff --git a/test/unit/specs/components/EventMerge/createComponent.spec.js b/test/unit/specs/components/EventMerge/createComponent.spec.js deleted file mode 100644 index 8aa85e220..000000000 --- a/test/unit/specs/components/EventMerge/createComponent.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createComponent from "../../../../../src/components/EventMerge/createComponent.js"; - -describe("EventMerge:createComponent", () => { - it("creates a component", () => { - const createEventMergeId = () => undefined; - expect(createComponent({ createEventMergeId })).toEqual({ - commands: { - createEventMergeId: { - run: createEventMergeId, - }, - }, - }); - }); -}); diff --git a/test/unit/specs/components/EventMerge/createEventMergeId.spec.js b/test/unit/specs/components/EventMerge/createEventMergeId.spec.js deleted file mode 100644 index f4a49ba98..000000000 --- a/test/unit/specs/components/EventMerge/createEventMergeId.spec.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createEventMergeId from "../../../../../src/components/EventMerge/createEventMergeId.js"; -import uuidV4Regex from "../../../constants/uuidV4Regex.js"; - -describe("EventMerge:createEventMergeId", () => { - it("returns a UUID v4-compliant Id", () => { - expect(uuidV4Regex.test(createEventMergeId().eventMergeId)).toBe(true); - }); - - it("doesn't return any other fields in the response", () => { - expect(Object.keys(createEventMergeId())).toEqual(["eventMergeId"]); - }); -}); diff --git a/test/unit/specs/components/Identity/addEcidToPayload.spec.js b/test/unit/specs/components/Identity/addEcidToPayload.spec.js deleted file mode 100644 index 469d0b51b..000000000 --- a/test/unit/specs/components/Identity/addEcidToPayload.spec.js +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import addEcidToPayload from "../../../../../src/components/Identity/addEcidToPayload.js"; - -describe("Identity:addEcidToPayload", () => { - it("adds ECID to payload", () => { - const payload = jasmine.createSpyObj("payload", ["addIdentity"]); - addEcidToPayload(payload, "user@adobe"); - expect(payload.addIdentity).toHaveBeenCalledWith("ECID", { - id: "user@adobe", - }); - }); -}); diff --git a/test/unit/specs/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.spec.js b/test/unit/specs/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.spec.js deleted file mode 100644 index f4a51ea9c..000000000 --- a/test/unit/specs/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.spec.js +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import appendIdentityToUrlOptionsValidator from "../../../../../../src/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.js"; - -describe("Identity::appendIdentityToUrlOptionsValidator", () => { - [ - undefined, - "myurl", - {}, - { url: "" }, - { url: "hello", other: "goodbye" }, - ].forEach((param) => { - it(`should throw an error when ${JSON.stringify(param)} is passed`, () => { - expect(() => { - appendIdentityToUrlOptionsValidator(param); - }).toThrowError(); - }); - }); - - it("should accept a url", () => { - expect( - appendIdentityToUrlOptionsValidator({ url: "http://google.com" }), - ).toEqual({ url: "http://google.com" }); - }); - - it("should accept override configuration", () => { - expect(() => { - appendIdentityToUrlOptionsValidator({ - url: "http://google.com", - edgeConfigOverrides: { identity: { idSyncContainerId: "123" } }, - }); - }).not.toThrowError(); - expect(() => { - appendIdentityToUrlOptionsValidator({ - url: "http://google.com", - edgeConfigOverrides: {}, - }); - }).not.toThrowError(); - }); -}); diff --git a/test/unit/specs/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.spec.js b/test/unit/specs/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.spec.js deleted file mode 100644 index e6a3ef35d..000000000 --- a/test/unit/specs/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.spec.js +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectAppendIdentityToUrl from "../../../../../../src/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.js"; - -describe("appendIdentityToUrl", () => { - const date = new Date(1234); - const orgId = "myorg@adobe"; - const appendIdentityToUrl = injectAppendIdentityToUrl({ - dateProvider: () => date, - orgId, - }); - const ecid = "1234"; - const qsp = "adobe_mc=TS%3D1%7CMCMID%3D1234%7CMCORGID%3Dmyorg%2540adobe"; - - const test = (original, expected) => { - it(`appends to "${original}"`, () => { - expect(appendIdentityToUrl(ecid, original)).toBe(expected); - }); - }; - - test("", `?${qsp}`); - test("/", `/?${qsp}`); - test("?", `?${qsp}`); - test("?a=b", `?a=b&${qsp}`); - test("#test", `?${qsp}#test`); - test("/example.html", `/example.html?${qsp}`); - test("https://adobe.com", `https://adobe.com?${qsp}`); - test("https://adobe.com?", `https://adobe.com?${qsp}`); - test("https://adobe.com?a=1", `https://adobe.com?a=1&${qsp}`); - test("https://adobe.com#myhash", `https://adobe.com?${qsp}#myhash`); - test("https://adobe.com?#myhash", `https://adobe.com?${qsp}#myhash`); - test("https://adobe.com?a=1#myhash", `https://adobe.com?a=1&${qsp}#myhash`); - test( - "https://adobe.com#myweird?hash", - `https://adobe.com?${qsp}#myweird?hash`, - ); -}); diff --git a/test/unit/specs/components/Identity/configValidators.spec.js b/test/unit/specs/components/Identity/configValidators.spec.js deleted file mode 100644 index fa55ff801..000000000 --- a/test/unit/specs/components/Identity/configValidators.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import configValidators from "../../../../../src/components/Identity/configValidators.js"; -import testConfigValidators from "../../../helpers/testConfigValidators.js"; - -describe("Identity config validators", () => { - testConfigValidators({ - configValidators, - validConfigurations: [ - {}, - { - thirdPartyCookiesEnabled: false, - }, - { - idMigrationEnabled: true, - }, - ], - invalidConfigurations: [ - { - thirdPartyCookiesEnabled: 42, - }, - { - idMigrationEnabled: () => {}, - }, - ], - defaultValues: { - thirdPartyCookiesEnabled: true, - idMigrationEnabled: true, - }, - }); -}); diff --git a/test/unit/specs/components/Identity/createComponent.spec.js b/test/unit/specs/components/Identity/createComponent.spec.js deleted file mode 100644 index b50a3195c..000000000 --- a/test/unit/specs/components/Identity/createComponent.spec.js +++ /dev/null @@ -1,438 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createComponent from "../../../../../src/components/Identity/createComponent.js"; -import { defer } from "../../../../../src/utils/index.js"; -import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; - -describe("Identity::createComponent", () => { - let addEcidQueryToPayload; - let addQueryStringIdentityToPayload; - let ensureSingleIdentity; - let setLegacyEcid; - let handleResponseForIdSyncs; - let getNamespacesFromResponse; - let getIdentity; - let consent; - let appendIdentityToUrl; - let logger; - let getIdentityOptionsValidator; - let awaitConsentDeferred; - let withConsentDeferred; - let getIdentityDeferred; - let response; - let component; - - beforeEach(() => { - ensureSingleIdentity = jasmine.createSpy("ensureSingleIdentity"); - addEcidQueryToPayload = jasmine.createSpy("addEcidQueryToPayload"); - addQueryStringIdentityToPayload = jasmine.createSpy( - "addQueryStringIdentityToPayload", - ); - setLegacyEcid = jasmine.createSpy("setLegacyEcid"); - handleResponseForIdSyncs = jasmine.createSpy("handleResponseForIdSyncs"); - getNamespacesFromResponse = jasmine.createSpy("getNamespacesFromResponse"); - getIdentityDeferred = defer(); - awaitConsentDeferred = defer(); - withConsentDeferred = defer(); - consent = jasmine.createSpyObj("consent", { - awaitConsent: awaitConsentDeferred.promise, - withConsent: withConsentDeferred.promise, - }); - appendIdentityToUrl = jasmine.createSpy("appendIdentityToUrl"); - logger = jasmine.createSpyObj("logger", ["warn"]); - getIdentity = jasmine - .createSpy("getIdentity") - .and.returnValue(getIdentityDeferred.promise); - getIdentityOptionsValidator = (options) => options; - component = createComponent({ - ensureSingleIdentity, - addEcidQueryToPayload, - addQueryStringIdentityToPayload, - setLegacyEcid, - handleResponseForIdSyncs, - getNamespacesFromResponse, - getIdentity, - consent, - appendIdentityToUrl, - logger, - getIdentityOptionsValidator, - }); - response = jasmine.createSpyObj("response", ["getEdge"]); - }); - - it("adds ECID query to event", () => { - const payload = { type: "payload" }; - const request = { - getPayload() { - return payload; - }, - }; - const onResponse = jasmine.createSpy("onResponse"); - component.lifecycle.onBeforeRequest({ request, onResponse }); - expect(addEcidQueryToPayload).toHaveBeenCalledWith(payload); - }); - - it("adds the query string identity to the payload", () => { - const payload = { type: "payload" }; - const request = { - getPayload() { - return payload; - }, - }; - component.lifecycle.onBeforeRequest({ request }); - expect(addQueryStringIdentityToPayload).toHaveBeenCalledOnceWith(payload); - }); - - it("ensures request has identity", () => { - const payload = { type: "payload" }; - const request = { - getPayload() { - return payload; - }, - }; - const onResponse = jasmine.createSpy("onResponse"); - const onRequestFailure = jasmine.createSpy("onRequestFailure"); - const ensureSingleIdentityPromise = Promise.resolve(); - ensureSingleIdentity.and.returnValue(ensureSingleIdentityPromise); - const result = component.lifecycle.onBeforeRequest({ - request, - onResponse, - onRequestFailure, - }); - expect(ensureSingleIdentity).toHaveBeenCalledWith({ - request, - onResponse, - onRequestFailure, - }); - expect(result).toBe(ensureSingleIdentityPromise); - }); - - it("does not create legacy identity cookie if response does not contain ECID", () => { - const idSyncsPromise = Promise.resolve(); - handleResponseForIdSyncs.and.returnValue(idSyncsPromise); - component.lifecycle.onResponse({ response }); - expect(getNamespacesFromResponse).toHaveBeenCalledWith(response); - expect(setLegacyEcid).not.toHaveBeenCalled(); - }); - - it("creates legacy identity cookie if response contains ECID", () => { - const idSyncsPromise = Promise.resolve(); - handleResponseForIdSyncs.and.returnValue(idSyncsPromise); - getNamespacesFromResponse.and.returnValue({ ECID: "user@adobe" }); - component.lifecycle.onResponse({ response }); - expect(getNamespacesFromResponse).toHaveBeenCalledWith(response); - expect(setLegacyEcid).toHaveBeenCalledWith("user@adobe"); - - component.lifecycle.onResponse({ response }); - expect(getNamespacesFromResponse).toHaveBeenCalledTimes(2); - expect(setLegacyEcid).toHaveBeenCalledTimes(1); - }); - - it("handles ID syncs", () => { - const idSyncsPromise = Promise.resolve(); - handleResponseForIdSyncs.and.returnValue(idSyncsPromise); - const result = component.lifecycle.onResponse({ response }); - expect(handleResponseForIdSyncs).toHaveBeenCalledWith(response); - return expectAsync(result).toBeResolvedTo(undefined); - }); - - it("getIdentity command should make a request when ecid is not available", () => { - const idSyncsPromise = Promise.resolve(); - handleResponseForIdSyncs.and.returnValue(idSyncsPromise); - const onResolved = jasmine.createSpy("onResolved"); - const onResolved2 = jasmine.createSpy("onResolved2"); - response.getEdge.and.returnValue({ regionId: 42 }); - component.commands.getIdentity - .run({ namespaces: ["ECID"] }) - .then(onResolved); - - return flushPromiseChains() - .then(() => { - expect(getIdentity).not.toHaveBeenCalled(); - expect(onResolved).not.toHaveBeenCalled(); - awaitConsentDeferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(getIdentity).toHaveBeenCalled(); - // ECID and CORE are requested regardless of the namespaces passed in. - getNamespacesFromResponse.and.returnValue({ - ECID: "user@adobe", - CORE: "mycoreid", - }); - component.lifecycle.onResponse({ response }); - getIdentityDeferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(onResolved).toHaveBeenCalledWith({ - identity: { - ECID: "user@adobe", - }, - edge: { - regionId: 42, - }, - }); - getNamespacesFromResponse.and.returnValue({ CORE: "mycoreid" }); - component.commands.getIdentity - .run({ namespaces: ["CORE"] }) - .then(onResolved2); - return flushPromiseChains(); - }) - .then(() => { - expect(getIdentity).toHaveBeenCalledTimes(1); - return flushPromiseChains(); - }) - .then(() => { - expect(onResolved2).toHaveBeenCalledWith({ - identity: { - CORE: "mycoreid", - }, - edge: { - regionId: 42, - }, - }); - }); - }); - - it("getIdentity command should not make a request when ecid is available", () => { - const idSyncsPromise = Promise.resolve(); - handleResponseForIdSyncs.and.returnValue(idSyncsPromise); - getNamespacesFromResponse.and.returnValue({ ECID: "user@adobe" }); - response.getEdge.and.returnValue({ regionId: 7 }); - component.lifecycle.onResponse({ response }); - const onResolved = jasmine.createSpy("onResolved"); - component.commands.getIdentity - .run({ namespaces: ["ECID"] }) - .then(onResolved); - return flushPromiseChains() - .then(() => { - expect(getIdentity).not.toHaveBeenCalled(); - expect(onResolved).not.toHaveBeenCalled(); - awaitConsentDeferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(getIdentity).not.toHaveBeenCalled(); - expect(onResolved).toHaveBeenCalledWith({ - identity: { - ECID: "user@adobe", - }, - edge: { - regionId: 7, - }, - }); - }); - }); - - it("getIdentity command should not make a request when CORE is available", () => { - const idSyncsPromise = Promise.resolve(); - handleResponseForIdSyncs.and.returnValue(idSyncsPromise); - getNamespacesFromResponse.and.returnValue({ - ECID: "user@adobe", - CORE: "mycoreid", - }); - response.getEdge.and.returnValue({ regionId: 7 }); - component.lifecycle.onResponse({ response }); - const onResolved = jasmine.createSpy("onResolved"); - component.commands.getIdentity - .run({ namespaces: ["CORE"] }) - .then(onResolved); - return flushPromiseChains() - .then(() => { - expect(getIdentity).not.toHaveBeenCalled(); - expect(onResolved).not.toHaveBeenCalled(); - awaitConsentDeferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(getIdentity).not.toHaveBeenCalled(); - expect(onResolved).toHaveBeenCalledWith({ - identity: { - CORE: "mycoreid", - }, - edge: { - regionId: 7, - }, - }); - }); - }); - - it("getIdentity command is called with configuration overrides, when provided", () => { - const idSyncsPromise = Promise.resolve(); - handleResponseForIdSyncs.and.returnValue(idSyncsPromise); - const onResolved = jasmine.createSpy("onResolved"); - response.getEdge.and.returnValue({ regionId: 42 }); - const getIdentityOptions = { - namespaces: ["ECID"], - edgeConfigOverrides: { - com_adobe_identity: { - idSyncContainerId: "123", - }, - }, - }; - component.commands.getIdentity.run(getIdentityOptions).then(onResolved); - - return flushPromiseChains() - .then(() => { - expect(getIdentity).not.toHaveBeenCalled(); - expect(onResolved).not.toHaveBeenCalled(); - awaitConsentDeferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(getIdentity).toHaveBeenCalledWith(getIdentityOptions); - getNamespacesFromResponse.and.returnValue({ ECID: "user@adobe" }); - component.lifecycle.onResponse({ response }); - getIdentityDeferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(onResolved).toHaveBeenCalledWith({ - identity: { - ECID: "user@adobe", - }, - edge: { - regionId: 42, - }, - }); - }); - }); - - it("appendIdentityToUrl should return the unmodified url when consent is not given.", () => { - const commandPromise = component.commands.appendIdentityToUrl.run({ - url: "myurl", - }); - withConsentDeferred.reject(new Error("My consent error.")); - return expectAsync(commandPromise) - .toBeResolvedTo({ url: "myurl" }) - .then(() => { - expect(logger.warn).toHaveBeenCalledWith( - "Unable to append identity to url. My consent error.", - ); - expect(getIdentity).not.toHaveBeenCalled(); - expect(appendIdentityToUrl).not.toHaveBeenCalled(); - }); - }); - - it("appendIdentityToUrl should return the unmodified url when getIdentity returns an error.", () => { - const commandPromise = component.commands.appendIdentityToUrl.run({ - url: "myurl", - }); - withConsentDeferred.resolve(); - getIdentityDeferred.reject(new Error("My getIdentity error.")); - return expectAsync(commandPromise) - .toBeResolvedTo({ url: "myurl" }) - .then(() => { - expect(logger.warn).toHaveBeenCalledOnceWith( - "Unable to append identity to url. My getIdentity error.", - ); - expect(appendIdentityToUrl).not.toHaveBeenCalled(); - }); - }); - - it("appendIdentityToUrl should getIdentity when there isn't one.", () => { - const idSyncsPromise = Promise.resolve(); - handleResponseForIdSyncs.and.returnValue(idSyncsPromise); - appendIdentityToUrl.and.returnValue("modifiedUrl"); - const onResolved = jasmine.createSpy("onResolved"); - response.getEdge.and.returnValue({ regionId: 42 }); - component.commands.appendIdentityToUrl - .run({ namespaces: ["ECID"], url: "myurl" }) - .then(onResolved); - - return flushPromiseChains() - .then(() => { - expect(getIdentity).not.toHaveBeenCalled(); - expect(onResolved).not.toHaveBeenCalled(); - withConsentDeferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(getIdentity).toHaveBeenCalledWith( - jasmine.objectContaining({ namespaces: ["ECID"] }), - ); - getNamespacesFromResponse.and.returnValue({ ECID: "user@adobe" }); - component.lifecycle.onResponse({ response }); - getIdentityDeferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(logger.warn).not.toHaveBeenCalled(); - expect(appendIdentityToUrl).toHaveBeenCalledOnceWith( - "user@adobe", - "myurl", - ); - }); - }); - - it("appendIdentityToUrl should append the identity to a url when there is already an identity", () => { - // set the ECID - const idSyncsPromise = Promise.resolve(); - handleResponseForIdSyncs.and.returnValue(idSyncsPromise); - getNamespacesFromResponse.and.returnValue({ ECID: "user@adobe" }); - response.getEdge.and.returnValue({ regionId: 7 }); - component.lifecycle.onResponse({ response }); - - appendIdentityToUrl.and.returnValue("modifiedUrl"); - const commandPromise = component.commands.appendIdentityToUrl.run({ - url: "myurl", - }); - withConsentDeferred.resolve(); - - return expectAsync(commandPromise).toBeResolvedTo({ url: "modifiedUrl" }); - }); - - it("appendIdentityToUrl should call getIdentity with configuration overrides, if provided", () => { - const idSyncsPromise = Promise.resolve(); - handleResponseForIdSyncs.and.returnValue(idSyncsPromise); - appendIdentityToUrl.and.returnValue("modifiedUrl"); - const onResolved = jasmine.createSpy("onResolved"); - response.getEdge.and.returnValue({ regionId: 42 }); - const edgeConfigOverrides = { - com_adobe_identity: { - idSyncContainerId: "123", - }, - }; - component.commands.appendIdentityToUrl - .run({ namespaces: ["ECID"], url: "myurl", edgeConfigOverrides }) - .then(onResolved); - - return flushPromiseChains() - .then(() => { - expect(getIdentity).not.toHaveBeenCalled(); - expect(onResolved).not.toHaveBeenCalled(); - withConsentDeferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(getIdentity).toHaveBeenCalledWith( - jasmine.objectContaining({ - namespaces: ["ECID"], - edgeConfigOverrides, - }), - ); - getNamespacesFromResponse.and.returnValue({ ECID: "user@adobe" }); - component.lifecycle.onResponse({ response }); - getIdentityDeferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(logger.warn).not.toHaveBeenCalled(); - expect(appendIdentityToUrl).toHaveBeenCalledOnceWith( - "user@adobe", - "myurl", - ); - }); - }); -}); diff --git a/test/unit/specs/components/Identity/createLegacyIdentity.spec.js b/test/unit/specs/components/Identity/createLegacyIdentity.spec.js deleted file mode 100644 index 97fd2398c..000000000 --- a/test/unit/specs/components/Identity/createLegacyIdentity.spec.js +++ /dev/null @@ -1,147 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createLegacyIdentity from "../../../../../src/components/Identity/createLegacyIdentity.js"; - -describe("Identity::createLegacyIdentity", () => { - let idMigrationEnabled; - let orgId; - let getEcidFromVisitor; - let apexDomain; - let isPageSsl; - let cookieJar; - - let legacyIdentity; - - beforeEach(() => { - idMigrationEnabled = true; - orgId = "TEST_ORG"; - getEcidFromVisitor = jasmine - .createSpy("getEcidFromVisitor") - .and.returnValue(Promise.resolve()); - apexDomain = "mydomain"; - isPageSsl = true; - cookieJar = jasmine.createSpyObj("cookieJar", ["get", "set"]); - - legacyIdentity = undefined; - }); - - const build = () => { - legacyIdentity = createLegacyIdentity({ - config: { - idMigrationEnabled, - orgId, - }, - getEcidFromVisitor, - apexDomain, - isPageSsl, - cookieJar, - }); - }; - - describe("getEcid", () => { - it("should return a promise resolved with undefined if ID migration disabled", async () => { - idMigrationEnabled = false; - build(); - const result = await legacyIdentity.getEcid(); - expect(result).toBeUndefined(); - expect(cookieJar.get).not.toHaveBeenCalled(); - }); - - it("should return promise resolved with undefined if no AMCV cookie or s_ecid cookie is present", async () => { - build(); - const result = await legacyIdentity.getEcid(); - expect(result).toBeUndefined(); - }); - - [ - "random|string|MCMID|1234|random|random", - "MCMID|1234|random|random", - "random|random|MCMID|1234", - "MCMID|1234", - ].forEach((cookieValue) => { - it(`should return promise resolved with ECID if AMCV cookie is ${cookieValue}`, async () => { - cookieJar.get.and.callFake((cookieName) => - cookieName === "AMCV_TEST_ORG" ? cookieValue : undefined, - ); - build(); - expect(await legacyIdentity.getEcid()).toEqual("1234"); - }); - - it(`should return promise resolved with ECID if s_ecid cookie is ${cookieValue}`, async () => { - cookieJar.get.and.callFake((cookieName) => - cookieName === "s_ecid" ? cookieValue : undefined, - ); - build(); - expect(await legacyIdentity.getEcid()).toEqual("1234"); - }); - }); - - it("should return promise resolved with undefined cookie does not contain MCMID", async () => { - const cookieValue = "version|0.0.4"; - cookieJar.get.and.returnValue(cookieValue); - build(); - expect(await legacyIdentity.getEcid()).toBeUndefined(); - }); - - it("should request ECID from visitor ID Service if legacy ECID cookies are missing", async () => { - getEcidFromVisitor.and.returnValue(Promise.resolve("visitor_ecid")); - build(); - expect(await legacyIdentity.getEcid()).toEqual("visitor_ecid"); - }); - }); - - describe("setEcid", () => { - it("should not write AMCV cookie if ID migration disabled", () => { - idMigrationEnabled = false; - build(); - legacyIdentity.setEcid("1234"); - expect(cookieJar.set).not.toHaveBeenCalled(); - }); - - it("writes a secure AMCV cookie", () => { - build(); - legacyIdentity.setEcid("1234"); - expect(cookieJar.set).toHaveBeenCalledOnceWith( - "AMCV_TEST_ORG", - "MCMID|1234", - { - domain: "mydomain", - expires: 390, - sameSite: "none", - secure: true, - }, - ); - }); - - it("writes an insecure AMCV cookie", () => { - isPageSsl = false; - build(); - legacyIdentity.setEcid("1234"); - expect(cookieJar.set).toHaveBeenCalledOnceWith( - "AMCV_TEST_ORG", - "MCMID|1234", - { - domain: "mydomain", - expires: 390, - }, - ); - }); - - it("should not write AMCV cookie if already present", () => { - cookieJar.get.and.returnValue("MCMID|1234|otherstuff"); - build(); - legacyIdentity.setEcid("1234"); - expect(cookieJar.set).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/test/unit/specs/components/Identity/getIdentity/createGetIdentity.spec.js b/test/unit/specs/components/Identity/getIdentity/createGetIdentity.spec.js deleted file mode 100644 index b56b9027a..000000000 --- a/test/unit/specs/components/Identity/getIdentity/createGetIdentity.spec.js +++ /dev/null @@ -1,187 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createGetIdentity from "../../../../../../src/components/Identity/getIdentity/createGetIdentity.js"; - -describe("Identity::createGetIdentity", () => { - let sendEdgeNetworkRequest; - let createIdentityRequestPayload; - let createIdentityRequest; - let requestPayload; - let request; - - beforeEach(() => { - sendEdgeNetworkRequest = jasmine.createSpy("sendEdgeNetworkRequest"); - requestPayload = jasmine.createSpyObj( - "requestPayload", - ["mergeConfigOverride"], - { - type: "payload", - }, - ); - createIdentityRequestPayload = jasmine - .createSpy("createIdentityRequestPayload") - .and.returnValue(requestPayload); - request = { - getPayload() { - return requestPayload; - }, - }; - createIdentityRequest = jasmine - .createSpy("createIdentityRequest") - .and.returnValue(request); - }); - - it("should return a function which calls sendEdgeNetworkRequest", () => { - const getIdentity = createGetIdentity({ - sendEdgeNetworkRequest, - createIdentityRequestPayload, - createIdentityRequest, - }); - getIdentity(); - expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ - request, - }); - }); - - it("each getIdentity call should create new payloads and requests", () => { - const payload1 = jasmine.createSpyObj( - "requestPayload", - ["mergeConfigOverride"], - { - type: "payload1", - }, - ); - const payload2 = jasmine.createSpyObj( - "requestPayload", - ["mergeConfigOverride"], - { - type: "payload2", - }, - ); - const request1 = { type: "request1" }; - const request2 = { type: "request2" }; - createIdentityRequestPayload.and.returnValues(payload1, payload2); - createIdentityRequest.and.returnValues(request1, request2); - const getIdentity = createGetIdentity({ - sendEdgeNetworkRequest, - createIdentityRequestPayload, - createIdentityRequest, - }); - getIdentity({ namespaces: ["namespace1", "namespace2"] }); - expect(createIdentityRequestPayload).toHaveBeenCalledWith([ - "namespace1", - "namespace2", - ]); - expect(createIdentityRequest).toHaveBeenCalledWith({ - payload: payload1, - }); - expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ - request: request1, - }); - getIdentity(); - expect(createIdentityRequest).toHaveBeenCalledWith({ - payload: payload2, - }); - expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ - request: request2, - }); - }); - - it("send override configuration, when provided", () => { - const request1 = { type: "request1" }; - createIdentityRequestPayload.and.returnValues(requestPayload); - createIdentityRequest.and.returnValues(request1); - const getIdentity = createGetIdentity({ - sendEdgeNetworkRequest, - createIdentityRequestPayload, - createIdentityRequest, - }); - const configuration = { - com_adobe_identity: { - idSyncContainerId: "123", - }, - }; - getIdentity({ - namespaces: ["namespace1"], - edgeConfigOverrides: configuration, - }); - expect(createIdentityRequestPayload).toHaveBeenCalledWith(["namespace1"]); - expect(createIdentityRequest).toHaveBeenCalledWith({ - payload: requestPayload, - }); - expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ - request: request1, - }); - expect(requestPayload.mergeConfigOverride).toHaveBeenCalledWith({ - com_adobe_identity: { - idSyncContainerId: configuration.com_adobe_identity.idSyncContainerId, - }, - }); - }); - - it("send global override configuration, when provided", () => { - const request1 = { type: "request1" }; - createIdentityRequestPayload.and.returnValues(requestPayload); - createIdentityRequest.and.returnValues(request1); - const configuration = { - com_adobe_identity: { - idSyncContainerId: "123", - }, - }; - const getIdentity = createGetIdentity({ - sendEdgeNetworkRequest, - createIdentityRequestPayload, - createIdentityRequest, - globalConfigOverrides: configuration, - }); - getIdentity({ - namespaces: ["namespace1"], - }); - expect(createIdentityRequestPayload).toHaveBeenCalledWith(["namespace1"]); - expect(createIdentityRequest).toHaveBeenCalledWith({ - payload: requestPayload, - }); - expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ - request: request1, - }); - expect(requestPayload.mergeConfigOverride).toHaveBeenCalledWith({ - com_adobe_identity: { - idSyncContainerId: configuration.com_adobe_identity.idSyncContainerId, - }, - }); - }); - - it("send edge config id override, when provided", () => { - const request1 = { type: "request1" }; - createIdentityRequestPayload.and.returnValues(requestPayload); - createIdentityRequest.and.returnValues(request1); - const getIdentity = createGetIdentity({ - sendEdgeNetworkRequest, - createIdentityRequestPayload, - createIdentityRequest, - }); - getIdentity({ - namespaces: ["namespace1"], - edgeConfigOverrides: { - datastreamId: "123", - }, - }); - expect(createIdentityRequestPayload).toHaveBeenCalledWith(["namespace1"]); - expect(createIdentityRequest).toHaveBeenCalledWith({ - payload: requestPayload, - datastreamIdOverride: "123", - }); - expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ - request: request1, - }); - }); -}); diff --git a/test/unit/specs/components/Identity/getIdentity/createGetIdentityOptionsValidator.spec.js b/test/unit/specs/components/Identity/getIdentity/createGetIdentityOptionsValidator.spec.js deleted file mode 100644 index 4407814a5..000000000 --- a/test/unit/specs/components/Identity/getIdentity/createGetIdentityOptionsValidator.spec.js +++ /dev/null @@ -1,104 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createGetIdentityOptionsValidator from "../../../../../../src/components/Identity/getIdentity/createGetIdentityOptionsValidator.js"; - -describe("Identity::getIdentityOptionsValidator", () => { - const thirdPartyValidator = createGetIdentityOptionsValidator({ - thirdPartyCookiesEnabled: true, - }); - const firstPartyValidator = createGetIdentityOptionsValidator({ - thirdPartyCookiesEnabled: false, - }); - - it("should throw an error when invalid options are passed", () => { - expect(() => { - thirdPartyValidator({ key: ["item1", "item2"] }); - }).toThrow(new Error("'key': Unknown field.")); - - expect(() => { - thirdPartyValidator({ - key1: ["item1", "item2"], - key2: ["item1", "item2"], - }); - }).toThrow(new Error("'key1': Unknown field.\n'key2': Unknown field.")); - - expect(() => { - thirdPartyValidator({ namespaces: [] }); - }).toThrow( - new Error("'namespaces': Expected a non-empty array, but got []."), - ); - - expect(() => { - thirdPartyValidator({ namespaces: ["ECID", "ECID"] }); - }).toThrow( - new Error( - `'namespaces': Expected array values to be unique, but got ["ECID","ECID"].`, - ), - ); - - expect(() => { - thirdPartyValidator({ namespaces: ["ACD"] }); - }).toThrow( - new Error( - `'namespaces[0]': Expected one of these values: ["ECID","CORE"], but got "ACD".`, - ), - ); - }); - - it("should return valid options when no options are passed", () => { - expect(() => { - thirdPartyValidator(); - }).not.toThrow(); - const validatedIdentityOptions = thirdPartyValidator(); - expect(validatedIdentityOptions).toEqual({ namespaces: ["ECID"] }); - }); - - it("should not throw when supported namespace options are passed", () => { - const ECID = "ECID"; - expect(() => { - thirdPartyValidator({ namespaces: [ECID] }); - }).not.toThrow(); - }); - - it("should return valid options when configuration is passed", () => { - expect(() => { - thirdPartyValidator({ - edgeConfigOverrides: { identity: { idSyncContainerId: "123" } }, - }); - }).not.toThrow(); - }); - - it("should return valid options when an empty configuration is passed", () => { - expect(() => { - thirdPartyValidator({ - edgeConfigOverrides: {}, - }); - }).not.toThrow(); - }); - - it("should throw an error when CORE is passed with third party cookies disabled", () => { - expect(() => { - firstPartyValidator({ namespaces: ["CORE"] }); - }).toThrow( - new Error( - `namespaces: The CORE namespace cannot be requested when third-party cookies are disabled.`, - ), - ); - }); - - it("should not throw when CORE is passed with third party cookies enabled", () => { - expect(() => { - thirdPartyValidator({ namespaces: ["CORE"] }); - }).not.toThrow(); - }); -}); diff --git a/test/unit/specs/components/Identity/getIdentity/createIdentityRequest.spec.js b/test/unit/specs/components/Identity/getIdentity/createIdentityRequest.spec.js deleted file mode 100644 index f921f3bb1..000000000 --- a/test/unit/specs/components/Identity/getIdentity/createIdentityRequest.spec.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import describeRequest from "../../../../helpers/describeRequest.js"; -import createIdentityRequest from "../../../../../../src/components/Identity/getIdentity/createIdentityRequest.js"; - -describe("createIdentityRequest", () => { - describeRequest(createIdentityRequest); - - it("provides the appropriate action", () => { - const payload = {}; - const request = createIdentityRequest({ payload }); - expect(request.getAction()).toBe("identity/acquire"); - }); - - it("never uses sendBeacon", () => { - const payload = {}; - const request = createIdentityRequest({ payload }); - expect(request.getUseSendBeacon()).toBeFalse(); - }); - - it("passes the datastreamIdOverride to the request", () => { - const payload = {}; - const datastreamIdOverride = "my-edge-config-id-override"; - const request = createIdentityRequest({ - payload, - datastreamIdOverride, - }); - expect(request.getDatastreamIdOverride()).toBe(datastreamIdOverride); - }); -}); diff --git a/test/unit/specs/components/Identity/getIdentity/createIdentityRequestPayload.spec.js b/test/unit/specs/components/Identity/getIdentity/createIdentityRequestPayload.spec.js deleted file mode 100644 index 5271ef83e..000000000 --- a/test/unit/specs/components/Identity/getIdentity/createIdentityRequestPayload.spec.js +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createIdentityPayload from "../../../../../../src/components/Identity/getIdentity/createIdentityRequestPayload.js"; -import describeRequestPayload from "../../../../helpers/describeRequestPayload.js"; - -describe("createIdentityRequestPayload", () => { - describeRequestPayload(() => { - return createIdentityPayload(["NS1", "NS2", "NS3"]); - }); - - it("adds identities", () => { - const payload = createIdentityPayload(["NS1", "NS2", "NS3"]); - payload.addIdentity("IDNS", { - id: "ABC123", - }); - payload.addIdentity("IDNS", { - id: "DEF456", - }); - expect(payload.toJSON()).toEqual({ - xdm: { - identityMap: { - IDNS: [ - { - id: "ABC123", - }, - { - id: "DEF456", - }, - ], - }, - }, - query: { identity: { fetch: ["NS1", "NS2", "NS3"] } }, - }); - }); -}); diff --git a/test/unit/specs/components/Identity/getNamespacesFromResponse.spec.js b/test/unit/specs/components/Identity/getNamespacesFromResponse.spec.js deleted file mode 100644 index b0ba97354..000000000 --- a/test/unit/specs/components/Identity/getNamespacesFromResponse.spec.js +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getNamespacesFromResponse from "../../../../../src/components/Identity/getNamespacesFromResponse.js"; - -describe("Identity::getEcidFromResponse", () => { - it("does not return ECID if ECID does not exist in response", () => { - const response = jasmine.createSpyObj("response", { - getPayloadsByType: [ - { - namespace: { - code: "other", - }, - id: "user123", - }, - ], - }); - - expect(getNamespacesFromResponse(response)).toEqual({ other: "user123" }); - expect(response.getPayloadsByType).toHaveBeenCalledWith("identity:result"); - }); - - it("returns ECID if ECID exists in response", () => { - const response = jasmine.createSpyObj("response", { - getPayloadsByType: [ - { - namespace: { - code: "other", - }, - id: "user123", - }, - { - namespace: { - code: "ECID", - }, - id: "user@adobe", - }, - ], - }); - - expect(getNamespacesFromResponse(response)).toEqual({ - other: "user123", - ECID: "user@adobe", - }); - expect(response.getPayloadsByType).toHaveBeenCalledWith("identity:result"); - }); -}); diff --git a/test/unit/specs/components/Identity/injectAddEcidQueryToPayload.spec.js b/test/unit/specs/components/Identity/injectAddEcidQueryToPayload.spec.js deleted file mode 100644 index 4daaa8358..000000000 --- a/test/unit/specs/components/Identity/injectAddEcidQueryToPayload.spec.js +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectAddEcidQueryToPayload from "../../../../../src/components/Identity/injectAddEcidQueryToPayload.js"; - -describe("Identity::addEcidQueryToPayload", () => { - it("adds an ECID & CORE query to the event when third party cookies are enabled on Chrome", () => { - const addEcidQueryToPayload = injectAddEcidQueryToPayload({ - thirdPartyCookiesEnabled: true, - areThirdPartyCookiesSupportedByDefault: () => true, - }); - const payload = jasmine.createSpyObj("payload", ["mergeQuery"]); - addEcidQueryToPayload(payload); - expect(payload.mergeQuery).toHaveBeenCalledWith({ - identity: { - fetch: ["ECID", "CORE"], - }, - }); - }); - it("adds only ECID query to the event when third party cookies are enabled on Safari", () => { - const addEcidQueryToPayload = injectAddEcidQueryToPayload({ - thirdPartyCookiesEnabled: true, - areThirdPartyCookiesSupportedByDefault: () => false, - }); - const payload = jasmine.createSpyObj("payload", ["mergeQuery"]); - addEcidQueryToPayload(payload); - expect(payload.mergeQuery).toHaveBeenCalledWith({ - identity: { - fetch: ["ECID"], - }, - }); - }); - it("adds an ECID query to the event when third party cookies are disabled on Chrome", () => { - const addEcidQueryToPayload = injectAddEcidQueryToPayload({ - thirdPartyCookiesEnabled: false, - areThirdPartyCookiesSupportedByDefault: () => true, - }); - const payload = jasmine.createSpyObj("payload", ["mergeQuery"]); - addEcidQueryToPayload(payload); - expect(payload.mergeQuery).toHaveBeenCalledWith({ - identity: { - fetch: ["ECID"], - }, - }); - }); -}); diff --git a/test/unit/specs/components/Identity/injectAddLegacyEcidToPayload.spec.js b/test/unit/specs/components/Identity/injectAddLegacyEcidToPayload.spec.js deleted file mode 100644 index e59df27e8..000000000 --- a/test/unit/specs/components/Identity/injectAddLegacyEcidToPayload.spec.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectAddLegacyEcidToPayload from "../../../../../src/components/Identity/injectAddLegacyEcidToPayload.js"; - -describe("Identity::injectAddLegacyEcidToPayload", () => { - let getLegacyEcid; - let addEcidToPayload; - let payload; - let addLegacyEcidToPayload; - - beforeEach(() => { - getLegacyEcid = jasmine - .createSpy("getEcidFromLegacy") - .and.returnValue(Promise.resolve("legacy@adobe")); - addEcidToPayload = jasmine.createSpy("addEcidToPayload"); - addLegacyEcidToPayload = injectAddLegacyEcidToPayload({ - getLegacyEcid, - addEcidToPayload, - }); - payload = jasmine.createSpyObj("payload", ["hasIdentity"]); - }); - - it("does not add legacy ECID to payload if legacy ECID does not exist", () => { - payload.hasIdentity.and.returnValue(false); - getLegacyEcid.and.returnValue(Promise.resolve()); - return addLegacyEcidToPayload(payload).then(() => { - expect(addEcidToPayload).not.toHaveBeenCalled(); - }); - }); - - it("adds legacy ECID to payload if legacy ECID exists", () => { - payload.hasIdentity.and.returnValue(false); - return addLegacyEcidToPayload(payload).then(() => { - expect(addEcidToPayload).toHaveBeenCalledWith(payload, "legacy@adobe"); - }); - }); - - it("does not add legacy ECID to payload if the payload already has an ecid", async () => { - payload.hasIdentity.and.returnValue(true); - const response = await addLegacyEcidToPayload(payload); - expect(getLegacyEcid).not.toHaveBeenCalled(); - expect(response).toBeUndefined(); - }); -}); diff --git a/test/unit/specs/components/Identity/injectAddQueryStringIdentityToPayload.spec.js b/test/unit/specs/components/Identity/injectAddQueryStringIdentityToPayload.spec.js deleted file mode 100644 index fa4e4ccd9..000000000 --- a/test/unit/specs/components/Identity/injectAddQueryStringIdentityToPayload.spec.js +++ /dev/null @@ -1,190 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectAddQueryStringIdentityToPayload from "../../../../../src/components/Identity/injectAddQueryStringIdentityToPayload.js"; -import createDataCollectionRequestPayload from "../../../../../src/utils/request/createDataCollectionRequestPayload.js"; -import createIdentityRequestPayload from "../../../../../src/components/Identity/getIdentity/createIdentityRequestPayload.js"; -import createConsentRequestPayload from "../../../../../src/components/Consent/createConsentRequestPayload.js"; - -describe("Identity::injectAddQueryStringIdentityToPayload", () => { - let locationSearch; - let dateProvider; - let orgId; - let logger; - let date; - let payload; - - beforeEach(() => { - dateProvider = () => date; - locationSearch = - "?foo=bar&adobe_mc=TS%3D1641432103%7CMCMID%3D77094828402023918047117570965393734545%7CMCORGID%3DFAF554945B90342F0A495E2C%40AdobeOrg&a=b"; - date = new Date(1641432103 * 1000); - orgId = "FAF554945B90342F0A495E2C@AdobeOrg"; - logger = jasmine.createSpyObj("logger", ["info", "warn"]); - }); - - const run = () => { - injectAddQueryStringIdentityToPayload({ - locationSearch, - dateProvider, - orgId, - logger, - })(payload); - }; - - [ - [ - "DataCollection", - createDataCollectionRequestPayload, - (p) => p.xdm.identityMap, - ], - ["Identity", createIdentityRequestPayload, (p) => p.xdm.identityMap], - ["Consent", createConsentRequestPayload, (p) => p.identityMap], - ].forEach(([type, createPayload, getIdentityMap]) => { - describe(`with ${type} payload`, () => { - beforeEach(() => { - payload = createPayload(); - }); - - it("adds the identity", () => { - run(); - expect(getIdentityMap(payload.toJSON())).toEqual({ - ECID: [ - { - id: "77094828402023918047117570965393734545", - }, - ], - }); - }); - - it("doesn't overwrite an existing identity in the identityMap", () => { - payload.addIdentity("ECID", { id: "1234" }); - run(); - expect(getIdentityMap(payload.toJSON())).toEqual({ - ECID: [ - { - id: "1234", - }, - ], - }); - }); - }); - }); - - describe("with mock payload", () => { - beforeEach(() => { - payload = jasmine.createSpyObj("payload", ["addIdentity", "hasIdentity"]); - payload.hasIdentity.and.returnValue(false); - }); - - it("doesn't do anything when there is no query string", () => { - locationSearch = ""; - run(); - expect(payload.addIdentity).not.toHaveBeenCalled(); - }); - - it("doesn't do anything when there is no TS parameter", () => { - locationSearch = `?adobe_mc=${encodeURIComponent( - "MCMID=myid|MCORG=myorg", - )}`; - run(); - expect(payload.addIdentity).not.toHaveBeenCalled(); - }); - - it("doesn't do anything when there is no MCMID parameter", () => { - locationSearch = `?adobe_mc=${encodeURIComponent("TS=1000|MCORG=myorg")}`; - run(); - expect(payload.addIdentity).not.toHaveBeenCalled(); - }); - - it("doesn't do anything when there is no MCORG parameter", () => { - locationSearch = `?adobe_mc=${encodeURIComponent("TS=1000|MCMID=myid")}`; - run(); - expect(payload.addIdentity).not.toHaveBeenCalled(); - }); - - it("doesn't do anything with an expired link", () => { - date = new Date((1641432103 + 301) * 1000); - run(); - expect(payload.addIdentity).not.toHaveBeenCalled(); - }); - - it("adds the identity for an exactly 5 minute old link", () => { - date = new Date((1641432103 + 300) * 1000); - run(); - expect(payload.addIdentity).toHaveBeenCalled(); - }); - - it("doesn't do anything when the orgs don't match", () => { - orgId = "myotherorg"; - run(); - expect(payload.addIdentity).not.toHaveBeenCalled(); - }); - - [ - "adobe_mc=", - "adobe_mc=a", - "adobe_mc=a%3Db", - "adobe_mc=%7C%7C", - `adobe_mc=${encodeURIComponent( - "TS=foo|MCMID=12345|MCORGID=FAF554945B90342F0A495E2C@AdobeOrg", - )}`, - `adobe_mc=${encodeURIComponent( - "TS=1641432103|MCMID=|MCORGID=FAF554945B90342F0A495E2C@AdobeOrg", - )}`, - `adobe_mc=${encodeURIComponent("TS|MCMID")}`, - ].forEach((value) => { - it(`handles garbage parameter value: ${value}`, () => { - locationSearch = `?${value}`; - run(); - expect(payload.addIdentity).not.toHaveBeenCalled(); - expect(logger.info).toHaveBeenCalledOnceWith( - jasmine.stringMatching(/invalid/), - ); - }); - }); - - it("reads an identity from visitor", () => { - locationSearch = - "?adobe_mc=MCMID%3D06387190804794960331430905673364101813%7CMCORGID%3D5BFE274A5F6980A50A495C08%2540AdobeOrg%7CTS%3D1653516560"; - orgId = "5BFE274A5F6980A50A495C08@AdobeOrg"; - date = new Date(1653516560 * 1000); - run(); - expect(payload.addIdentity).toHaveBeenCalledOnceWith("ECID", { - id: "06387190804794960331430905673364101813", - }); - }); - - it("handles multiple copies of the adobe_mc param", () => { - locationSearch = - "?adobe_mc=MCMID%3Dfirst%7CMCORGID%3Dabc%7CTS%3D1653516560&adobe_mc=MCMID%3Dsecond%7CMCORGID%3Dabc%7CTS%3D1653516560"; - orgId = "abc"; - date = new Date(1653516560 * 1000); - run(); - expect(payload.addIdentity).toHaveBeenCalledOnceWith("ECID", { - id: "second", - }); - expect(logger.warn).toHaveBeenCalled(); - }); - - it("handles multiple copies of the adobe_mc param with empty param", () => { - locationSearch = - "?adobe_mc=MCMID%3Dfirst%7CMCORGID%3Dabc%7CTS%3D1653516560&adobe_mc="; - orgId = "abc"; - date = new Date(1653516560 * 1000); - run(); - expect(payload.addIdentity).not.toHaveBeenCalled(); - expect(logger.warn).toHaveBeenCalled(); - expect(logger.info).toHaveBeenCalled(); - }); - }); -}); diff --git a/test/unit/specs/components/Identity/injectAwaitIdentityCookie.spec.js b/test/unit/specs/components/Identity/injectAwaitIdentityCookie.spec.js deleted file mode 100644 index 222ea54d4..000000000 --- a/test/unit/specs/components/Identity/injectAwaitIdentityCookie.spec.js +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectAwaitIdentityCookie from "../../../../../src/components/Identity/injectAwaitIdentityCookie.js"; - -describe("Identity::injectAwaitIdentityCookie", () => { - let identityCookieExists; - let awaitIdentityCookie; - let runOnResponseCallbacks; - let runOnRequestFailureCallbacks; - let onResponse; - let onRequestFailure; - let logger; - - beforeEach(() => { - identityCookieExists = true; - const onResponseCallbacks = []; - runOnResponseCallbacks = () => { - onResponseCallbacks.forEach((callback) => { - callback(); - }); - }; - onResponse = (callback) => onResponseCallbacks.push(callback); - const onRequestFailureCallbacks = []; - runOnRequestFailureCallbacks = () => { - onRequestFailureCallbacks.forEach((callback) => { - callback(); - }); - }; - onRequestFailure = (callback) => onRequestFailureCallbacks.push(callback); - logger = jasmine.createSpyObj("logger", ["warn"]); - awaitIdentityCookie = injectAwaitIdentityCookie({ - orgId: "org@adobe", - doesIdentityCookieExist: () => identityCookieExists, - logger, - }); - }); - - it("resolves promise if identity cookie exists after response", () => { - const promise = awaitIdentityCookie({ onResponse, onRequestFailure }); - runOnResponseCallbacks(); - expect(logger.warn).not.toHaveBeenCalled(); - return promise; - }); - - it("rejects promise if identity cookie does not exist after response and logs warning", () => { - identityCookieExists = false; - const promise = awaitIdentityCookie({ onResponse, onRequestFailure }); - runOnResponseCallbacks(); - expect(logger.warn).toHaveBeenCalled(); - return expectAsync(promise).toBeRejectedWithError( - /Identity cookie not found/i, - ); - }); - - it("resolves promise if identity cookie exists after request failure", () => { - const promise = awaitIdentityCookie({ onResponse, onRequestFailure }); - runOnRequestFailureCallbacks(); - expect(logger.warn).not.toHaveBeenCalled(); - return promise; - }); - - it("rejects promise if identity cookie does not exist after request failure", () => { - identityCookieExists = false; - const promise = awaitIdentityCookie({ onResponse, onRequestFailure }); - runOnRequestFailureCallbacks(); - expect(logger.warn).not.toHaveBeenCalled(); - return expectAsync(promise).toBeRejectedWithError( - /Identity cookie not found/i, - ); - }); -}); diff --git a/test/unit/specs/components/Identity/injectEnsureSingleIdentity.spec.js b/test/unit/specs/components/Identity/injectEnsureSingleIdentity.spec.js deleted file mode 100644 index d0ced378c..000000000 --- a/test/unit/specs/components/Identity/injectEnsureSingleIdentity.spec.js +++ /dev/null @@ -1,219 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectEnsureSingleIdentity from "../../../../../src/components/Identity/injectEnsureSingleIdentity.js"; -import { defer } from "../../../../../src/utils/index.js"; -import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; - -describe("Identity::injectEnsureSingleIdentity", () => { - let doesIdentityCookieExist; - let setDomainForInitialIdentityPayload; - let addLegacyEcidToPayload; - let awaitIdentityCookie; - let logger; - let ensureSingleIdentity; - - let sentIndex; - let receivedIndex; - let requests; - let requestSentStatusByIndex; - let awaitIdentityDeferreds; - let onResponse; - let onRequestFailure; - let doesIdentityCookieExistBoolean; - - beforeEach(() => { - logger = jasmine.createSpyObj("logger", ["info"]); - - sentIndex = 0; - receivedIndex = 0; - requests = []; - requestSentStatusByIndex = []; - awaitIdentityDeferreds = []; - doesIdentityCookieExistBoolean = false; - - setDomainForInitialIdentityPayload = (request) => { - request.setUseIdThirdPartyDomain(); - }; - addLegacyEcidToPayload = (payload) => { - payload.addIdentity("ECID", { id: "ABC123" }); - return Promise.resolve(); - }; - awaitIdentityCookie = () => { - const deferred = defer(); - awaitIdentityDeferreds.push(deferred); - return deferred.promise; - }; - doesIdentityCookieExist = () => doesIdentityCookieExistBoolean; - }); - - const setup = () => { - ensureSingleIdentity = injectEnsureSingleIdentity({ - doesIdentityCookieExist, - setDomainForInitialIdentityPayload, - addLegacyEcidToPayload, - awaitIdentityCookie, - logger, - }); - }; - - const sendRequest = () => { - const requestPayload = jasmine.createSpyObj("requestPayload", [ - "addIdentity", - ]); - const request = jasmine.createSpyObj("request", { - getPayload: requestPayload, - setIsIdentityEstablished: undefined, - setUseIdThirdPartyDomain: undefined, - }); - requests.push(request); - onResponse = jasmine.createSpy("onResponse"); - onRequestFailure = jasmine.createSpy("onRequestFailure"); - const i = sentIndex; - requestSentStatusByIndex.push(false); - ensureSingleIdentity({ - request, - onResponse, - onRequestFailure, - }).then(() => { - requestSentStatusByIndex[i] = true; - }); - - sentIndex += 1; - }; - const simulateResponseWithIdentity = () => { - doesIdentityCookieExist = true; - awaitIdentityDeferreds[receivedIndex].resolve(); - receivedIndex += 1; - }; - const simulateResponseWithoutIdentity = () => { - awaitIdentityDeferreds[receivedIndex].reject(); - receivedIndex += 1; - }; - - it("allows first request to proceed and pauses subsequent requests until identity cookie exists", () => { - setup(); - return Promise.resolve() - .then(() => { - sendRequest(); - sendRequest(); - sendRequest(); - return flushPromiseChains(); - }) - .then(() => { - expect(requestSentStatusByIndex).toEqual([true, false, false]); - simulateResponseWithIdentity(); - return flushPromiseChains(); - }) - .then(() => { - expect(requestSentStatusByIndex).toEqual([true, true, true]); - expect(requests[0].setUseIdThirdPartyDomain).toHaveBeenCalled(); - expect(requests[1].setUseIdThirdPartyDomain).not.toHaveBeenCalled(); - expect(requests[2].setUseIdThirdPartyDomain).not.toHaveBeenCalled(); - expect(requests[0].getPayload().addIdentity).toHaveBeenCalled(); - expect(requests[1].getPayload().addIdentity).not.toHaveBeenCalled(); - expect(requests[2].getPayload().addIdentity).not.toHaveBeenCalled(); - sendRequest(); - return flushPromiseChains(); - }) - .then(() => { - expect(requests[3].setUseIdThirdPartyDomain).not.toHaveBeenCalled(); - expect(requests[3].getPayload().addIdentity).not.toHaveBeenCalled(); - expect(requestSentStatusByIndex[3]).toEqual(true); - }); - }); - - it("allows the second request to be called if the first doesn't set the cookie, but still holds up the third", () => { - setup(); - return Promise.resolve() - .then(() => { - sendRequest(); - sendRequest(); - sendRequest(); - sendRequest(); - return flushPromiseChains(); - }) - .then(() => { - expect(requestSentStatusByIndex).toEqual([true, false, false, false]); - simulateResponseWithoutIdentity(); - return flushPromiseChains(); - }) - .then(() => { - expect(requestSentStatusByIndex).toEqual([true, true, false, false]); - simulateResponseWithIdentity(); - return flushPromiseChains(); - }) - .then(() => { - expect(requestSentStatusByIndex).toEqual([true, true, true, true]); - expect(requests[0].setUseIdThirdPartyDomain).toHaveBeenCalled(); - expect(requests[1].setUseIdThirdPartyDomain).toHaveBeenCalled(); - expect(requests[2].setUseIdThirdPartyDomain).not.toHaveBeenCalled(); - expect(requests[3].setUseIdThirdPartyDomain).not.toHaveBeenCalled(); - expect(requests[0].getPayload().addIdentity).toHaveBeenCalled(); - expect(requests[1].getPayload().addIdentity).toHaveBeenCalled(); - expect(requests[2].getPayload().addIdentity).not.toHaveBeenCalled(); - expect(requests[3].getPayload().addIdentity).not.toHaveBeenCalled(); - }); - }); - - it("logs messages", () => { - setup(); - return Promise.resolve() - .then(() => { - sendRequest(); - return flushPromiseChains(); - }) - .then(() => { - expect(logger.info).not.toHaveBeenCalled(); - sendRequest(); - return flushPromiseChains(); - }) - .then(() => { - expect(logger.info).toHaveBeenCalledWith( - "Delaying request while retrieving ECID from server.", - ); - simulateResponseWithIdentity(); - return flushPromiseChains(); - }) - .then(() => { - expect(logger.info).toHaveBeenCalledWith( - "Resuming previously delayed request.", - ); - }); - }); - - it("sends a request without third-party domain or legacy ECID if we have an identity cookie", () => { - doesIdentityCookieExistBoolean = true; - setup(); - return Promise.resolve() - .then(() => { - sendRequest(); - return flushPromiseChains(); - }) - .then(() => { - expect(requestSentStatusByIndex).toEqual([true]); - expect(requests[0].setUseIdThirdPartyDomain).not.toHaveBeenCalled(); - expect(requests[0].getPayload().addIdentity).not.toHaveBeenCalled(); - }); - }); - - it("calls awaitIdentityCookie with the correct parameters", () => { - awaitIdentityCookie = jasmine.createSpy("awaitIdentityCookie"); - awaitIdentityCookie.and.returnValue(Promise.resolve()); - setup(); - sendRequest(); - expect(awaitIdentityCookie).toHaveBeenCalledWith({ - onResponse, - onRequestFailure, - }); - }); -}); diff --git a/test/unit/specs/components/Identity/injectHandleResponseForIdSyncs.spec.js b/test/unit/specs/components/Identity/injectHandleResponseForIdSyncs.spec.js deleted file mode 100644 index 3d6e6ce26..000000000 --- a/test/unit/specs/components/Identity/injectHandleResponseForIdSyncs.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectHandleResponseForIdSyncs from "../../../../../src/components/Identity/injectHandleResponseForIdSyncs.js"; - -describe("Identity::injectHandleResponseForIdSyncs", () => { - it("processes ID syncs", () => { - const processIdSyncsPromise = Promise.resolve(); - const processIdSyncs = jasmine - .createSpy("processIdSyncs") - .and.returnValue(processIdSyncsPromise); - const idSyncPayloads = [ - { - type: "idSync", - }, - ]; - const response = jasmine.createSpyObj("response", { - getPayloadsByType: idSyncPayloads, - }); - const handleResponseForIdSyncs = injectHandleResponseForIdSyncs({ - processIdSyncs, - }); - const result = handleResponseForIdSyncs(response); - expect(processIdSyncs).toHaveBeenCalledWith(idSyncPayloads); - expect(result).toBe(processIdSyncsPromise); - }); -}); diff --git a/test/unit/specs/components/Identity/injectProcessIdSyncs.spec.js b/test/unit/specs/components/Identity/injectProcessIdSyncs.spec.js deleted file mode 100644 index cc9cf18ef..000000000 --- a/test/unit/specs/components/Identity/injectProcessIdSyncs.spec.js +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import injectProcessIdSyncs from "../../../../../src/components/Identity/injectProcessIdSyncs.js"; - -describe("Identity::injectProcessIdSyncs", () => { - let fireReferrerHideableImage; - let logger; - let processIdSyncs; - - beforeEach(() => { - fireReferrerHideableImage = jasmine - .createSpy() - .and.returnValue(Promise.resolve()); - logger = jasmine.createSpyObj("logger", ["info", "error"]); - processIdSyncs = injectProcessIdSyncs({ - fireReferrerHideableImage, - logger, - }); - }); - - it("handles no ID syncs", () => { - return processIdSyncs([]).then(() => { - expect(fireReferrerHideableImage).not.toHaveBeenCalled(); - }); - }); - - it("calls fireReferrerHideableImage for all ID syncs of type URL, and logs results", () => { - fireReferrerHideableImage.and.callFake(({ url }) => { - return url === "http://test.zyx" ? Promise.resolve() : Promise.reject(); - }); - - const identities = [ - { - type: "url", - id: 2097728, - spec: { - url: "http://test.abc", - hideReferrer: true, - }, - }, - { - type: "cookie", - spec: { - name: "testCookieIdSync", - value: "id\u003ds2", - domain: "", - ttl: 30, - }, - }, - { - type: "url", - id: 2097729, - spec: { - url: "http://test.zyx", - hideReferrer: false, - }, - }, - ]; - - return processIdSyncs(identities).then(() => { - expect(fireReferrerHideableImage).toHaveBeenCalledWith({ - url: "http://test.abc", - hideReferrer: true, - }); - expect(fireReferrerHideableImage).toHaveBeenCalledWith({ - url: "http://test.zyx", - hideReferrer: false, - }); - expect(logger.info).toHaveBeenCalledWith( - "ID sync succeeded: http://test.zyx", - ); - expect(logger.error).toHaveBeenCalledWith( - "ID sync failed: http://test.abc", - ); - }); - }); -}); diff --git a/test/unit/specs/components/Identity/injectSetDomainForInitialIdentityPayload.spec.js b/test/unit/specs/components/Identity/injectSetDomainForInitialIdentityPayload.spec.js deleted file mode 100644 index 5f8d30820..000000000 --- a/test/unit/specs/components/Identity/injectSetDomainForInitialIdentityPayload.spec.js +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectSetDomainForInitialIdentityPayload from "../../../../../src/components/Identity/injectSetDomainForInitialIdentityPayload.js"; - -describe("Identity::injectSetDomainForInitialIdentityPayload", () => { - let request; - let thirdPartyCookiesEnabled; - let areThirdPartyCookiesSupportedByDefault; - let setDomainForInitialIdentityPayload; - - const build = () => { - setDomainForInitialIdentityPayload = - injectSetDomainForInitialIdentityPayload({ - thirdPartyCookiesEnabled, - areThirdPartyCookiesSupportedByDefault, - }); - }; - - beforeEach(() => { - request = jasmine.createSpyObj("request", ["setUseIdThirdPartyDomain"]); - areThirdPartyCookiesSupportedByDefault = jasmine.createSpy( - "areThirdPartyCookiesSupportedByDefault", - ); - }); - - it("does not use third-party domain if third-party cookies are disabled", () => { - thirdPartyCookiesEnabled = false; - areThirdPartyCookiesSupportedByDefault.and.returnValue(true); - build(); - setDomainForInitialIdentityPayload(request); - expect(request.setUseIdThirdPartyDomain).not.toHaveBeenCalled(); - }); - - it("does not use third-party domain if third-party cookies are not supported by the browser by default", () => { - thirdPartyCookiesEnabled = true; - areThirdPartyCookiesSupportedByDefault.and.returnValue(false); - build(); - setDomainForInitialIdentityPayload(request); - expect(areThirdPartyCookiesSupportedByDefault).toHaveBeenCalledWith(); - expect(request.setUseIdThirdPartyDomain).not.toHaveBeenCalled(); - }); - - it("uses third-party domain if third-party cookies are enabled and supported by the browser by default", () => { - thirdPartyCookiesEnabled = true; - areThirdPartyCookiesSupportedByDefault.and.returnValue(true); - build(); - setDomainForInitialIdentityPayload(request); - expect(areThirdPartyCookiesSupportedByDefault).toHaveBeenCalledWith(); - expect(request.setUseIdThirdPartyDomain).toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Identity/visitorService/awaitVisitorOptIn.spec.js b/test/unit/specs/components/Identity/visitorService/awaitVisitorOptIn.spec.js deleted file mode 100644 index 3887a32ff..000000000 --- a/test/unit/specs/components/Identity/visitorService/awaitVisitorOptIn.spec.js +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import awaitVisitorOptIn from "../../../../../../src/components/Identity/visitorService/awaitVisitorOptIn.js"; - -const logger = jasmine.createSpyObj(["info"]); - -describe("awaitVisitorOptIn", () => { - beforeEach(() => { - window.adobe = undefined; - }); - - afterAll(() => { - window.adobe = undefined; - }); - - describe("No legacy opt in object is present", () => { - it("should return promise resolved with undefined", () => { - return expectAsync(awaitVisitorOptIn({ logger })).toBeResolvedTo( - undefined, - ); - }); - }); - - describe("Legacy opt in object is present and gives approval", () => { - it("should return promise resolved with undefined", () => { - window.adobe = { - optIn: { - fetchPermissions(callback) { - setTimeout(callback, 0); - }, - isApproved() { - return true; - }, - Categories: { - ECID: "ecid", - }, - }, - }; - - return expectAsync(awaitVisitorOptIn({ logger })).toBeResolvedTo( - undefined, - ); - }); - }); - - describe("Legacy opt in object is present and gives denial", () => { - it('should return promise rejected with new Error("Legacy opt-in was declined.")', () => { - window.adobe = { - optIn: { - fetchPermissions(callback) { - setTimeout(callback, 0); - }, - isApproved() { - return false; - }, - Categories: { - ECID: "ecid", - }, - }, - }; - - return expectAsync(awaitVisitorOptIn({ logger })).toBeRejectedWithError( - "Legacy opt-in was declined.", - ); - }); - }); -}); diff --git a/test/unit/specs/components/Identity/visitorService/getVisitor.spec.js b/test/unit/specs/components/Identity/visitorService/getVisitor.spec.js deleted file mode 100644 index 5bd76bd88..000000000 --- a/test/unit/specs/components/Identity/visitorService/getVisitor.spec.js +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getVisitor from "../../../../../../src/components/Identity/visitorService/getVisitor.js"; - -describe("getVisitor", () => { - let mockWindow; - - beforeEach(() => { - mockWindow = {}; - }); - - it("Returns Visitor function if Visitor is available and valid", () => { - mockWindow.Visitor = jasmine.createSpy(); - mockWindow.Visitor.getInstance = jasmine.createSpy(); - expect(getVisitor(mockWindow)).toEqual(mockWindow.Visitor); - }); - it("Returns false if Visitor is available but does not support getInstance", () => { - mockWindow.Visitor = jasmine.createSpy(); - expect(getVisitor(mockWindow)).toBe(false); - }); - it("Returns false if Visitor is not available", () => { - expect(getVisitor(mockWindow)).toBe(false); - }); -}); diff --git a/test/unit/specs/components/Identity/visitorService/injectGetEcidFromVisitor.spec.js b/test/unit/specs/components/Identity/visitorService/injectGetEcidFromVisitor.spec.js deleted file mode 100644 index 3f3c13c15..000000000 --- a/test/unit/specs/components/Identity/visitorService/injectGetEcidFromVisitor.spec.js +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectGetEcidFromVisitor from "../../../../../../src/components/Identity/visitorService/injectGetEcidFromVisitor.js"; - -const logger = jasmine.createSpyObj(["info"]); - -const Visitor = () => {}; -Visitor.getInstance = () => { - return { - getMarketingCloudVisitorID(cb) { - setTimeout(() => { - cb("ecid123"); - }, 0); - }, - }; -}; - -const orgId = "456org"; - -describe("getEcidFromVisitor", () => { - beforeEach(() => { - window.Visitor = undefined; - }); - - afterAll(() => { - window.Visitor = undefined; - }); - - describe("Visitor does not exist", () => { - it("should return promise resolved with undefined", () => { - const getEcidFromVisitor = injectGetEcidFromVisitor({ logger, orgId }); - return expectAsync(getEcidFromVisitor()).toBeResolvedTo(undefined); - }); - }); - - describe("Visitor exists; awaitVisitorOptIn resolves the promise", () => { - it("should return promise resolved with ecid123", () => { - window.Visitor = Visitor; - const awaitVisitorOptIn = () => { - return Promise.resolve(); - }; - - const getEcidFromVisitor = injectGetEcidFromVisitor({ - logger, - orgId, - awaitVisitorOptIn, - }); - return expectAsync(getEcidFromVisitor()).toBeResolvedTo("ecid123"); - }); - }); - - describe("Visitor exists; awaitVisitorOptIn rejects the promise", () => { - it("should return promise resolved with undefined", () => { - window.Visitor = Visitor; - const awaitVisitorOptIn = () => { - return Promise.reject(); - }; - - const getEcidFromVisitor = injectGetEcidFromVisitor({ - logger, - orgId, - awaitVisitorOptIn, - }); - return expectAsync(getEcidFromVisitor()).toBeResolvedTo(undefined); - }); - }); - - it("should find Visitor if it was defined after Web SDK initialization.", () => { - const awaitVisitorOptIn = () => { - return Promise.resolve(); - }; - - const getEcidFromVisitor = injectGetEcidFromVisitor({ - logger, - orgId, - awaitVisitorOptIn, - }); - window.Visitor = Visitor; - return expectAsync(getEcidFromVisitor()).toBeResolvedTo("ecid123"); - }); -}); diff --git a/test/unit/specs/components/LibraryInfo/index.spec.js b/test/unit/specs/components/LibraryInfo/index.spec.js deleted file mode 100644 index 14e18b877..000000000 --- a/test/unit/specs/components/LibraryInfo/index.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createLibraryInfo from "../../../../../src/components/LibraryInfo/index.js"; - -describe("LibraryInfo", () => { - let toolsMock; - - beforeEach(() => { - toolsMock = { - config: { - foo: "bar", - }, - componentRegistry: { - getCommandNames: () => ["bar"], - getComponentNames: () => ["ComponentA", "ComponentB"], - }, - }; - }); - - it("returns library, command, and config information", () => { - expect(createLibraryInfo(toolsMock).commands.getLibraryInfo.run()).toEqual({ - libraryInfo: { - version: `__VERSION__`, - configs: { foo: "bar" }, - commands: ["bar", "configure", "setDebug"], - components: ["ComponentA", "ComponentB"], - }, - }); - }); -}); diff --git a/test/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js b/test/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js deleted file mode 100644 index cb75b2ff3..000000000 --- a/test/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js +++ /dev/null @@ -1,275 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createGetInstance from "../../../../../src/components/MediaAnalyticsBridge/createGetInstance.js"; - -describe("createGetInstance", () => { - const logger = { - warn: jasmine.createSpy(), - }; - let trackMediaSession; - let trackMediaEvent; - let uuid; - - beforeEach(() => { - trackMediaSession = jasmine.createSpy(); - trackMediaEvent = jasmine.createSpy(); - uuid = jasmine.createSpy().and.returnValue("1234-5678-9101-1121"); - }); - - it("should return an object", () => { - const result = createGetInstance({ - logger, - trackMediaSession, - trackMediaEvent, - uuid, - }); - expect(typeof result).toBe("object"); - expect(typeof result.trackSessionStart).toBe("function"); - expect(typeof result.trackPlay).toBe("function"); - expect(typeof result.trackComplete).toBe("function"); - expect(typeof result.trackPause).toBe("function"); - expect(typeof result.trackError).toBe("function"); - expect(typeof result.trackEvent).toBe("function"); - expect(typeof result.trackSessionEnd).toBe("function"); - expect(typeof result.updatePlayhead).toBe("function"); - expect(typeof result.updateQoEObject).toBe("function"); - expect(typeof result.destroy).toBe("function"); - }); - - it("when play is called", () => { - const result = createGetInstance({ - logger, - trackMediaSession, - trackMediaEvent, - uuid, - }); - result.trackSessionStart({ sessionDetails: {} }); - result.trackPlay(); - - expect(trackMediaEvent).toHaveBeenCalledWith({ - playerId: "1234-5678-9101-1121", - xdm: { eventType: "media.play", mediaCollection: {} }, - }); - }); - it("when pause is called", () => { - const result = createGetInstance({ - logger, - trackMediaSession, - trackMediaEvent, - uuid, - }); - result.trackSessionStart({ sessionDetails: {} }); - result.trackPause(); - - expect(trackMediaEvent).toHaveBeenCalledWith({ - playerId: "1234-5678-9101-1121", - xdm: { eventType: "media.pauseStart", mediaCollection: {} }, - }); - }); - - it("when sessionStart is called", () => { - const result = createGetInstance({ - logger, - trackMediaSession, - trackMediaEvent, - uuid, - }); - const sessionDetails = { - name: "test", - friendlyName: "test1", - length: "test2", - streamType: "vod", - contentType: "video/mp4", - }; - - const meta = { - isUserLoggedIn: "false", - tvStation: "Sample TV station", - programmer: "Sample programmer", - assetID: "/uri-reference", - "a.media.episode": "episode1", - }; - result.trackSessionStart({ sessionDetails }, meta); - expect(trackMediaSession).toHaveBeenCalledWith({ - playerId: "1234-5678-9101-1121", - getPlayerDetails: jasmine.any(Function), - xdm: { - eventType: "media.sessionStart", - mediaCollection: { - sessionDetails: { - name: "test", - friendlyName: "test1", - length: "test2", - streamType: "vod", - contentType: "video/mp4", - episode: "episode1", - }, - customMetadata: [ - { name: "isUserLoggedIn", value: "false" }, - { name: "tvStation", value: "Sample TV station" }, - { name: "programmer", value: "Sample programmer" }, - { name: "assetID", value: "/uri-reference" }, - ], - }, - }, - }); - }); - - it("when trackError is called", () => { - const result = createGetInstance({ - logger, - trackMediaSession, - trackMediaEvent, - uuid, - }); - result.trackSessionStart({ sessionDetails: {} }); - result.trackError("error"); - - expect(trackMediaEvent).toHaveBeenCalledWith({ - playerId: "1234-5678-9101-1121", - xdm: { - eventType: "media.error", - mediaCollection: { - errorDetails: { name: "error", source: "player" }, - }, - }, - }); - expect(logger.warn).toHaveBeenCalled(); - }); - - it("when trackComplete is called", () => { - const result = createGetInstance({ - logger, - trackMediaSession, - trackMediaEvent, - uuid, - }); - result.trackSessionStart({ sessionDetails: {} }); - result.trackComplete(); - - expect(trackMediaEvent).toHaveBeenCalledWith({ - playerId: "1234-5678-9101-1121", - xdm: { - eventType: "media.sessionComplete", - mediaCollection: {}, - }, - }); - }); - it("when trackSessionEnd is called", () => { - const result = createGetInstance({ - logger, - trackMediaSession, - trackMediaEvent, - uuid, - }); - result.trackSessionStart({ sessionDetails: {} }); - result.trackSessionEnd(); - - expect(trackMediaEvent).toHaveBeenCalledWith({ - playerId: "1234-5678-9101-1121", - xdm: { - eventType: "media.sessionEnd", - mediaCollection: {}, - }, - }); - }); - it("when state update is called", () => { - const result = createGetInstance({ - logger, - trackMediaSession, - trackMediaEvent, - uuid, - }); - const state = { - name: "muted", - }; - result.trackSessionStart({ sessionDetails: {} }); - result.trackEvent("stateStart", state); - - expect(trackMediaEvent).toHaveBeenCalledWith({ - playerId: "1234-5678-9101-1121", - xdm: { - eventType: "media.statesUpdate", - mediaCollection: { - statesStart: [{ name: "muted" }], - }, - }, - }); - }); - it("when state update is called", () => { - const result = createGetInstance({ - logger, - trackMediaSession, - trackMediaEvent, - uuid, - }); - const state = { - name: "muted", - }; - result.trackSessionStart({ sessionDetails: {} }); - result.trackEvent("stateEnd", state); - - expect(trackMediaEvent).toHaveBeenCalledWith({ - playerId: "1234-5678-9101-1121", - xdm: { - eventType: "media.statesUpdate", - mediaCollection: { - statesEnd: [{ name: "muted" }], - }, - }, - }); - }); - it("when track adds is called add get's converted correctly", () => { - const result = createGetInstance({ - logger, - trackMediaSession, - trackMediaEvent, - uuid, - }); - const advertisingDetails = { - friendlyName: "test", - name: "trst1", - podPosition: 2, - length: 100, - }; - - const adContextData = { - affiliate: "Sample affiliate 2", - campaign: "Sample ad campaign 2", - "a.media.ad.advertiser": "Sample Advertiser 2", - "a.media.ad.campaign": "csmpaign2", - }; - result.trackSessionStart({ sessionDetails: {} }); - result.trackEvent("adStart", { advertisingDetails }, adContextData); - - expect(trackMediaEvent).toHaveBeenCalledWith({ - playerId: "1234-5678-9101-1121", - xdm: { - eventType: "media.adStart", - mediaCollection: { - advertisingDetails: { - friendlyName: "test", - name: "trst1", - podPosition: 2, - length: 100, - advertiser: "Sample Advertiser 2", - campaignID: "csmpaign2", - }, - customMetadata: [ - { name: "affiliate", value: "Sample affiliate 2" }, - { name: "campaign", value: "Sample ad campaign 2" }, - ], - }, - }, - }); - }); -}); diff --git a/test/unit/specs/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.spec.js b/test/unit/specs/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.spec.js deleted file mode 100644 index e60cbc281..000000000 --- a/test/unit/specs/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.spec.js +++ /dev/null @@ -1,105 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createMediaAnalyticsBridgeComponent from "../../../../../src/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.js"; - -describe("MediaAnalyticsBridge::createMediaAnalyticsBridgeComponent", () => { - const config = { - streamingMedia: { - channel: "testChannel", - playerName: "testPlayerName", - appVersion: "testAppVersion", - }, - }; - let logger; - let mediaAnalyticsBridgeComponent; - let trackMediaEvent; - let mediaResponseHandler; - let trackMediaSession; - let createMediaHelper; - let createGetInstance; - - const build = (configs) => { - mediaAnalyticsBridgeComponent = createMediaAnalyticsBridgeComponent({ - config: configs, - logger, - trackMediaEvent, - mediaResponseHandler, - trackMediaSession, - createMediaHelper, - createGetInstance, - }); - }; - - beforeEach(() => { - logger = jasmine.createSpyObj("logger", ["info"]); - mediaResponseHandler = jasmine.createSpy(); - trackMediaEvent = jasmine.createSpy(); - trackMediaSession = jasmine.createSpy(); - createMediaHelper = jasmine.createSpy(); - createGetInstance = jasmine.createSpy(); - build(config); - }); - - it("should reject promise when called with invalid config", async () => { - build({}); - const getMediaAnalyticsTracker = - mediaAnalyticsBridgeComponent.commands.getMediaAnalyticsTracker; - - return expectAsync(getMediaAnalyticsTracker.run()).toBeRejected(); - }); - - it("should call createGetInstance when getInstance Media API is called", async () => { - build(config); - - const { getMediaAnalyticsTracker } = mediaAnalyticsBridgeComponent.commands; - const mediaApi = await getMediaAnalyticsTracker.run(); - mediaApi.getInstance(); - expect(createGetInstance).toHaveBeenCalled(); - }); - - it("should call onBeforeMediaEvent when onBeforeEvent is called with legacy flag", async () => { - build(config); - const getPlayerDetails = () => {}; - const { onBeforeEvent } = mediaAnalyticsBridgeComponent.lifecycle; - const mediaOptions = { - legacy: true, - playerId: "testPlayerId", - getPlayerDetails, - }; - const onResponseHandler = (onResponse) => { - onResponse({ response: {} }); - }; - onBeforeEvent({ mediaOptions, onResponse: onResponseHandler }); - expect(mediaResponseHandler).toHaveBeenCalledWith({ - getPlayerDetails, - playerId: "testPlayerId", - response: {}, - }); - }); - - it("should not call onBeforeMediaEvent when onBeforeEvent is called without legacy flag", async () => { - build(config); - const getPlayerDetails = () => {}; - const { onBeforeEvent } = mediaAnalyticsBridgeComponent.lifecycle; - const mediaOptions = { - legacy: false, - playerId: "testPlayerId", - getPlayerDetails, - }; - const onResponseHandler = (onResponse) => { - onResponse({ response: {} }); - }; - onBeforeEvent({ mediaOptions, onResponse: onResponseHandler }); - expect(mediaResponseHandler).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/MediaAnalyticsBridge/createMediaHelper.spec.js b/test/unit/specs/components/MediaAnalyticsBridge/createMediaHelper.spec.js deleted file mode 100644 index b0f2d151c..000000000 --- a/test/unit/specs/components/MediaAnalyticsBridge/createMediaHelper.spec.js +++ /dev/null @@ -1,259 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createMediaHelper from "../../../../../src/components/MediaAnalyticsBridge/createMediaHelper.js"; - -describe("createMediaHelper", () => { - let logger; - let mediaHelper; - - beforeEach(() => { - logger = { - warn: jasmine.createSpy("warn"), - }; - mediaHelper = createMediaHelper({ logger }); - }); - - describe("createMediaObject", () => { - it("should return a valid media object when called with valid arguments", () => { - const friendlyName = "testFriendlyName"; - const name = "testName"; - const length = 120; - const contentType = "video/mp4"; - const streamType = "VOD"; - - const expectedResult = { - sessionDetails: { - friendlyName, - name, - length, - contentType, - streamType, - }, - }; - - const result = mediaHelper.createMediaObject( - friendlyName, - name, - length, - contentType, - streamType, - ); - - expect(result).toEqual(expectedResult); - }); - - it("should log a warning and return an empty object when validation fails", () => { - const friendlyName = ""; - const name = ""; - const length = "invalid"; - const contentType = ""; - const streamType = ""; - - const expectedResult = {}; - - const result = mediaHelper.createMediaObject( - friendlyName, - name, - length, - contentType, - streamType, - ); - - expect(result).toEqual(expectedResult); - expect(logger.warn).toHaveBeenCalled(); - }); - }); - - describe("createAdBreakObject", () => { - it("should return a valid ad break object when called with valid arguments", () => { - const name = "testAdBreak"; - const position = 1; - const startTime = 120; - - const expectedResult = { - advertisingPodDetails: { - friendlyName: name, - offset: position, - index: startTime, - }, - }; - - const result = mediaHelper.createAdBreakObject(name, position, startTime); - - expect(result).toEqual(expectedResult); - }); - - it("should log a warning and return an empty object when validation fails", () => { - const name = ""; - const position = "invalid"; - const startTime = "invalid"; - - const expectedResult = {}; - - const result = mediaHelper.createAdBreakObject(name, position, startTime); - - expect(result).toEqual(expectedResult); - expect(logger.warn).toHaveBeenCalled(); - }); - }); - - describe("createAdObject", () => { - it("should return a valid ad object when called with valid arguments", () => { - const name = "testAd"; - const id = "testId"; - const position = 1; - const length = 30; - - const expectedResult = { - advertisingDetails: { - friendlyName: name, - name: id, - podPosition: position, - length, - }, - }; - - const result = mediaHelper.createAdObject(name, id, position, length); - - expect(result).toEqual(expectedResult); - }); - - it("should log a warning and return an empty object when validation fails", () => { - const name = ""; - const id = ""; - const position = "invalid"; - const length = "invalid"; - - const expectedResult = {}; - - const result = mediaHelper.createAdObject(name, id, position, length); - - expect(result).toEqual(expectedResult); - expect(logger.warn).toHaveBeenCalled(); - }); - }); - - describe("createChapterObject", () => { - it("should return a valid chapter object when called with valid arguments", () => { - const name = "testChapter"; - const position = 1; - const length = 30; - const startTime = 120; - - const expectedResult = { - chapterDetails: { - friendlyName: name, - offset: position, - length, - index: startTime, - }, - }; - - const result = mediaHelper.createChapterObject( - name, - position, - length, - startTime, - ); - - expect(result).toEqual(expectedResult); - }); - - it("should log a warning and return an empty object when validation fails", () => { - const name = ""; - const position = "invalid"; - const length = "invalid"; - const startTime = "invalid"; - - const expectedResult = {}; - - const result = mediaHelper.createChapterObject( - name, - position, - length, - startTime, - ); - - expect(result).toEqual(expectedResult); - expect(logger.warn).toHaveBeenCalled(); - }); - }); - - describe("createStateObject", () => { - it("should return a valid state object when called with valid arguments", () => { - const stateName = "testState"; - - const expectedResult = { - name: stateName, - }; - - const result = mediaHelper.createStateObject(stateName); - - expect(result).toEqual(expectedResult); - }); - - it("should log a warning and return an empty object when validation fails", () => { - const stateName = "invalid state name"; - - const expectedResult = {}; - - const result = mediaHelper.createStateObject(stateName); - - expect(result).toEqual(expectedResult); - expect(logger.warn).toHaveBeenCalled(); - }); - }); - - describe("createQoEObject", () => { - it("should return a valid QOE object when called with valid arguments", () => { - const bitrate = 5000; - const droppedFrames = 10; - const framesPerSecond = 30; - const timeToStart = 2; - - const expectedResult = { - bitrate, - droppedFrames, - framesPerSecond, - timeToStart, - }; - - const result = mediaHelper.createQoEObject( - bitrate, - droppedFrames, - framesPerSecond, - timeToStart, - ); - - expect(result).toEqual(expectedResult); - }); - - it("should log a warning and return an empty object when validation fails", () => { - const bitrate = "invalid"; - const droppedFrames = "invalid"; - const fps = "invalid"; - const startupTime = "invalid"; - - const expectedResult = {}; - - const result = mediaHelper.createQoEObject( - bitrate, - droppedFrames, - fps, - startupTime, - ); - - expect(result).toEqual(expectedResult); - expect(logger.warn).toHaveBeenCalled(); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/createApplyPropositions.spec.js b/test/unit/specs/components/Personalization/createApplyPropositions.spec.js deleted file mode 100644 index 70e54dc7e..000000000 --- a/test/unit/specs/components/Personalization/createApplyPropositions.spec.js +++ /dev/null @@ -1,296 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - MIXED_PROPOSITIONS, - PAGE_WIDE_SCOPE_DECISIONS, -} from "./responsesMock/eventResponses.js"; -import createApplyPropositions from "../../../../../src/components/Personalization/createApplyPropositions.js"; -import clone from "../../../../../src/utils/clone.js"; -import injectCreateProposition from "../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; -import createMockProposition from "../../../helpers/createMockProposition.js"; -import { DOM_ACTION_COLLECT_INTERACTIONS } from "../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; -import { - JSON_CONTENT_ITEM, - DOM_ACTION, -} from "../../../../../src/constants/schema.js"; - -const METADATA = { - home: { - selector: "#home-item1", - actionType: "setHtml", - }, -}; - -describe("Personalization::createApplyPropositions", () => { - let processPropositions; - let createProposition; - let renderedPropositions; - let viewCache; - let applyPropositions; - let render; - - beforeEach(() => { - processPropositions = jasmine.createSpy("processPropositions"); - processPropositions.and.callFake((propositions) => { - const returnedPropositions = propositions.map((proposition) => ({ - ...proposition.toJSON(), - renderAttempted: true, - })); - return { returnedPropositions, render }; - }); - render = jasmine.createSpy("render"); - render.and.callFake(() => Promise.resolve("notifications")); - createProposition = injectCreateProposition({ - preprocess: (data) => data, - isPageWideSurface: () => false, - }); - - renderedPropositions = jasmine.createSpyObj("renderedPropositions", [ - "concat", - ]); - viewCache = jasmine.createSpyObj("viewCache", ["getView"]); - viewCache.getView.and.returnValue(Promise.resolve([])); - applyPropositions = createApplyPropositions({ - processPropositions, - createProposition, - renderedPropositions, - viewCache, - }); - }); - - it("it should return an empty propositions promise if propositions is empty array", async () => { - const result = await applyPropositions({ - propositions: [], - }); - expect(result).toEqual({ propositions: [] }); - expect(processPropositions).toHaveBeenCalledOnceWith([]); - }); - - it("it should apply user-provided dom-action/default-content schema propositions", async () => { - const expectedExecuteDecisionsPropositions = clone( - PAGE_WIDE_SCOPE_DECISIONS, - ).map((proposition) => { - proposition.items = proposition.items.slice(0, 2); - return proposition; - }); - - const result = await applyPropositions({ - propositions: PAGE_WIDE_SCOPE_DECISIONS, - }); - - expect(processPropositions).toHaveBeenCalledTimes(1); - - const expectedScopes = expectedExecuteDecisionsPropositions.map( - (proposition) => proposition.scope, - ); - result.propositions.forEach((proposition) => { - expect(proposition.renderAttempted).toBeTrue(); - expect(expectedScopes).toContain(proposition.scope); - expect(proposition.items).toEqual( - jasmine.arrayContaining([jasmine.any(Object)]), - ); - expect(proposition.items.length).toEqual(3); - }); - }); - - it("it should merge metadata with propositions that have html-content-item schema", async () => { - const { propositions } = await applyPropositions({ - propositions: MIXED_PROPOSITIONS, - metadata: METADATA, - }); - - expect(propositions.length).toEqual(4); - propositions.forEach((proposition) => { - expect(proposition.items.length).toEqual(1); - if (proposition.items[0].id === "442358") { - expect(proposition.items[0].data.selector).toEqual("#root"); - expect(proposition.items[0].data.type).toEqual("click"); - } else if (proposition.items[0].id === "442359") { - expect(proposition.scope).toEqual("home"); - expect(proposition.items[0].data.selector).toEqual("#home-item1"); - expect(proposition.items[0].data.type).toEqual("setHtml"); - } - }); - expect(processPropositions).toHaveBeenCalledTimes(1); - }); - - it("it should drop items with html-content-item schema when there is no metadata", async () => { - const propositions = [ - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", - scope: "home", - items: [ - { - id: "442359", - schema: "https://ns.adobe.com/personalization/html-content-item", - data: { - content: "

    Some custom content for the home page

    ", - format: "text/html", - id: "1202448", - }, - }, - { - id: "442358", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - format: "application/vnd.adobe.target.dom-action", - selector: "#root", - }, - }, - ], - }, - ]; - - const result = await applyPropositions({ - propositions, - }); - - expect(result.propositions.length).toEqual(1); - expect(result.propositions[0].items.length).toEqual(1); - expect(result.propositions[0].items[0].id).toEqual("442358"); - expect(result.propositions[0].renderAttempted).toBeTrue(); - }); - - it("it should return renderAttempted = true on resulting propositions", async () => { - const result = await applyPropositions({ - propositions: MIXED_PROPOSITIONS, - }); - expect(result.propositions.length).toEqual(3); - result.propositions.forEach((proposition) => { - expect(proposition.renderAttempted).toBeTrue(); - }); - }); - - it("it should ignore propositions with __view__ scope that have already been rendered", async () => { - const propositions = JSON.parse(JSON.stringify(MIXED_PROPOSITIONS)); - propositions[4].renderAttempted = true; - - const result = await applyPropositions({ - propositions, - }); - expect(result.propositions.length).toEqual(2); - result.propositions.forEach((proposition) => { - expect(proposition.renderAttempted).toBeTrue(); - if (proposition.scope === "__view__") { - expect(proposition.items[0].id).not.toEqual("442358"); - } else { - expect(proposition.scope).toEqual("home"); - } - }); - }); - - it("it should ignore items with unsupported schemas", async () => { - const expectedItemIds = ["442358", "442359"]; - - const { propositions } = await applyPropositions({ - propositions: MIXED_PROPOSITIONS, - }); - expect(propositions.length).toEqual(3); - propositions.forEach((proposition) => { - expect(proposition.items.length).toEqual(1); - proposition.items.forEach((item) => { - expect(expectedItemIds.indexOf(item.id) > -1); - }); - }); - }); - - it("it should not mutate original propositions", async () => { - const originalPropositions = clone(MIXED_PROPOSITIONS); - const result = await applyPropositions({ - propositions: originalPropositions, - metadata: METADATA, - }); - - let numReturnedPropositions = 0; - expect(originalPropositions).toEqual(MIXED_PROPOSITIONS); - result.propositions.forEach((proposition) => { - const [original] = originalPropositions.filter( - (originalProposition) => originalProposition.id === proposition.id, - ); - if (original) { - numReturnedPropositions += 1; - expect(proposition).not.toBe(original); - } - }); - expect(numReturnedPropositions).toEqual(4); - }); - - it("concats viewName propositions", async () => { - viewCache.getView.and.returnValue( - Promise.resolve([ - createProposition({ id: "myViewNameProp1", items: [{}] }), - ]), - ); - const result = await applyPropositions({ - viewName: "myViewName", - }); - expect(result).toEqual({ - propositions: [ - { - id: "myViewNameProp1", - items: [{}], - renderAttempted: true, - }, - ], - }); - }); - - it("handles track actions for json-content-item", async () => { - const testElementId = "superfluous123"; - - const proposition = createMockProposition({ - id: "abc", - schema: JSON_CONTENT_ITEM, - data: { isGood: true }, - }); - - const expectedProposition = { - id: "id", - scope: "scope", - scopeDetails: { - decisionProvider: "AJO", - }, - items: [ - { - id: "abc", - schema: DOM_ACTION, - data: { - isGood: true, - selector: "#superfluous123", - type: DOM_ACTION_COLLECT_INTERACTIONS, - }, - }, - ], - }; - - const result = await applyPropositions({ - propositions: [proposition.toJSON()], - metadata: { - scope: { - selector: `#${testElementId}`, - actionType: DOM_ACTION_COLLECT_INTERACTIONS, - }, - }, - }); - - expect(result).toEqual({ - propositions: [{ ...expectedProposition, renderAttempted: true }], - }); - expect(processPropositions).toHaveBeenCalledTimes(1); - - expect(processPropositions.calls.first().args[0][0].toJSON()).toEqual( - expectedProposition, - ); - }); -}); diff --git a/test/unit/specs/components/Personalization/createClickStorage.spec.js b/test/unit/specs/components/Personalization/createClickStorage.spec.js deleted file mode 100644 index 50017cbe0..000000000 --- a/test/unit/specs/components/Personalization/createClickStorage.spec.js +++ /dev/null @@ -1,122 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createClickStorage from "../../../../../src/components/Personalization/createClickStorage.js"; - -describe("Personalization::createClickStorage", () => { - let clickStorage; - - const FIRST_CLICK = { - selector: "div:123:h2", - meta: { - id: "AT:123", - scope: "__view__", - scopeDetails: { - test: "blah1", - }, - trackingLabel: "mylabel", - scopeType: "myscopetype", - }, - }; - const SECOND_CLICK = { - selector: "div:123:h2", - meta: { - id: "AT:123", - scope: "consent", - scopeDetails: { - test: "blah3", - }, - }, - }; - const THIRD_CLICK = { - selector: "div:123:h2", - meta: { - id: "AT:234", - scope: "consent", - scopeDetails: { - test: "blah4", - }, - }, - }; - const FORTH_CLICK = { - selector: "div:123:h1", - meta: { - id: "AT:123", - scope: "consent", - scopeDetails: { - test: "blah5", - }, - }, - }; - - /* this is how the clickStorage map should look like - const expectedClicksInStorage = { - "div:123:h1": { - "AT:123": { - scope: "consent", - scopeDetails: { - blah: "blah" - } - } - }, - "div:123:h2": { - "AT:123": { - scope: "consent", - scopeDetails: { - blah: "blah" - }, - }, - "AT:234": { - scope: "consent", - scopeDetails: { - blah: "blah" - } - } - } - }; */ - beforeEach(() => { - clickStorage = createClickStorage(); - }); - - it("returns empty array if empty storage", () => { - expect(clickStorage.getClickSelectors()).toEqual([]); - }); - - it("returns empty object when no metadata for this selector", () => { - expect(clickStorage.getClickMetas("123")).toEqual({}); - }); - - it("stores clicks as a map in the click storage and returns the selectors and metadata", () => { - clickStorage.storeClickMeta(FIRST_CLICK); - clickStorage.storeClickMeta(SECOND_CLICK); - clickStorage.storeClickMeta(THIRD_CLICK); - clickStorage.storeClickMeta(FORTH_CLICK); - - expect(clickStorage.getClickSelectors().length).toEqual(2); - expect(clickStorage.getClickMetas("div:123:h2").length).toEqual(2); - }); - - it("getClickMetas returns the id, scopeDetails, scope, trackingLabel, and scopeType", () => { - clickStorage.storeClickMeta(FIRST_CLICK); - - const meta = clickStorage.getClickMetas("div:123:h2"); - - expect(clickStorage.getClickSelectors().length).toEqual(1); - expect(meta.length).toEqual(1); - expect(meta[0]).toEqual({ - id: "AT:123", - scope: "__view__", - scopeDetails: { test: "blah1" }, - trackingLabel: "mylabel", - scopeType: "myscopetype", - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/createComponent.spec.js b/test/unit/specs/components/Personalization/createComponent.spec.js deleted file mode 100644 index d8162ab4f..000000000 --- a/test/unit/specs/components/Personalization/createComponent.spec.js +++ /dev/null @@ -1,192 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createComponent from "../../../../../src/components/Personalization/createComponent.js"; - -describe("Personalization", () => { - let logger; - let fetchDataHandler; - let onClickHandler; - let viewChangeHandler; - let showContainers; - let isAuthoringModeEnabled; - let viewCache; - let mergeQuery; - let event; - let personalizationComponent; - let setTargetMigration; - let mergeDecisionsMeta; - let renderedPropositions; - let cacheUpdate; - - const build = () => { - personalizationComponent = createComponent({ - logger, - fetchDataHandler, - viewChangeHandler, - onClickHandler, - isAuthoringModeEnabled, - mergeQuery, - viewCache, - showContainers, - setTargetMigration, - mergeDecisionsMeta, - renderedPropositions, - }); - }; - - beforeEach(() => { - event = jasmine.createSpyObj("event", ["mergeQuery", "getViewName"]); - event.getViewName.and.returnValue({}); - logger = { - info: jasmine.createSpy("logger.info"), - warn: jasmine.createSpy("logger.warn"), - }; - isAuthoringModeEnabled = jasmine - .createSpy("isAuthoringModeEnabled") - .and.returnValue(false); - fetchDataHandler = jasmine.createSpy("fetchDataHandler"); - viewChangeHandler = jasmine.createSpy("viewChangeHandler"); - onClickHandler = jasmine.createSpy("onClickHandler"); - showContainers = jasmine.createSpy("showContainers"); - mergeQuery = jasmine.createSpy("mergeQuery"); - viewCache = jasmine.createSpyObj("viewCache", [ - "isInitialized", - "createCacheUpdate", - ]); - cacheUpdate = jasmine.createSpyObj("cacheUpdate", ["update", "cancel"]); - viewCache.createCacheUpdate.and.returnValue(cacheUpdate); - setTargetMigration = jasmine.createSpy("setTargetMigration"); - mergeDecisionsMeta = jasmine.createSpy("mergeDecisionsMeta"); - renderedPropositions = jasmine.createSpyObj("renderedPropositions", [ - "clear", - ]); - - build(); - }); - - describe("onBeforeEvent", () => { - it("shouldn't do anything since authoringMode is enabled", () => { - isAuthoringModeEnabled.and.returnValue(true); - const renderDecisions = true; - const personalization = { - decisionScopes: ["foo"], - }; - personalizationComponent.lifecycle.onBeforeEvent({ - event, - renderDecisions, - personalization, - }); - - expect(logger.warn).toHaveBeenCalledWith( - "Rendering is disabled for authoring mode.", - ); - expect(isAuthoringModeEnabled).toHaveBeenCalled(); - expect(mergeQuery).toHaveBeenCalledWith(event, { enabled: false }); - expect(fetchDataHandler).not.toHaveBeenCalled(); - expect(viewChangeHandler).not.toHaveBeenCalled(); - expect(onClickHandler).not.toHaveBeenCalled(); - expect(showContainers).not.toHaveBeenCalled(); - expect(viewCache.createCacheUpdate).not.toHaveBeenCalled(); - }); - - it("should trigger pageLoad if there are decisionScopes", () => { - const renderDecisions = false; - const personalization = { - decisionScopes: ["alloy1"], - }; - personalizationComponent.lifecycle.onBeforeEvent({ - event, - renderDecisions, - personalization, - }); - - expect(isAuthoringModeEnabled).toHaveBeenCalled(); - expect(fetchDataHandler).toHaveBeenCalled(); - expect(viewChangeHandler).not.toHaveBeenCalled(); - expect(mergeQuery).not.toHaveBeenCalled(); - expect(onClickHandler).not.toHaveBeenCalled(); - expect(viewCache.createCacheUpdate).toHaveBeenCalled(); - }); - it("should trigger pageLoad if cache is not initialized", () => { - const renderDecisions = false; - const personalization = { - decisionScopes: [], - }; - viewCache.isInitialized.and.returnValue(false); - - personalizationComponent.lifecycle.onBeforeEvent({ - event, - renderDecisions, - personalization, - }); - - expect(isAuthoringModeEnabled).toHaveBeenCalled(); - expect(fetchDataHandler).toHaveBeenCalled(); - expect(viewChangeHandler).not.toHaveBeenCalled(); - expect(mergeQuery).not.toHaveBeenCalled(); - expect(onClickHandler).not.toHaveBeenCalled(); - expect(viewCache.createCacheUpdate).toHaveBeenCalled(); - }); - it("should trigger viewHandler if cache is initialized and viewName is provided", () => { - const renderDecisions = false; - const personalization = { - decisionScopes: [], - }; - viewCache.isInitialized.and.returnValue(true); - event.getViewName.and.returnValue("cart"); - - personalizationComponent.lifecycle.onBeforeEvent({ - event, - renderDecisions, - personalization, - }); - - expect(isAuthoringModeEnabled).toHaveBeenCalled(); - expect(fetchDataHandler).not.toHaveBeenCalled(); - expect(viewChangeHandler).toHaveBeenCalled(); - expect(mergeQuery).not.toHaveBeenCalled(); - expect(onClickHandler).not.toHaveBeenCalled(); - expect(viewCache.createCacheUpdate).not.toHaveBeenCalled(); - }); - it("should trigger onClickHandler at onClick", () => { - personalizationComponent.lifecycle.onClick({ event }); - - expect(onClickHandler).toHaveBeenCalled(); - }); - it("should call showContainers() when a request fails", () => { - viewCache.isInitialized.and.returnValue(true); - const onRequestFailure = jasmine - .createSpy("onRequestFailure") - .and.callFake((func) => func()); - - personalizationComponent.lifecycle.onBeforeEvent({ - event, - onRequestFailure, - }); - - expect(onRequestFailure).toHaveBeenCalled(); - expect(showContainers).toHaveBeenCalled(); - }); - }); - - describe("onBeforeRequest", () => { - it("should always call setTargetMigration during onBeforeRequest", () => { - const request = jasmine.createSpyObj("request", ["getPayload"]); - personalizationComponent.lifecycle.onBeforeRequest({ - request, - }); - - expect(setTargetMigration).toHaveBeenCalledOnceWith(request); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/createFetchDataHandler.spec.js b/test/unit/specs/components/Personalization/createFetchDataHandler.spec.js deleted file mode 100644 index 594be012c..000000000 --- a/test/unit/specs/components/Personalization/createFetchDataHandler.spec.js +++ /dev/null @@ -1,208 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createFetchDataHandler from "../../../../../src/components/Personalization/createFetchDataHandler.js"; -import injectCreateProposition from "../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; -import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; -import defer from "../../../../../src/utils/defer.js"; -import createNotificationHandler from "../../../../../src/components/Personalization/createNotificationHandler.js"; - -describe("Personalization::createFetchDataHandler", () => { - let prehidingStyle; - let showContainers; - let hideContainers; - let mergeQuery; - let collect; - let processPropositions; - let createProposition; - let renderedPropositions; - let notificationHandler; - let consent; - let logger; - let cacheUpdate; - let personalizationDetails; - let event; - let onResponse; - - let response; - - beforeEach(() => { - logger = jasmine.createSpyObj("logger", ["logOnContentRendering"]); - prehidingStyle = "myprehidingstyle"; - showContainers = jasmine.createSpy("showContainers"); - hideContainers = jasmine.createSpy("hideContainers"); - mergeQuery = jasmine.createSpy("mergeQuery"); - collect = jasmine.createSpy("collect"); - processPropositions = jasmine.createSpy("processPropositions"); - createProposition = injectCreateProposition({ - preprocess: (data) => data, - isPageWideSurface: () => false, - }); - renderedPropositions = jasmine.createSpyObj("renderedPropositions", [ - "concat", - ]); - notificationHandler = createNotificationHandler( - collect, - renderedPropositions, - ); - consent = jasmine.createSpyObj("consent", ["current"]); - consent.current.and.returnValue({ state: "in", wasSet: false }); - - cacheUpdate = jasmine.createSpyObj("cacheUpdate", ["update"]); - personalizationDetails = jasmine.createSpyObj("personalizationDetails", [ - "isRenderDecisions", - "createQueryDetails", - "getViewName", - "isSendDisplayEvent", - ]); - personalizationDetails.createQueryDetails.and.returnValue("myquerydetails"); - personalizationDetails.isSendDisplayEvent.and.returnValue(true); - event = "myevent"; - onResponse = jasmine.createSpy(); - response = jasmine.createSpyObj("response", ["getPayloadsByType"]); - }); - - const run = () => { - const fetchDataHandler = createFetchDataHandler({ - prehidingStyle, - showContainers, - hideContainers, - mergeQuery, - processPropositions, - createProposition, - notificationHandler, - consent, - logger, - }); - fetchDataHandler({ - cacheUpdate, - personalizationDetails, - event, - onResponse, - }); - }; - - const returnResponse = () => { - expect(onResponse).toHaveBeenCalledTimes(1); - const callback = onResponse.calls.argsFor(0)[0]; - return callback({ response }); - }; - - it("should hide containers if renderDecisions is true", () => { - personalizationDetails.isRenderDecisions.and.returnValue(true); - run(); - expect(hideContainers).toHaveBeenCalled(); - }); - - it("shouldn't hide containers if renderDecisions is false", () => { - personalizationDetails.isRenderDecisions.and.returnValue(false); - run(); - expect(hideContainers).not.toHaveBeenCalled(); - }); - - it("shouldn't hide containers if we have out consent cookie", () => { - consent.current.and.returnValue({ state: "out", wasSet: true }); - personalizationDetails.isRenderDecisions.and.returnValue(true); - run(); - expect(hideContainers).not.toHaveBeenCalled(); - }); - - it("should trigger responseHandler at onResponse", () => { - personalizationDetails.isRenderDecisions.and.returnValue(false); - run(); - response.getPayloadsByType.and.returnValue([]); - cacheUpdate.update.and.returnValue([]); - processPropositions.and.returnValue({ - returnedPropositions: [], - returnedDecisions: [], - }); - const result = returnResponse(); - expect(result).toEqual({ - propositions: [], - decisions: [], - }); - }); - - it("should render decisions", async () => { - personalizationDetails.isRenderDecisions.and.returnValue(true); - personalizationDetails.getViewName.and.returnValue("myviewname"); - processPropositions = () => { - return { - render: () => Promise.resolve([{ id: "handle1" }]), - returnedPropositions: [ - { id: "handle1", items: ["item1"], renderAttempted: true }, - ], - returnedDecisions: [], - }; - }; - run(); - response.getPayloadsByType.and.returnValue([ - { - id: "handle1", - scopeDetails: { characteristics: { scopeType: "view" } }, - }, - { id: "handle2" }, - ]); - cacheUpdate.update.and.returnValue([createProposition({ id: "handle1" })]); - const result = returnResponse(); - expect(result).toEqual({ - propositions: [ - { id: "handle1", items: ["item1"], renderAttempted: true }, - ], - decisions: [], - }); - await flushPromiseChains(); - expect(showContainers).toHaveBeenCalled(); - expect(collect).toHaveBeenCalledOnceWith({ - decisionsMeta: [{ id: "handle1" }], - viewName: "myviewname", - }); - }); - - it("should show containers immediately", async () => { - personalizationDetails.isRenderDecisions.and.returnValue(true); - const renderDeferred = defer(); - processPropositions = () => { - return { - render: () => renderDeferred.promise, - returnedPropositions: [ - { - id: "handle2", - scope: "__view__", - items: ["item1"], - renderAttempted: true, - }, - ], - returnedDecisions: [], - }; - }; - run(); - response.getPayloadsByType.and.returnValue([ - { - id: "handle2", - scope: "__view__", - items: ["item1"], - }, - ]); - cacheUpdate.update.and.returnValue([]); - expect(showContainers).not.toHaveBeenCalled(); - returnResponse(); - expect(showContainers).toHaveBeenCalled(); - expect(collect).not.toHaveBeenCalled(); - renderDeferred.resolve([{ id: "handle2" }]); - await flushPromiseChains(); - expect(collect).toHaveBeenCalledOnceWith({ - decisionsMeta: [{ id: "handle2" }], - viewName: undefined, - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/createGetPageLocation.spec.js b/test/unit/specs/components/Personalization/createGetPageLocation.spec.js deleted file mode 100644 index a740f81f6..000000000 --- a/test/unit/specs/components/Personalization/createGetPageLocation.spec.js +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createGetPageLocation from "../../../../../src/components/Personalization/createGetPageLocation.js"; - -describe("Personalization::createGetPageLocation", () => { - it("it should return page location object", () => { - const win = { - location: { - href: "https://alloy.test.com/test/page/1/", - host: "alloy.test.com", - pathname: "/test/page/1/", - }, - }; - const getPageLocation = createGetPageLocation({ window: win }); - const location = getPageLocation(); - - expect(location).toEqual(win.location); - }); -}); diff --git a/test/unit/specs/components/Personalization/createHandleConsentFlicker.spec.js b/test/unit/specs/components/Personalization/createHandleConsentFlicker.spec.js deleted file mode 100644 index 2b9a342e3..000000000 --- a/test/unit/specs/components/Personalization/createHandleConsentFlicker.spec.js +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createHandleConsentFlicker from "../../../../../src/components/Personalization/createHandleConsentFlicker.js"; -import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; - -describe("Personalization::createHandleConsentFlicker", () => { - let showContainers; - let consent; - let handleConsentFlicker; - - beforeEach(() => { - showContainers = jasmine.createSpy("showContainers"); - consent = jasmine.createSpyObj("consent", ["current", "awaitConsent"]); - handleConsentFlicker = createHandleConsentFlicker({ - showContainers, - consent, - }); - }); - - it("shows containers when consent is out and was set", () => { - consent.current.and.returnValue({ state: "out", wasSet: true }); - handleConsentFlicker(); - expect(showContainers).toHaveBeenCalled(); - return flushPromiseChains().then(() => { - expect(consent.awaitConsent).not.toHaveBeenCalled(); - }); - }); - - it("shows containers after consent is rejected", () => { - consent.current.and.returnValue({ state: "out", wasSet: false }); - consent.awaitConsent.and.returnValue(Promise.reject()); - handleConsentFlicker(); - expect(consent.awaitConsent).toHaveBeenCalled(); - return flushPromiseChains().then(() => { - expect(showContainers).toHaveBeenCalled(); - }); - }); - - it("does not show containers after consent is given", () => { - consent.current.and.returnValue({ state: "out", wasSet: false }); - consent.awaitConsent.and.returnValue(Promise.resolve()); - handleConsentFlicker(); - expect(consent.awaitConsent).toHaveBeenCalled(); - return flushPromiseChains().then(() => { - expect(showContainers).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/createInteractionStorage.spec.js b/test/unit/specs/components/Personalization/createInteractionStorage.spec.js deleted file mode 100644 index 89dab8931..000000000 --- a/test/unit/specs/components/Personalization/createInteractionStorage.spec.js +++ /dev/null @@ -1,178 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createInteractionStorage from "../../../../../src/components/Personalization/createInteractionStorage.js"; - -describe("Personalization::createInteractionStorage", () => { - let storeInteractionMeta; - let getInteractionMetas; - - let PROPOSITIONS = []; - - let interactIDs = {}; - - beforeEach(() => { - ({ storeInteractionMeta, getInteractionMetas } = - createInteractionStorage()); - - interactIDs = { - "div:123:h2": [1], - "div:123:h1": [2], - }; - - PROPOSITIONS = [ - { - id: "AT:123", - scope: "__view__", - scopeDetails: { - test: "blah1", - characteristics: { - scopeType: "page", - }, - }, - items: [ - { - id: "0632668e-53a4-4f31-b092-45696e45829d", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - selector: "div:123:h2", - }, - characteristics: { - trackingLabel: "mylabel", - }, - }, - ], - }, - { - id: "AT:123", - scope: "consent", - scopeDetails: { - test: "blah3", - characteristics: { - scopeType: "view", - }, - }, - items: [ - { - id: "0632668e-53a4-4f31-b092-45696e45829d", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - selector: "div:123:h2", - }, - }, - ], - }, - { - id: "AT:234", - scope: "consent", - scopeDetails: { - test: "blah4", - characteristics: { - scopeType: "view", - }, - }, - items: [ - { - id: "0632668e-53a4-4f31-b092-45696e45829d", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - selector: "div:123:h2", - }, - }, - ], - }, - { - id: "AT:123", - scope: "consent", - scopeDetails: { - test: "blah5", - characteristics: { - scopeType: "view", - }, - }, - items: [ - { - id: "0632668e-53a4-4f31-b092-45696e45829d", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - selector: "div:123:h1", - }, - }, - ], - }, - ]; - }); - - it("returns empty array when no metadata for this selector", () => { - expect(getInteractionMetas([1])).toEqual([]); - }); - - it("stores clicks as a map in the click storage and returns the metadata", () => { - PROPOSITIONS.forEach((proposition) => { - const { id, scope, scopeDetails } = proposition; - proposition.items.forEach((item) => - storeInteractionMeta( - proposition.id, - item.id, - proposition.scopeDetails.characteristics.scopeType, - { id, scope, scopeDetails }, - interactIDs[item.data.selector], - ), - ); - }); - - expect(getInteractionMetas(interactIDs["div:123:h2"]).length).toEqual(2); - expect(getInteractionMetas(interactIDs["div:123:h1"]).length).toEqual(1); - }); - - it("getInteractionMetas returns the id, scopeDetails, scope, trackingLabel, and scopeType", () => { - const proposition = PROPOSITIONS[0]; - - proposition.items.forEach((item) => - storeInteractionMeta( - proposition.id, - item.id, - proposition.scopeDetails.characteristics.scopeType, - { - id: proposition.id, - scope: proposition.scope, - scopeDetails: proposition.scopeDetails, - }, - interactIDs[item.data.selector], - ), - ); - - const meta = getInteractionMetas(interactIDs["div:123:h2"]); - - expect(meta.length).toEqual(1); - - expect(meta[0]).toEqual({ - id: "AT:123", - scope: "__view__", - scopeDetails: { - test: "blah1", - characteristics: { - scopeType: "page", - }, - }, - scopeType: "page", - items: [ - { - id: "0632668e-53a4-4f31-b092-45696e45829d", - }, - ], - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/createNotificationHandler.spec.js b/test/unit/specs/components/Personalization/createNotificationHandler.spec.js deleted file mode 100644 index 356ca02f1..000000000 --- a/test/unit/specs/components/Personalization/createNotificationHandler.spec.js +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createNotificationHandler from "../../../../../src/components/Personalization/createNotificationHandler.js"; - -describe("Personalization::createNotificationHandler", () => { - let collect; - let renderedPropositions; - let notificationHandler; - const NOTIFICATIONS = [ - { - id: "abc", - scope: "web://localhost:3000/inAppMessages", - scopeDetails: { - activity: { - id: "abc#123", - }, - }, - }, - ]; - - beforeEach(() => { - collect = jasmine.createSpy("collect").and.returnValue(Promise.resolve()); - - renderedPropositions = jasmine.createSpyObj("renderedPropositions", [ - "concat", - ]); - - notificationHandler = createNotificationHandler( - collect, - renderedPropositions, - ); - }); - - it("emits a notification immediately", () => { - const handleNotifications = notificationHandler(true, true, "foo"); - handleNotifications(NOTIFICATIONS); - expect(collect).toHaveBeenCalledOnceWith({ - decisionsMeta: NOTIFICATIONS, - viewName: "foo", - }); - }); - - it("defers the notification", () => { - const handleNotifications = notificationHandler(true, false, undefined); - handleNotifications(NOTIFICATIONS); - - expect(collect).not.toHaveBeenCalled(); - expect(renderedPropositions.concat).toHaveBeenCalledTimes(1); - }); - - it("doesn't do anything if renderDecisions is false", () => { - notificationHandler(false, true, undefined); - expect(renderedPropositions.concat).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Personalization/createOnClickHandler.spec.js b/test/unit/specs/components/Personalization/createOnClickHandler.spec.js deleted file mode 100644 index f9709015a..000000000 --- a/test/unit/specs/components/Personalization/createOnClickHandler.spec.js +++ /dev/null @@ -1,514 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createOnClickHandler from "../../../../../src/components/Personalization/createOnClickHandler.js"; -import { mergeDecisionsMeta } from "../../../../../src/utils/event.js"; -import createEvent from "../../../../../src/core/createEvent.js"; -import { createNode } from "../../../../../src/utils/dom/index.js"; - -import { - ALWAYS, - NEVER, -} from "../../../../../src/constants/propositionInteractionType.js"; -import { - ADOBE_JOURNEY_OPTIMIZER, - ADOBE_TARGET, -} from "../../../../../src/constants/decisionProvider.js"; - -describe("Personalization::createOnClickHandler", () => { - let collectInteractions; - let collectClicks; - - let getInteractionMetas; - let getClickMetas; - - let getClickSelectors; - - let event; - const decisionsMeta = [ - { - id: 1, - scope: "foo", - }, - ]; - const decisionsMeta2 = [ - { - id: 2, - scope: "bar", - }, - ]; - - let autoCollectPropositionInteractions; - - beforeEach(() => { - collectInteractions = jasmine.createSpy("collectInteractions"); - collectClicks = jasmine.createSpy("collectClicks"); - - getInteractionMetas = jasmine.createSpy("getInteractionMetas"); - getClickMetas = jasmine.createSpy("getInteractionMetas"); - - getClickSelectors = jasmine.createSpy("getClickSelectors"); - - event = createEvent(); - spyOn(event, "mergeXdm").and.callThrough(); - - autoCollectPropositionInteractions = { - [ADOBE_JOURNEY_OPTIMIZER]: ALWAYS, - [ADOBE_TARGET]: NEVER, - }; - }); - - it("collects clicks", () => { - const selectors = ["foo", "foo2"]; - collectInteractions.and.returnValue({ decisionsMeta: [] }); - collectClicks.and.returnValue({ - decisionsMeta, - eventLabel: "", - }); - - getClickSelectors.and.returnValue(selectors); - const handleOnClick = createOnClickHandler({ - mergeDecisionsMeta, - collectInteractions, - collectClicks, - getInteractionMetas, - getClickMetas, - getClickSelectors, - autoCollectPropositionInteractions, - }); - - const clickedElement = "foo"; - - handleOnClick({ event, clickedElement }); - - const expectedXdm = { - eventType: "decisioning.propositionInteract", - _experience: { - decisioning: { - propositions: [ - { - id: 1, - scope: "foo", - }, - ], - propositionEventType: { - interact: 1, - }, - }, - }, - }; - - expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); - expect(collectClicks).toHaveBeenCalledWith( - clickedElement, - selectors, - getClickMetas, - ); - event.finalize(); - expect(event.toJSON()).toEqual({ - xdm: expectedXdm, - }); - }); - - it("collects interactions", () => { - collectClicks.and.returnValue({ decisionsMeta: [] }); - collectInteractions.and.returnValue({ - decisionsMeta, - propositionActionLabel: "", - }); - - const handleOnClick = createOnClickHandler({ - mergeDecisionsMeta, - collectInteractions, - collectClicks, - getInteractionMetas, - getClickMetas, - getClickSelectors, - autoCollectPropositionInteractions, - }); - const clickedElement = createNode("div", { class: "clicked-element" }); - - handleOnClick({ event, clickedElement }); - - const expectedXdm = { - eventType: "decisioning.propositionInteract", - _experience: { - decisioning: { - propositions: [ - { - id: 1, - scope: "foo", - }, - ], - propositionEventType: { - interact: 1, - }, - }, - }, - }; - - expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); - expect(collectInteractions).toHaveBeenCalledWith( - clickedElement, - getInteractionMetas, - autoCollectPropositionInteractions, - ); - event.finalize(); - expect(event.toJSON()).toEqual({ - xdm: expectedXdm, - }); - }); - - it("collects clicks with label", () => { - const selectors = ["foo", "foo2"]; - collectInteractions.and.returnValue({ decisionsMeta: [] }); - collectClicks.and.returnValue({ - decisionsMeta, - propositionActionLabel: "click-label", - }); - getClickSelectors.and.returnValue(selectors); - const handleOnClick = createOnClickHandler({ - mergeDecisionsMeta, - collectInteractions, - collectClicks, - getInteractionMetas, - getClickMetas, - getClickSelectors, - autoCollectPropositionInteractions, - }); - const clickedElement = "foo"; - - handleOnClick({ event, clickedElement }); - - const expectedXdm = { - eventType: "decisioning.propositionInteract", - _experience: { - decisioning: { - propositions: [ - { - id: 1, - scope: "foo", - }, - ], - propositionEventType: { - interact: 1, - }, - propositionAction: { - label: "click-label", - }, - }, - }, - }; - - expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); - expect(collectClicks).toHaveBeenCalledWith( - clickedElement, - selectors, - getClickMetas, - ); - event.finalize(); - expect(event.toJSON()).toEqual({ - xdm: expectedXdm, - }); - }); - - it("collects interactions with label", () => { - collectClicks.and.returnValue({ decisionsMeta: [] }); - collectInteractions.and.returnValue({ - decisionsMeta, - propositionActionLabel: "click-label", - }); - - const handleOnClick = createOnClickHandler({ - mergeDecisionsMeta, - collectInteractions, - collectClicks, - getInteractionMetas, - getClickMetas, - getClickSelectors, - autoCollectPropositionInteractions, - }); - const clickedElement = createNode("div", { class: "clicked-element" }); - - handleOnClick({ event, clickedElement }); - - const expectedXdm = { - eventType: "decisioning.propositionInteract", - _experience: { - decisioning: { - propositions: [ - { - id: 1, - scope: "foo", - }, - ], - propositionEventType: { - interact: 1, - }, - propositionAction: { - label: "click-label", - }, - }, - }, - }; - - expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); - expect(collectInteractions).toHaveBeenCalledWith( - clickedElement, - getInteractionMetas, - autoCollectPropositionInteractions, - ); - event.finalize(); - expect(event.toJSON()).toEqual({ - xdm: expectedXdm, - }); - }); - - // TODO collect clicks with token ?? - - it("collects interactions with token", () => { - collectClicks.and.returnValue({ decisionsMeta: [] }); - collectInteractions.and.returnValue({ - decisionsMeta, - propositionActionToken: "click-token", - }); - - const handleOnClick = createOnClickHandler({ - mergeDecisionsMeta, - collectInteractions, - collectClicks, - getInteractionMetas, - getClickMetas, - getClickSelectors, - autoCollectPropositionInteractions, - }); - const clickedElement = createNode("div", { class: "clicked-element" }); - - handleOnClick({ event, clickedElement }); - - const expectedXdm = { - eventType: "decisioning.propositionInteract", - _experience: { - decisioning: { - propositions: [ - { - id: 1, - scope: "foo", - }, - ], - propositionEventType: { - interact: 1, - }, - propositionAction: { - tokens: ["click-token"], - }, - }, - }, - }; - - expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); - expect(collectInteractions).toHaveBeenCalledWith( - clickedElement, - getInteractionMetas, - autoCollectPropositionInteractions, - ); - event.finalize(); - expect(event.toJSON()).toEqual({ - xdm: expectedXdm, - }); - }); - - it("shouldn't be called when clickStorage and interactionStorage are empty", () => { - collectInteractions.and.returnValue({ decisionsMeta: [] }); - collectClicks.and.returnValue({ decisionsMeta: [] }); - - const handleOnClick = createOnClickHandler({ - mergeDecisionsMeta, - collectInteractions, - collectClicks, - getInteractionMetas, - getClickMetas, - getClickSelectors, - autoCollectPropositionInteractions, - }); - const clickedElement = createNode("div", { class: "clicked-element" }); - - handleOnClick({ event, clickedElement }); - - expect(event.mergeXdm).not.toHaveBeenCalled(); - }); - - it("for interactions, adds a viewName to the response", () => { - collectClicks.and.returnValue({ decisionsMeta: [] }); - collectInteractions.and.returnValue({ - decisionsMeta, - viewName: "myview", - }); - const handleOnClick = createOnClickHandler({ - mergeDecisionsMeta, - collectInteractions, - collectClicks, - getInteractionMetas, - getClickMetas, - getClickSelectors, - autoCollectPropositionInteractions, - }); - const clickedElement = createNode("div", { class: "clicked-element" }); - - handleOnClick({ event, clickedElement }); - - const expectedXdm = { - eventType: "decisioning.propositionInteract", - web: { - webPageDetails: { - viewName: "myview", - }, - }, - _experience: { - decisioning: { - propositions: [ - { - id: 1, - scope: "foo", - }, - ], - propositionEventType: { - interact: 1, - }, - }, - }, - }; - - expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); - expect(collectInteractions).toHaveBeenCalledWith( - clickedElement, - getInteractionMetas, - autoCollectPropositionInteractions, - ); - event.finalize(); - expect(event.toJSON()).toEqual({ - xdm: expectedXdm, - }); - }); - - it("for clicks, adds a viewName to the response", () => { - const selectors = ["foo", "foo2"]; - collectInteractions.and.returnValue({ decisionsMeta: [] }); - collectClicks.and.returnValue({ - decisionsMeta, - viewName: "myview", - }); - getClickSelectors.and.returnValue(selectors); - const handleOnClick = createOnClickHandler({ - mergeDecisionsMeta, - collectInteractions, - collectClicks, - getInteractionMetas, - getClickMetas, - getClickSelectors, - autoCollectPropositionInteractions, - }); - const clickedElement = "foo"; - - handleOnClick({ event, clickedElement }); - - const expectedXdm = { - eventType: "decisioning.propositionInteract", - web: { - webPageDetails: { - viewName: "myview", - }, - }, - _experience: { - decisioning: { - propositions: [ - { - id: 1, - scope: "foo", - }, - ], - propositionEventType: { - interact: 1, - }, - }, - }, - }; - - expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); - expect(collectClicks).toHaveBeenCalledWith( - clickedElement, - selectors, - getClickMetas, - ); - event.finalize(); - expect(event.toJSON()).toEqual({ - xdm: expectedXdm, - }); - }); - - it("gets metas for both click and interact", () => { - collectInteractions.and.returnValue({ - decisionsMeta, - }); - - collectClicks.and.returnValue({ - decisionsMeta: decisionsMeta2, - }); - - const handleOnClick = createOnClickHandler({ - mergeDecisionsMeta, - collectInteractions, - collectClicks, - getInteractionMetas, - getClickMetas, - getClickSelectors, - autoCollectPropositionInteractions, - }); - const clickedElement = createNode("div", { class: "clicked-element" }); - - handleOnClick({ event, clickedElement }); - - expect(collectInteractions).toHaveBeenCalled(); - expect(collectClicks).toHaveBeenCalled(); - - const expectedXdm = { - eventType: "decisioning.propositionInteract", - _experience: { - decisioning: { - propositions: [ - { - id: 1, - scope: "foo", - }, - { - id: 2, - scope: "bar", - }, - ], - propositionEventType: { - interact: 1, - }, - }, - }, - }; - - expect(event.mergeXdm).toHaveBeenCalledWith(expectedXdm); - expect(collectInteractions).toHaveBeenCalledWith( - clickedElement, - getInteractionMetas, - autoCollectPropositionInteractions, - ); - event.finalize(); - expect(event.toJSON()).toEqual({ - xdm: expectedXdm, - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/createOnDecisionHandler.spec.js b/test/unit/specs/components/Personalization/createOnDecisionHandler.spec.js deleted file mode 100644 index ca4d37308..000000000 --- a/test/unit/specs/components/Personalization/createOnDecisionHandler.spec.js +++ /dev/null @@ -1,263 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createOnDecisionHandler from "../../../../../src/components/Personalization/createOnDecisionHandler.js"; -import { MESSAGE_CONTENT_CARD } from "../../../../../src/constants/schema.js"; -import injectCreateProposition from "../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; -import createNotificationHandler from "../../../../../src/components/Personalization/createNotificationHandler.js"; - -describe("Personalization::createOnDecisionHandler", () => { - const PROPOSITIONS = [ - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - qualifiedDate: 1683042673387, - displayedDate: 1683042673395, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - qualifiedDate: 1683042673387, - displayedDate: 1683042673395, - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - scope: "web://mywebsite.com/my-cards", - }, - { - id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", - items: [ - { - schema: MESSAGE_CONTENT_CARD, - data: { - expiryDate: 1712190456, - publishedDate: 1677752640000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/lumon.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "a handshake is available upon request.", - title: "Welcome to Lumon!", - }, - contentType: "application/json", - qualifiedDate: 1683042628064, - displayedDate: 1683042628070, - }, - id: "a48ca420-faea-467e-989a-5d179d9f562d", - }, - { - schema: MESSAGE_CONTENT_CARD, - data: { - expiryDate: 1712190456, - publishedDate: 1677839040000, - - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/achievement.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Great job, you completed your profile.", - title: "Achievement Unlocked!", - }, - contentType: "application/json", - qualifiedDate: 1683042628064, - displayedDate: 1683042628070, - }, - id: "b7173290-588f-40c6-a05c-43ed5ec08b28", - }, - ], - scope: "web://mywebsite.com/my-cards", - }, - { - id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", - items: [ - { - schema: MESSAGE_CONTENT_CARD, - data: { - expiryDate: 1712190456, - publishedDate: 1678098240000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/twitter.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Posting on social media helps us spread the word.", - title: "Thanks for sharing!", - }, - contentType: "application/json", - qualifiedDate: 1683042658312, - displayedDate: 1683042658316, - }, - id: "cfcb1af7-7bc2-45b2-a86a-0aa93fe69ce7", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - eventToken: - "eyJtZXNzYWdlRXhlY3V0aW9uIjp7Im1lc3NhZ2VFeGVjdXRpb25JRCI6Ik5BIiwibWVzc2FnZUlEIjoiMDJjNzdlYTgtN2MwZS00ZDMzLTgwOTAtNGE1YmZkM2Q3NTAzIiwibWVzc2FnZVR5cGUiOiJtYXJrZXRpbmciLCJjYW1wYWlnbklEIjoiMzlhZThkNGItYjU1ZS00M2RjLWExNDMtNzdmNTAxOTViNDg3IiwiY2FtcGFpZ25WZXJzaW9uSUQiOiJiZDg1ZDllOC0yMDM3LTQyMmYtYjZkMi0zOTU3YzkwNTU5ZDMiLCJjYW1wYWlnbkFjdGlvbklEIjoiYjQ3ZmRlOGItNTdjMS00YmJlLWFlMjItNjRkNWI3ODJkMTgzIiwibWVzc2FnZVB1YmxpY2F0aW9uSUQiOiJhZTUyY2VkOC0yMDBjLTQ5N2UtODc4Ny1lZjljZmMxNzgyMTUifSwibWVzc2FnZVByb2ZpbGUiOnsiY2hhbm5lbCI6eyJfaWQiOiJodHRwczovL25zLmFkb2JlLmNvbS94ZG0vY2hhbm5lbHMvd2ViIiwiX3R5cGUiOiJodHRwczovL25zLmFkb2JlLmNvbS94ZG0vY2hhbm5lbC10eXBlcy93ZWIifSwibWVzc2FnZVByb2ZpbGVJRCI6ImY1Y2Q5OTk1LTZiNDQtNDIyMS05YWI3LTViNTMzOGQ1ZjE5MyJ9fQ==", - }, - strategies: [ - { - strategyID: "3VQe3oIqiYq2RAsYzmDTSf", - treatmentID: "yu7rkogezumca7i0i44v", - }, - ], - activity: { - id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", - }, - correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", - }, - }, - }, - { - id: "d1f7d411-a549-47bc-a4d8-c8e638b0a46b", - items: [ - { - schema: MESSAGE_CONTENT_CARD, - data: { - expiryDate: 1712190456, - publishedDate: 1678184640000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/gold-coin.jpg", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Now you're ready to earn!", - title: "Funds deposited!", - }, - contentType: "application/json", - qualifiedDate: 1683042653905, - displayedDate: 1683042653909, - }, - id: "0263e171-fa32-4c7a-9611-36b28137a81d", - }, - ], - scope: "web://mywebsite.com/my-cards", - }, - ]; - let render; - let collect; - let processPropositions; - let createProposition; - let onDecisionHandler; - let renderedPropositions; - let notificationHandler; - - beforeEach(() => { - render = jasmine - .createSpy("render") - .and.returnValue( - Promise.resolve([ - { id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", hi: true }, - ]), - ); - collect = jasmine.createSpy("collect").and.returnValue(Promise.resolve()); - processPropositions = jasmine - .createSpy("processPropositions") - .and.returnValue({ render, returnedPropositions: PROPOSITIONS }); - - createProposition = injectCreateProposition({ - preprocess: (data) => data, - isPageWideSurface: () => false, - }); - - renderedPropositions = jasmine.createSpyObj("renderedPropositions", [ - "concat", - ]); - - notificationHandler = createNotificationHandler( - collect, - renderedPropositions, - ); - - onDecisionHandler = createOnDecisionHandler({ - processPropositions, - createProposition, - notificationHandler, - }); - }); - - it("does not call render if renderDecisions=false", () => { - onDecisionHandler({ - viewName: "blippi", - renderDecisions: false, - propositions: PROPOSITIONS, - }); - - expect(render).not.toHaveBeenCalled(); - }); - - it("calls render if renderDecisions=true", async () => { - const mockEvent = { getViewName: () => "blippi" }; - const { propositions } = await onDecisionHandler({ - event: mockEvent, - personalization: {}, - renderDecisions: true, - propositions: PROPOSITIONS, - }); - - expect(propositions).toEqual(PROPOSITIONS); - - expect(render).toHaveBeenCalledTimes(1); - - expect(collect).toHaveBeenCalledOnceWith({ - decisionsMeta: [{ id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", hi: true }], - viewName: "blippi", - }); - expect(renderedPropositions.concat).not.toHaveBeenCalled(); - }); - - it("defers sending display notification when sendDisplayEvent=false", async () => { - await onDecisionHandler({ - renderDecisions: true, - propositions: PROPOSITIONS, - event: { getViewName: () => "blippi" }, - personalization: { - sendDisplayEvent: false, - }, - }); - - expect(collect).not.toHaveBeenCalled(); - expect(renderedPropositions.concat).toHaveBeenCalledTimes(1); - }); -}); diff --git a/test/unit/specs/components/Personalization/createPersonalizationDetails.spec.js b/test/unit/specs/components/Personalization/createPersonalizationDetails.spec.js deleted file mode 100644 index 0be23a184..000000000 --- a/test/unit/specs/components/Personalization/createPersonalizationDetails.spec.js +++ /dev/null @@ -1,478 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import PAGE_WIDE_SCOPE from "../../../../../src/constants/pageWideScope.js"; -import createPersonalizationDetails from "../../../../../src/components/Personalization/createPersonalizationDetails.js"; -import createGetPageLocation from "../../../../../src/components/Personalization/createGetPageLocation.js"; -import { - DEFAULT_CONTENT_ITEM, - DOM_ACTION, - HTML_CONTENT_ITEM, - MESSAGE_IN_APP, - JSON_CONTENT_ITEM, - REDIRECT_ITEM, - RULESET_ITEM, - MESSAGE_CONTENT_CARD, -} from "../../../../../src/constants/schema.js"; - -describe("Personalization::createPersonalizationDetails", () => { - const TEST_SURFACE = "web://alloy.test.com/test/page/1"; - const window = { - location: { - host: "alloy.test.com", - pathname: "/test/page/1/", - }, - }; - const getPageLocation = createGetPageLocation({ window }); - - let event; - let logger; - - beforeEach(() => { - event = jasmine.createSpyObj("event", ["getViewName"]); - logger = jasmine.createSpyObj("logger", ["info", "warn", "error"]); - }); - - // s - has scopes or surfaces - // i - cache is initialized - // dp - defaultPersonalizationEnabled flag - // fetch - should fetch data - [ - { s: false, i: false, dp: false, fetch: false }, - { s: true, i: false, dp: false, fetch: true }, - { s: false, i: true, dp: false, fetch: false }, - { s: true, i: true, dp: false, fetch: true }, - - { s: false, i: false, dp: true, fetch: true }, - { s: true, i: false, dp: true, fetch: true }, - { s: false, i: true, dp: true, fetch: true }, - { s: true, i: true, dp: true, fetch: true }, - - { s: false, i: false, fetch: true }, - { s: true, i: false, fetch: true }, - { s: false, i: true, fetch: false }, - { s: true, i: true, fetch: true }, - ].forEach(({ s, i, dp, fetch }) => { - it(`should ${fetch ? "" : "not "}fetch data when ${ - s ? "" : "no " - }scopes, the cache is ${ - i ? "" : "not " - }initialized, and initializePersonalization is '${dp}'`, () => { - const personalizationDetails = createPersonalizationDetails({ - getPageLocation, - renderDecisions: true, - decisionScopes: [], - personalization: { - decisionScopes: s ? ["test"] : undefined, - defaultPersonalizationEnabled: dp, - }, - event, - isCacheInitialized: i, - logger, - }); - expect(personalizationDetails.shouldFetchData()).toEqual(fetch); - }); - }); - - it("should fetch data when no cache, renderDecisions is true, no viewName and decisionScopes/surfaces (in non SPA world)", () => { - const decisionScopes = []; - const personalization = {}; - const renderDecisions = true; - event.getViewName.and.returnValue(undefined); - const personalizationDetails = createPersonalizationDetails({ - getPageLocation, - renderDecisions, - decisionScopes, - personalization, - event, - isCacheInitialized: false, - logger, - }); - const expectedDecisionScopes = [PAGE_WIDE_SCOPE]; - const expectedQueryDetails = { - schemas: [ - DEFAULT_CONTENT_ITEM, - HTML_CONTENT_ITEM, - JSON_CONTENT_ITEM, - REDIRECT_ITEM, - RULESET_ITEM, - MESSAGE_IN_APP, - MESSAGE_CONTENT_CARD, - DOM_ACTION, - ], - decisionScopes: expectedDecisionScopes, - surfaces: [TEST_SURFACE], - }; - const queryDetails = personalizationDetails.createQueryDetails(); - - expect(personalizationDetails.isRenderDecisions()).toEqual(true); - expect(personalizationDetails.hasScopes()).toEqual(false); - expect(personalizationDetails.hasSurfaces()).toEqual(false); - expect(queryDetails).toEqual(expectedQueryDetails); - expect(personalizationDetails.getViewName()).toEqual(undefined); - expect(personalizationDetails.shouldFetchData()).toEqual(true); - expect(personalizationDetails.hasViewName()).toEqual(false); - expect(personalizationDetails.shouldUseCachedData()).toEqual(false); - }); - it("should fetch data when no cache, renderDecisions is false, no viewName and decisionScopes/surfaces is empty", () => { - const decisionScopes = []; - const personalization = {}; - const renderDecisions = false; - event.getViewName.and.returnValue(undefined); - const personalizationDetails = createPersonalizationDetails({ - getPageLocation, - renderDecisions, - decisionScopes, - personalization, - event, - isCacheInitialized: false, - logger, - }); - const expectedDecisionScopes = [PAGE_WIDE_SCOPE]; - const expectedQueryDetails = { - schemas: [ - DEFAULT_CONTENT_ITEM, - HTML_CONTENT_ITEM, - JSON_CONTENT_ITEM, - REDIRECT_ITEM, - RULESET_ITEM, - MESSAGE_IN_APP, - MESSAGE_CONTENT_CARD, - DOM_ACTION, - ], - decisionScopes: expectedDecisionScopes, - surfaces: [TEST_SURFACE], - }; - const queryDetails = personalizationDetails.createQueryDetails(); - - expect(personalizationDetails.isRenderDecisions()).toEqual(false); - expect(personalizationDetails.hasScopes()).toEqual(false); - expect(personalizationDetails.hasSurfaces()).toEqual(false); - expect(queryDetails).toEqual(expectedQueryDetails); - expect(personalizationDetails.getViewName()).toEqual(undefined); - expect(personalizationDetails.shouldFetchData()).toEqual(true); - expect(personalizationDetails.hasViewName()).toEqual(false); - expect(personalizationDetails.shouldUseCachedData()).toEqual(false); - }); - it("should fetch data when no cache, renderDecisions is false, no viewName and decisionScopes is not empty", () => { - const decisionScopes = ["test1"]; - const personalization = {}; - const renderDecisions = false; - event.getViewName.and.returnValue(undefined); - const personalizationDetails = createPersonalizationDetails({ - getPageLocation, - renderDecisions, - decisionScopes, - personalization, - event, - isCacheInitialized: false, - logger, - }); - const expectedDecisionScopes = ["test1", "__view__"]; - const expectedQueryDetails = { - schemas: [ - DEFAULT_CONTENT_ITEM, - HTML_CONTENT_ITEM, - JSON_CONTENT_ITEM, - REDIRECT_ITEM, - RULESET_ITEM, - MESSAGE_IN_APP, - MESSAGE_CONTENT_CARD, - DOM_ACTION, - ], - decisionScopes: expectedDecisionScopes, - surfaces: [TEST_SURFACE], - }; - const queryDetails = personalizationDetails.createQueryDetails(); - - expect(personalizationDetails.isRenderDecisions()).toEqual(false); - expect(personalizationDetails.hasScopes()).toEqual(true); - expect(personalizationDetails.hasSurfaces()).toEqual(false); - expect(queryDetails).toEqual(expectedQueryDetails); - expect(personalizationDetails.getViewName()).toEqual(undefined); - expect(personalizationDetails.shouldFetchData()).toEqual(true); - expect(personalizationDetails.hasViewName()).toEqual(false); - expect(personalizationDetails.shouldUseCachedData()).toEqual(false); - }); - it("should fetch data when cache initialized, renderDecisions is false, no viewName, and decisionScopes is not empty", () => { - const decisionScopes = ["test1"]; - const personalization = {}; - const renderDecisions = false; - event.getViewName.and.returnValue(undefined); - const personalizationDetails = createPersonalizationDetails({ - getPageLocation, - renderDecisions, - personalization, - decisionScopes, - event, - isCacheInitialized: true, - logger, - }); - const expectedDecisionScopes = ["test1"]; - const expectedQueryDetails = { - schemas: [ - DEFAULT_CONTENT_ITEM, - HTML_CONTENT_ITEM, - JSON_CONTENT_ITEM, - REDIRECT_ITEM, - RULESET_ITEM, - MESSAGE_IN_APP, - MESSAGE_CONTENT_CARD, - ], - decisionScopes: expectedDecisionScopes, - surfaces: [], - }; - const queryDetails = personalizationDetails.createQueryDetails(); - - expect(personalizationDetails.isRenderDecisions()).toEqual(false); - expect(personalizationDetails.hasScopes()).toEqual(true); - expect(personalizationDetails.hasSurfaces()).toEqual(false); - expect(queryDetails).toEqual(expectedQueryDetails); - expect(personalizationDetails.getViewName()).toEqual(undefined); - expect(personalizationDetails.shouldFetchData()).toEqual(true); - expect(personalizationDetails.hasViewName()).toEqual(false); - expect(personalizationDetails.shouldUseCachedData()).toEqual(false); - }); - it("should fetch data when cache initialized, renderDecisions is false, no viewName, and surfaces is not empty", () => { - const decisionScopes = []; - const personalization = { - surfaces: ["web://test1.com"], - }; - const renderDecisions = false; - event.getViewName.and.returnValue(undefined); - const personalizationDetails = createPersonalizationDetails({ - getPageLocation, - renderDecisions, - decisionScopes, - personalization, - event, - isCacheInitialized: true, - logger, - }); - const expectedDecisionScopes = []; - const expectedQueryDetails = { - schemas: [ - DEFAULT_CONTENT_ITEM, - HTML_CONTENT_ITEM, - JSON_CONTENT_ITEM, - REDIRECT_ITEM, - RULESET_ITEM, - MESSAGE_IN_APP, - MESSAGE_CONTENT_CARD, - ], - decisionScopes: expectedDecisionScopes, - surfaces: ["web://test1.com/"], - }; - const queryDetails = personalizationDetails.createQueryDetails(); - - expect(personalizationDetails.isRenderDecisions()).toEqual(false); - expect(personalizationDetails.hasScopes()).toEqual(false); - expect(personalizationDetails.hasSurfaces()).toEqual(true); - expect(queryDetails).toEqual(expectedQueryDetails); - expect(personalizationDetails.getViewName()).toEqual(undefined); - expect(personalizationDetails.shouldFetchData()).toEqual(true); - expect(personalizationDetails.hasViewName()).toEqual(false); - expect(personalizationDetails.shouldUseCachedData()).toEqual(false); - }); - it("should fetch data when cache initialized, renderDecisions is true and decisionScopes is not empty and viewName exist", () => { - event.getViewName.and.returnValue("cart"); - const decisionScopes = ["test1"]; - const personalization = { - decisionScopes: ["test2"], - surfaces: ["web://test1.com"], - }; - const renderDecisions = true; - const personalizationDetails = createPersonalizationDetails({ - getPageLocation, - renderDecisions, - decisionScopes, - personalization, - event, - isCacheInitialized: true, - logger, - }); - - const expectedDecisionScopes = ["test1", "test2"]; - const expectedQueryDetails = { - schemas: [ - DEFAULT_CONTENT_ITEM, - HTML_CONTENT_ITEM, - JSON_CONTENT_ITEM, - REDIRECT_ITEM, - RULESET_ITEM, - MESSAGE_IN_APP, - MESSAGE_CONTENT_CARD, - ], - decisionScopes: expectedDecisionScopes, - surfaces: ["web://test1.com/"], - }; - const queryDetails = personalizationDetails.createQueryDetails(); - - expect(personalizationDetails.isRenderDecisions()).toEqual(true); - expect(personalizationDetails.hasScopes()).toEqual(true); - expect(personalizationDetails.hasSurfaces()).toEqual(true); - expect(queryDetails).toEqual(expectedQueryDetails); - expect(personalizationDetails.getViewName()).toEqual("cart"); - expect(personalizationDetails.shouldFetchData()).toEqual(true); - expect(personalizationDetails.hasViewName()).toEqual(true); - }); - it("should do nothing when cache is initialized, renderDecisions true, no viewName and decisionScopes is empty", () => { - event.getViewName.and.returnValue(undefined); - const decisionScopes = []; - const personalization = {}; - const renderDecisions = true; - const personalizationDetails = createPersonalizationDetails({ - getPageLocation, - renderDecisions, - decisionScopes, - personalization, - event, - isCacheInitialized: true, - logger, - }); - - expect(personalizationDetails.isRenderDecisions()).toEqual(true); - expect(personalizationDetails.hasScopes()).toEqual(false); - expect(personalizationDetails.hasSurfaces()).toEqual(false); - expect(personalizationDetails.getViewName()).toEqual(undefined); - expect(personalizationDetails.shouldFetchData()).toEqual(false); - expect(personalizationDetails.hasViewName()).toEqual(false); - expect(personalizationDetails.shouldUseCachedData()).toEqual(false); - }); - it("should do nothing when cache is initialized, renderDecisions false, no viewName and decisionScopes is empty", () => { - event.getViewName.and.returnValue(undefined); - const decisionScopes = []; - const personalization = {}; - const renderDecisions = false; - const personalizationDetails = createPersonalizationDetails({ - getPageLocation, - renderDecisions, - decisionScopes, - personalization, - event, - isCacheInitialized: true, - logger, - }); - - expect(personalizationDetails.isRenderDecisions()).toEqual(false); - expect(personalizationDetails.hasScopes()).toEqual(false); - expect(personalizationDetails.hasSurfaces()).toEqual(false); - expect(personalizationDetails.getViewName()).toEqual(undefined); - expect(personalizationDetails.shouldFetchData()).toEqual(false); - expect(personalizationDetails.hasViewName()).toEqual(false); - expect(personalizationDetails.shouldUseCachedData()).toEqual(false); - }); - it("should use cache when cache initialized, renderDecisions is true and decisionScopes is empty and viewName exist", () => { - event.getViewName.and.returnValue("cart"); - const decisionScopes = []; - const personalization = {}; - const renderDecisions = true; - const personalizationDetails = createPersonalizationDetails({ - getPageLocation, - renderDecisions, - personalization, - decisionScopes, - event, - isCacheInitialized: true, - logger, - }); - - expect(personalizationDetails.isRenderDecisions()).toEqual(true); - expect(personalizationDetails.hasScopes()).toEqual(false); - expect(personalizationDetails.hasSurfaces()).toEqual(false); - expect(personalizationDetails.getViewName()).toEqual("cart"); - expect(personalizationDetails.hasViewName()).toEqual(true); - expect(personalizationDetails.shouldFetchData()).toEqual(false); - expect(personalizationDetails.shouldUseCachedData()).toEqual(true); - }); - it("should use cache when cache initialized, renderDecisions is false and decisionScopes is empty and viewName exist", () => { - event.getViewName.and.returnValue("cart"); - const decisionScopes = []; - const personalization = {}; - const renderDecisions = false; - const personalizationDetails = createPersonalizationDetails({ - getPageLocation, - renderDecisions, - personalization, - decisionScopes, - event, - isCacheInitialized: true, - logger, - }); - - expect(personalizationDetails.isRenderDecisions()).toEqual(false); - expect(personalizationDetails.hasScopes()).toEqual(false); - expect(personalizationDetails.hasSurfaces()).toEqual(false); - expect(personalizationDetails.getViewName()).toEqual("cart"); - expect(personalizationDetails.hasViewName()).toEqual(true); - expect(personalizationDetails.shouldFetchData()).toEqual(false); - expect(personalizationDetails.shouldUseCachedData()).toEqual(true); - }); - it("should fetch data when cache initialized, renderDecisions is true and decisionScopes has __view__ and viewName exist", () => { - event.getViewName.and.returnValue("cart"); - const decisionScopes = ["__view__"]; - const personalization = { - surfaces: [TEST_SURFACE], - }; - const renderDecisions = true; - const personalizationDetails = createPersonalizationDetails({ - getPageLocation, - renderDecisions, - decisionScopes, - personalization, - event, - isCacheInitialized: true, - logger, - }); - - const expectedDecisionScopes = ["__view__"]; - const expectedQueryDetails = { - schemas: [ - DEFAULT_CONTENT_ITEM, - HTML_CONTENT_ITEM, - JSON_CONTENT_ITEM, - REDIRECT_ITEM, - RULESET_ITEM, - MESSAGE_IN_APP, - MESSAGE_CONTENT_CARD, - DOM_ACTION, - ], - decisionScopes: expectedDecisionScopes, - surfaces: [TEST_SURFACE], - }; - const queryDetails = personalizationDetails.createQueryDetails(); - - expect(personalizationDetails.isRenderDecisions()).toEqual(true); - expect(personalizationDetails.hasScopes()).toEqual(true); - expect(personalizationDetails.hasSurfaces()).toEqual(true); - expect(queryDetails).toEqual(expectedQueryDetails); - expect(personalizationDetails.getViewName()).toEqual("cart"); - expect(personalizationDetails.shouldFetchData()).toEqual(true); - expect(personalizationDetails.hasViewName()).toEqual(true); - }); - it("hasViewName should return false when viewName is empty", () => { - const decisionScopes = []; - const personalization = {}; - const renderDecisions = true; - event.getViewName.and.returnValue(""); - const personalizationDetails = createPersonalizationDetails({ - getPageLocation, - renderDecisions, - decisionScopes, - personalization, - event, - isCacheInitialized: false, - logger, - }); - expect(personalizationDetails.isRenderDecisions()).toEqual(true); - expect(personalizationDetails.hasViewName()).toEqual(false); - }); -}); diff --git a/test/unit/specs/components/Personalization/createSetTargetMigration.spec.js b/test/unit/specs/components/Personalization/createSetTargetMigration.spec.js deleted file mode 100644 index deafa7b15..000000000 --- a/test/unit/specs/components/Personalization/createSetTargetMigration.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createSetTargetMigration from "../../../../../src/components/Personalization/createSetTargetMigration.js"; - -describe("Personalization::createSetTargetMigration", () => { - let request; - let payload; - - beforeEach(() => { - request = jasmine.createSpyObj("request", ["getPayload"]); - payload = jasmine.createSpyObj("payload", ["mergeMeta"]); - request.getPayload.and.returnValue(payload); - }); - - it("adds to request meta if targetMigrationEnabled=true is configured", () => { - const setTargetMigration = createSetTargetMigration({ - targetMigrationEnabled: true, - }); - setTargetMigration(request); - expect(payload.mergeMeta).toHaveBeenCalledOnceWith({ - target: { migration: true }, - }); - }); - - it("does not add to request meta if targetMigrationEnabled is not configured", () => { - const setTargetMigration = createSetTargetMigration({ - targetMigrationEnabled: false, - }); - setTargetMigration(request); - expect(payload.mergeMeta).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Personalization/createViewCacheManager.spec.js b/test/unit/specs/components/Personalization/createViewCacheManager.spec.js deleted file mode 100644 index 75e8cfe15..000000000 --- a/test/unit/specs/components/Personalization/createViewCacheManager.spec.js +++ /dev/null @@ -1,209 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { DEFAULT_CONTENT_ITEM } from "../../../../../src/constants/schema.js"; -import createViewCacheManager from "../../../../../src/components/Personalization/createViewCacheManager.js"; -import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; - -const propsToJSON = (props) => props.map((p) => p.toJSON()); - -describe("Personalization::createViewCacheManager", () => { - const viewHandles = [ - { - id: "foo1", - scope: "home", - }, - { - id: "foo2", - scope: "home", - }, - { - id: "foo3", - scope: "cart", - }, - { - id: "foo4", - scope: "other", - }, - ]; - - let createProposition; - let propositions; - - beforeEach(() => { - createProposition = (viewHandle) => { - const { scope } = viewHandle; - return { - getScope() { - return scope; - }, - toJSON() { - return viewHandle; - }, - }; - }; - propositions = viewHandles.map(createProposition); - }); - - it("stores and gets the decisions based on a viewName", async () => { - const viewCacheManager = createViewCacheManager({ createProposition }); - - const cacheUpdate = viewCacheManager.createCacheUpdate("home"); - const resultingHandles = cacheUpdate.update(propositions); - expect(resultingHandles).toEqual([propositions[0], propositions[1]]); - - const homeViews = await viewCacheManager.getView("home"); - expect(homeViews).toEqual([propositions[0], propositions[1]]); - - const cartViews = await viewCacheManager.getView("cart"); - expect(cartViews).toEqual([propositions[2]]); - - const otherViews = await viewCacheManager.getView("other"); - expect(otherViews).toEqual([propositions[3]]); - }); - - it("should be no views when decisions deferred is rejected", async () => { - const viewCacheManager = createViewCacheManager({ createProposition }); - const cacheUpdate = viewCacheManager.createCacheUpdate("home"); - cacheUpdate.cancel(); - - const homeViews = await viewCacheManager.getView("home"); - expect(homeViews.map((h) => h.toJSON())).toEqual([ - { - scope: "home", - scopeDetails: { - characteristics: { - scopeType: "view", - }, - }, - items: [ - { - schema: DEFAULT_CONTENT_ITEM, - }, - ], - }, - ]); - }); - - it("should not be initialized when first created", () => { - const viewCacheManager = createViewCacheManager({ createProposition }); - expect(viewCacheManager.isInitialized()).toBe(false); - }); - - it("should be initialized when first cache update is created", () => { - const viewCacheManager = createViewCacheManager({ createProposition }); - viewCacheManager.createCacheUpdate("home"); - expect(viewCacheManager.isInitialized()).toBe(true); - }); - - it("allows you to store the views multiple times", async () => { - const viewCacheManager = createViewCacheManager({ createProposition }); - - const cacheUpdate1 = viewCacheManager.createCacheUpdate("cart"); - const cartProps = await cacheUpdate1.update(propositions); - - expect(cartProps).toEqual([propositions[2]]); - - const cacheUpdate2 = viewCacheManager.createCacheUpdate(); - const cartViewPromise = viewCacheManager.getView("cart"); - await flushPromiseChains(); - await expectAsync(cartViewPromise).toBePending(); - cacheUpdate2.update([ - createProposition({ - id: "foo4", - items: [], - scope: "about", - }), - ]); - - expect(await cartViewPromise).toEqual([propositions[2]]); - expect(await viewCacheManager.getView("about").then(propsToJSON)).toEqual([ - { - id: "foo4", - items: [], - scope: "about", - }, - ]); - }); - - it("is initialized after the first storeViews call", () => { - const viewCacheManager = createViewCacheManager({ createProposition }); - - viewCacheManager.createCacheUpdate(); - - expect(viewCacheManager.isInitialized()).toBeTrue(); - }); - - it("is initialized even after a failure", async () => { - const viewCacheManager = createViewCacheManager({ createProposition }); - - const update1 = viewCacheManager.createCacheUpdate(); - update1.cancel(); - await flushPromiseChains(); - - expect(viewCacheManager.isInitialized()).toBeTrue(); - }); - - it("reverts to old storage after a failure", async () => { - const viewCacheManager = createViewCacheManager({ createProposition }); - const update1 = viewCacheManager.createCacheUpdate(); - update1.update(propositions); - const update2 = viewCacheManager.createCacheUpdate(); - update2.cancel(); - expect(await viewCacheManager.getView("home")).toEqual([ - propositions[0], - propositions[1], - ]); - }); - /* - it("applies the decisions in the order they were requested", async () => { - const viewCacheManager = createViewCacheManager({ createProposition }); - - const update1 = viewCacheManager.createCacheUpdate(); - const viewPromise1 = viewCacheManager.getView(cartView).then(props => props.map(p => p.toJSON())); - const update2 = viewCacheManager.createCacheUpdate(); - const viewPromise2 = viewCacheManager.getView(cartView).then(props => props.map(p => p.toJSON())); - - update2.update([ - createProposition({ - id: "foo4", - items: [], - scope: "cart" - }) - ]); - - update1.update(viewHandles); - - await expectAsync(viewPromise2).toBeResolvedTo([ - { - id: "foo4", - items: [], - scope: "cart" - } - ]); - await expectAsync(viewPromise1).toBeResolvedTo([ - { - id: "foo3", - items: [], - scope: "cart" - } - ]); - await expectAsync(viewCacheManager.getView(cartView)).toBeResolvedTo([ - { - id: "foo4", - items: [], - scope: "cart" - } - ]); - }); - */ -}); diff --git a/test/unit/specs/components/Personalization/createViewChangeHandler.spec.js b/test/unit/specs/components/Personalization/createViewChangeHandler.spec.js deleted file mode 100644 index b06cc1347..000000000 --- a/test/unit/specs/components/Personalization/createViewChangeHandler.spec.js +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createViewChangeHandler from "../../../../../src/components/Personalization/createViewChangeHandler.js"; -import { CART_VIEW_DECISIONS } from "./responsesMock/eventResponses.js"; -import injectCreateProposition from "../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; - -describe("Personalization::createViewChangeHandler", () => { - let processPropositions; - let viewCache; - - let personalizationDetails; - let event; - let onResponse; - let logger; - let createProposition; - - beforeEach(() => { - logger = jasmine.createSpyObj("logger", ["logOnContentRendering"]); - processPropositions = jasmine.createSpy("processPropositions"); - viewCache = jasmine.createSpyObj("viewCache", ["getView"]); - - personalizationDetails = jasmine.createSpyObj("personalizationDetails", [ - "isRenderDecisions", - "getViewName", - ]); - event = "myevent"; - onResponse = jasmine.createSpy(); - - createProposition = injectCreateProposition({ - preprocess: (data) => data, - isPageWideSurface: () => false, - }); - }); - - const run = async () => { - const viewChangeHandler = createViewChangeHandler({ - logger, - processPropositions, - viewCache, - }); - const decisionsMeta = await viewChangeHandler({ - event, - personalizationDetails, - onResponse, - }); - const result = onResponse.calls.argsFor(0)[0](); - return { decisionsMeta, result }; - }; - - it("should trigger render if renderDecisions is true", async () => { - viewCache.getView.and.returnValue( - Promise.resolve(CART_VIEW_DECISIONS.map((p) => createProposition(p))), - ); - personalizationDetails.isRenderDecisions.and.returnValue(true); - personalizationDetails.getViewName.and.returnValue("cart"); - processPropositions.and.returnValue({ - render: () => Promise.resolve("decisionMeta"), - returnedPropositions: [], - returnedDecisions: CART_VIEW_DECISIONS, - }); - - const { decisionsMeta, result } = await run(); - - expect(logger.logOnContentRendering).toHaveBeenCalledTimes(1); - expect(processPropositions).toHaveBeenCalledTimes(1); - expect(decisionsMeta).toEqual("decisionMeta"); - - expect(result.decisions).toEqual(CART_VIEW_DECISIONS); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/action.spec.js b/test/unit/specs/components/Personalization/dom-actions/action.spec.js deleted file mode 100644 index 26ef92385..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/action.spec.js +++ /dev/null @@ -1,11 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ diff --git a/test/unit/specs/components/Personalization/dom-actions/addNonceToInlineStyleElements.spec.js b/test/unit/specs/components/Personalization/dom-actions/addNonceToInlineStyleElements.spec.js deleted file mode 100644 index 5f8b89ce8..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/addNonceToInlineStyleElements.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import addNonceToInlineStyleElements from "../../../../../../src/components/Personalization/dom-actions/addNonceToInlineStyleElements.js"; -import { testResetCachedNonce } from "../../../../../../src/components/Personalization/dom-actions/dom/getNonce.js"; -import { createFragment } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import { STYLE } from "../../../../../../src/constants/tagName.js"; -import { - selectNodes, - removeNode, - appendNode, - createNode, -} from "../../../../../../src/utils/dom/index.js"; - -describe("Personalization::dom-actions::addNonceToInlineStyleElements", () => { - afterEach(() => { - selectNodes("#fooById").forEach(removeNode); - }); - - it("should add nonce to inline style elements if available", () => { - testResetCachedNonce(); - // Make sure a nonce is available to alloy - appendNode( - document.head, - createNode("script", { id: "fooById", nonce: "123" }), - ); - const fragmentHtml = ""; - const fragment = createFragment(fragmentHtml); - addNonceToInlineStyleElements(fragment); - const styleNodes = selectNodes(STYLE, fragment); - expect(styleNodes[0].nonce).toEqual("123"); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js b/test/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js deleted file mode 100644 index 1749641e0..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, - selectNodes, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_APPEND_HTML } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::appendHtml", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("appendHtml"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_APPEND_HTML, - }); - }); - - afterEach(() => { - cleanUpDomChanges("appendHtml"); - }); - - it("should append personalized content", () => { - const modules = initDomActionsModules(); - const { appendHtml } = modules; - const element = createNode( - "ul", - { id: "appendHtml" }, - { innerHTML: "
  • 1
  • " }, - ); - - appendNode(document.body, element); - - const settings = { - selector: "#appendHtml", - prehidingSelector: "#appendHtml", - content: `
  • 2
  • 3
  • `, - meta: { a: 1 }, - }; - - return appendHtml(settings, decorateProposition).then(() => { - const result = selectNodes("ul#appendHtml li"); - - expect(result.length).toEqual(3); - expect(result[0].innerHTML).toEqual("1"); - expect(result[1].innerHTML).toEqual("2"); - expect(result[2].innerHTML).toEqual("3"); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js b/test/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js deleted file mode 100644 index 3e02bafe4..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js +++ /dev/null @@ -1,158 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - createNode, - appendNode, - selectNodes, - removeNode, -} from "../../../../../../../src/utils/dom/index.js"; -import collectClicks from "../../../../../../../src/components/Personalization/dom-actions/clicks/collectClicks.js"; - -describe("Personalization::tracking::clicks", () => { - afterEach(() => { - selectNodes(".eq").forEach(removeNode); - }); - - it("should collect clicks", () => { - const meta = [ - { - id: "AT:1234", - scope: "example_scope", - }, - ]; - const getClickMetas = jasmine - .createSpy("getClickMetas") - .and.returnValue(meta); - const content = ` -
    -
    first
    - -
    second
    - -
    third
    -
    - `; - const node = createNode( - "DIV", - { id: "abc", class: "eq" }, - { innerHTML: content }, - ); - - appendNode(document.body, node); - - const selectors = ["#abc:eq(0) > div.b:eq(0) > div.c"]; - - const element = document.getElementById("one"); - const { decisionsMeta, propositionActionLabel } = collectClicks( - element, - selectors, - getClickMetas, - ); - - expect(decisionsMeta).toEqual(meta); - expect(propositionActionLabel).toEqual(""); - }); - - it("should collect and dedupe clicks with labels", () => { - const metaOuter = [ - { - id: "AT:outer-id-1", - scope: "outer-scope1", - }, - { - id: "AJO:inner-id-2", - scope: "inner-scope2", - trackingLabel: "outer-label-2", - }, - { - id: "AJO:outer-id-3", - scope: "outer-scope3", - trackingLabel: "outer-label-3", - }, - ]; - const metaInner = [ - { - id: "AT:inner-id-1", - scope: "inner-scope1", - }, - { - id: "AJO:inner-id-2", - scope: "inner-scope2", - trackingLabel: "inner-label-2", - }, - { - id: "AJO:inner-id-3", - scope: "inner-scope3", - trackingLabel: "inner-label-3", - }, - ]; - const getClickMetas = jasmine - .createSpy("getClickMetas") - .withArgs("#abc:eq(0) > div.b:eq(0)") - .and.returnValue(metaOuter) - .withArgs("#abc:eq(0) > div.b:eq(0) > div.c") - .and.returnValue(metaInner); - const content = ` -
    -
    first
    - -
    second
    - -
    third
    -
    - `; - const node = createNode( - "DIV", - { id: "abc", class: "eq" }, - { innerHTML: content }, - ); - - appendNode(document.body, node); - - const selectors = [ - "#abc:eq(0) > div.b:eq(0)", - "#abc:eq(0) > div.b:eq(0) > div.c", - ]; - - const element = document.getElementById("one"); - const { decisionsMeta, propositionActionLabel } = collectClicks( - element, - selectors, - getClickMetas, - ); - - expect(decisionsMeta).toEqual([ - { - id: "AT:outer-id-1", - scope: "outer-scope1", - }, - { - id: "AJO:inner-id-2", - scope: "inner-scope2", - }, - { - id: "AJO:outer-id-3", - scope: "outer-scope3", - }, - { - id: "AT:inner-id-1", - scope: "inner-scope1", - }, - { - id: "AJO:inner-id-3", - scope: "inner-scope3", - }, - ]); - expect(propositionActionLabel).toEqual("inner-label-2"); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/clicks/collectInteractions.spec.js b/test/unit/specs/components/Personalization/dom-actions/clicks/collectInteractions.spec.js deleted file mode 100644 index 72da21f68..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/clicks/collectInteractions.spec.js +++ /dev/null @@ -1,615 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createInteractionStorage from "../../../../../../../src/components/Personalization/createInteractionStorage.js"; -import { - appendNode, - createNode, - removeNode, - selectNodes, -} from "../../../../../../../src/utils/dom/index.js"; -import collectInteractions from "../../../../../../../src/components/Personalization/dom-actions/clicks/collectInteractions.js"; -import { - ADOBE_JOURNEY_OPTIMIZER, - ADOBE_TARGET, -} from "../../../../../../../src/constants/decisionProvider.js"; -import { - ALWAYS, - DECORATED_ELEMENTS_ONLY, - NEVER, -} from "../../../../../../../src/constants/propositionInteractionType.js"; - -describe("Personalization::tracking::interactions", () => { - let storeInteractionMeta; - let getInteractionMetas; - let autoCollectPropositionInteractions; - let scopeDetails; - - beforeEach(() => { - selectNodes(".eq").forEach(removeNode); - ({ storeInteractionMeta, getInteractionMetas } = - createInteractionStorage()); - - autoCollectPropositionInteractions = { - [ADOBE_JOURNEY_OPTIMIZER]: ALWAYS, - [ADOBE_TARGET]: NEVER, - }; - - scopeDetails = { decisionProvider: ADOBE_JOURNEY_OPTIMIZER }; - }); - - afterEach(() => { - selectNodes(".eq").forEach(removeNode); - }); - - it("should collect interactions with interact-id, label and token", () => { - storeInteractionMeta( - "AT:1234", - "063", - "page", - { - id: "AT:1234", - scope: "example_scope", - scopeDetails, - }, - 99, - ); - - const content = ` -
    -
    first
    -
    second
    -
    third
    -
    - `; - const node = createNode( - "DIV", - { id: "abc", class: "eq" }, - { innerHTML: content }, - ); - - appendNode(document.body, node); - - [ - { - elementId: "one", - expectedLabel: "lbl-first", - expectedToken: "tok-111", - }, - { - elementId: "two", - expectedLabel: "lbl-second", - expectedToken: "tok-222", - }, - { - elementId: "three", - expectedLabel: "lbl-third", - expectedToken: "tok-333", - }, - ].forEach((definition) => { - const element = document.getElementById(definition.elementId); - const { decisionsMeta, propositionActionLabel, propositionActionToken } = - collectInteractions( - element, - getInteractionMetas, - autoCollectPropositionInteractions, - ); - - expect(decisionsMeta).toEqual([ - { - id: "AT:1234", - scope: "example_scope", - scopeDetails, - items: [ - { - id: "063", - }, - ], - }, - ]); - expect(propositionActionLabel).toEqual(definition.expectedLabel); - expect(propositionActionToken).toEqual(definition.expectedToken); - }); - }); - - it("should collect interactions for elements decorated with label when configured for 'decoratedElementsOnly'", () => { - autoCollectPropositionInteractions = { - [ADOBE_JOURNEY_OPTIMIZER]: DECORATED_ELEMENTS_ONLY, - [ADOBE_TARGET]: NEVER, - }; - - storeInteractionMeta( - "AT:1234", - "063", - "page", - { - id: "AT:1234", - scope: "example_scope", - scopeDetails, - }, - 99, - ); - - const content = ` -
    -
    first
    -
    second
    -
    third
    -
    - `; - const node = createNode( - "DIV", - { id: "abc", class: "eq" }, - { innerHTML: content }, - ); - - appendNode(document.body, node); - - [ - { - elementId: "one", - expectedLabel: "lbl-first", - expectedToken: "tok-111", - }, - { - elementId: "two", - expectedLabel: "lbl-second", - expectedToken: "tok-222", - }, - { - elementId: "three", - expectedLabel: "lbl-third", - expectedToken: "tok-333", - }, - ].forEach((definition) => { - const element = document.getElementById(definition.elementId); - const { decisionsMeta, propositionActionLabel, propositionActionToken } = - collectInteractions( - element, - getInteractionMetas, - autoCollectPropositionInteractions, - ); - - expect(decisionsMeta).toEqual([ - { - id: "AT:1234", - scope: "example_scope", - scopeDetails, - items: [ - { - id: "063", - }, - ], - }, - ]); - expect(propositionActionLabel).toEqual(definition.expectedLabel); - expect(propositionActionToken).toEqual(definition.expectedToken); - }); - }); - - it("should NOT collect interactions for elements NOT decorated with label when configured for 'decoratedElementsOnly'", () => { - autoCollectPropositionInteractions = { - [ADOBE_JOURNEY_OPTIMIZER]: DECORATED_ELEMENTS_ONLY, - [ADOBE_TARGET]: NEVER, - }; - - storeInteractionMeta( - "AT:1234", - "063", - "page", - { - id: "AT:1234", - scope: "example_scope", - scopeDetails, - }, - 99, - ); - - const content = ` -
    -
    first
    -
    second
    -
    third
    -
    - `; - const node = createNode( - "DIV", - { id: "abc", class: "eq" }, - { innerHTML: content }, - ); - - appendNode(document.body, node); - - [ - { - elementId: "one", - expectedLabel: undefined, - expectedToken: undefined, - }, - { - elementId: "two", - expectedLabel: undefined, - expectedToken: undefined, - }, - { - elementId: "three", - expectedLabel: undefined, - expectedToken: undefined, - }, - ].forEach((definition) => { - const element = document.getElementById(definition.elementId); - const { decisionsMeta, propositionActionLabel, propositionActionToken } = - collectInteractions( - element, - getInteractionMetas, - autoCollectPropositionInteractions, - ); - - expect(decisionsMeta).toEqual([]); - expect(propositionActionLabel).toBeNull(); - expect(propositionActionToken).toBeNull(); - }); - }); - - it("should find closest label and token", () => { - storeInteractionMeta( - "AT:1234", - "063", - "page", - { - id: "AT:1234", - scope: "example_scope", - scopeDetails, - }, - 99, - ); - - const content = ` -
    -
    first
    -
    second
    -
    third
    -
    - `; - const node = createNode( - "DIV", - { id: "abc", class: "eq" }, - { innerHTML: content }, - ); - appendNode(document.body, node); - - [ - { - elementId: "one", - expectedLabel: "lbl-main", - expectedToken: "tok-main", - }, - { - elementId: "two", - expectedLabel: "lbl-main", - expectedToken: "tok-main", - }, - { - elementId: "three", - expectedLabel: "lbl-third", - expectedToken: "tok-333", - }, - ].forEach((definition) => { - const element = document.getElementById(definition.elementId); - const { decisionsMeta, propositionActionLabel, propositionActionToken } = - collectInteractions( - element, - getInteractionMetas, - autoCollectPropositionInteractions, - ); - - expect(decisionsMeta).toEqual([ - { - id: "AT:1234", - scope: "example_scope", - scopeDetails, - items: [ - { - id: "063", - }, - ], - }, - ]); - expect(propositionActionLabel).toEqual(definition.expectedLabel); - expect(propositionActionToken).toEqual(definition.expectedToken); - }); - }); - - it("should find closest label and token (nesting)", () => { - storeInteractionMeta( - "AT:1234", - "063", - "page", - { - id: "AT:1234", - scope: "example_scope", - scopeDetails, - }, - 99, - ); - - const content = ` -
    -
    -
    -
    -
    -
    -
    -
    -
    - `; - const node = createNode( - "DIV", - { id: "abc", class: "eq" }, - { innerHTML: content }, - ); - appendNode(document.body, node); - - [ - { - elementId: "onegreatgrandchild", - expectedLabel: "lbl-onegrandchild", - expectedToken: "tok-onegreatgrandchild", - }, - { - elementId: "onegrandchild", - expectedLabel: "lbl-onegrandchild", - expectedToken: null, - }, - { - elementId: "onechild", - expectedLabel: null, - expectedToken: null, - }, - ].forEach((definition) => { - const element = document.getElementById(definition.elementId); - const { decisionsMeta, propositionActionLabel, propositionActionToken } = - collectInteractions( - element, - getInteractionMetas, - autoCollectPropositionInteractions, - ); - - expect(decisionsMeta).toEqual([ - { - id: "AT:1234", - scope: "example_scope", - scopeDetails, - items: [ - { - id: "063", - }, - ], - }, - ]); - expect(propositionActionLabel).toEqual(definition.expectedLabel); - expect(propositionActionToken).toEqual(definition.expectedToken); - }); - }); - - it("handles case where no interact-id exists", () => { - storeInteractionMeta( - "AT:1234", - "063", - "page", - { - id: "AT:1234", - scope: "example_scope", - scopeDetails, - }, - 99, - ); - - const content = ` -
    -
    -
    -
    -
    -
    -
    -
    -
    - `; - const node = createNode( - "DIV", - { id: "abc", class: "eq" }, - { innerHTML: content }, - ); - appendNode(document.body, node); - - const element = document.getElementById("onegreatgrandchild"); - expect( - collectInteractions( - element, - getInteractionMetas, - autoCollectPropositionInteractions, - ), - ).toEqual({}); - }); - - it("should collect and dedupe interactions with labels", () => { - // outer - storeInteractionMeta( - "1", - "p", - "page", - { - id: "AT:outer-id-1", - scope: "outer-scope1", - scopeDetails, - }, - 99, - ); - storeInteractionMeta( - "1", - "a", - "page", - { - id: "AT:outer-id-1", - scope: "outer-scope1", - scopeDetails, - }, - 99, - ); - storeInteractionMeta( - "2", - "b", - "page", - { - id: "AJO:inner-id-2", - scope: "inner-scope2", - scopeDetails, - trackingLabel: "outer-label-2", - }, - 99, - ); - storeInteractionMeta( - "3", - "c", - "page", - { - id: "AJO:outer-id-3", - scope: "outer-scope3", - scopeDetails, - trackingLabel: "outer-label-3", - }, - 99, - ); - - // inner - storeInteractionMeta( - "4", - "d", - "page", - { - id: "AT:inner-id-1", - scope: "inner-scope1", - scopeDetails, - }, - 11, - ); - storeInteractionMeta( - "2", - "b", - "page", - { - id: "AJO:inner-id-2", - scope: "inner-scope2", - scopeDetails, - trackingLabel: "inner-label-2", - }, - 11, - ); - storeInteractionMeta( - "6", - "f", - "page", - { - id: "AJO:inner-id-3", - scope: "inner-scope3", - scopeDetails, - trackingLabel: "inner-label-3", - }, - 11, - ); - - const content = ` -
    -
    first
    -
    second
    -
    third
    -
    - `; - const node = createNode( - "DIV", - { id: "abc", class: "eq" }, - { innerHTML: content }, - ); - - appendNode(document.body, node); - - const element = document.getElementById("one"); - const { decisionsMeta, propositionActionLabel, propositionActionToken } = - collectInteractions( - element, - getInteractionMetas, - autoCollectPropositionInteractions, - ); - - expect(decisionsMeta).toEqual( - jasmine.arrayContaining([ - { - id: "AJO:inner-id-2", - scope: "inner-scope2", - scopeDetails, - items: [ - { - id: "b", - }, - ], - }, - { - id: "AT:inner-id-1", - scope: "inner-scope1", - scopeDetails, - items: [ - { - id: "d", - }, - ], - }, - { - id: "AJO:inner-id-3", - scope: "inner-scope3", - scopeDetails, - items: [ - { - id: "f", - }, - ], - }, - { - id: "AT:outer-id-1", - scope: "outer-scope1", - scopeDetails, - items: [ - { - id: "p", - }, - { - id: "a", - }, - ], - }, - { - id: "AJO:outer-id-3", - scope: "outer-scope3", - scopeDetails, - items: [ - { - id: "c", - }, - ], - }, - ]), - ); - expect(propositionActionLabel).toEqual("inner-label-2"); - expect(propositionActionToken).toEqual("inner-token-2"); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/collectInteractions.spec.js b/test/unit/specs/components/Personalization/dom-actions/collectInteractions.spec.js deleted file mode 100644 index e8ffa7df9..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/collectInteractions.spec.js +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { INTERACT_ID_DATA_ATTRIBUTE } from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_COLLECT_INTERACTIONS } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::collectInteractions", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("something"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_COLLECT_INTERACTIONS, - }); - }); - - afterEach(() => { - cleanUpDomChanges("something"); - }); - - it("should decorate element", async () => { - const itemData = { - isCool: true, - selector: "#something", - }; - - const modules = initDomActionsModules(); - - const element = createNode("div", { id: "something" }); - appendNode(document.body, element); - - await modules[DOM_ACTION_COLLECT_INTERACTIONS]( - itemData, - decorateProposition, - ); - - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/createPreprocess.spec.js b/test/unit/specs/components/Personalization/dom-actions/createPreprocess.spec.js deleted file mode 100644 index bb509dc04..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/createPreprocess.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createPreprocess from "../../../../../../src/components/Personalization/dom-actions/createPreprocess.js"; - -describe("Personalization::dom-actions::createPreprocess", () => { - let preprocessor1; - let preprocessor2; - let preprocess; - beforeEach(() => { - preprocessor1 = jasmine.createSpy("preprocessor1"); - preprocessor2 = jasmine.createSpy("preprocessor2"); - preprocess = createPreprocess([preprocessor1, preprocessor2]); - }); - - it("handles an empty action", () => { - expect(preprocess({})).toEqual({}); - }); - - it("passes the data through", () => { - preprocessor1.and.callFake((data) => data); - preprocessor2.and.callFake((data) => data); - expect(preprocess({ a: 1, b: 2 })).toEqual({ a: 1, b: 2 }); - }); - - it("passes the data through when the preprocessor returns undefined", () => { - preprocessor1.and.callFake(() => undefined); - preprocessor2.and.callFake(() => undefined); - expect(preprocess({ a: 1, b: 2 })).toEqual({ a: 1, b: 2 }); - }); - - it("updates the data", () => { - preprocessor1.and.callFake(() => ({ c: 3 })); - preprocessor2.and.callFake(() => ({ d: 4 })); - expect(preprocess({ a: 1, b: 2 })).toEqual({ a: 1, b: 2, c: 3, d: 4 }); - }); - - it("updates the data2", () => { - preprocessor1.and.callFake((data) => ({ ...data, c: 3 })); - preprocessor2.and.callFake((data) => ({ ...data, d: 4 })); - expect(preprocess({ a: 1, b: 2 })).toEqual({ a: 1, b: 2, c: 3, d: 4 }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js b/test/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js deleted file mode 100644 index 606f5a791..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createRedirect from "../../../../../../src/components/Personalization/dom-actions/createRedirect.js"; - -describe("createRedirect", () => { - it("redirects", () => { - const window = { - location: { - replace: jasmine.createSpy(), - href: jasmine.createSpy(), - }, - }; - const redirect = createRedirect(window); - redirect("myurl"); - expect(window.location.replace).toHaveBeenCalledWith("myurl"); - expect(window.location.href).not.toHaveBeenCalled(); - }); - - it("redirects using window.location.href when preserveHistory is true", () => { - const window = { - location: { - href: jasmine.createSpy(), - replace: jasmine.createSpy(), - }, - }; - const redirectUrl = "https://www.adobe.com"; - const redirect = createRedirect(window); - redirect(redirectUrl, true); - expect(window.location.href).toBe(redirectUrl); - expect(window.location.replace).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/customCode.spec.js b/test/unit/specs/components/Personalization/dom-actions/customCode.spec.js deleted file mode 100644 index 51b1b2718..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/customCode.spec.js +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_CUSTOM_CODE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::customCode", () => { - let decorateProposition; - let customCode; - let element; - - beforeEach(() => { - cleanUpDomChanges("customCode"); - delete window.someEvar123; - - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_CUSTOM_CODE, - }); - - const modules = initDomActionsModules(); - ({ customCode } = modules); - }); - - afterEach(() => { - cleanUpDomChanges("customCode"); - delete window.someEvar123; - }); - - it("should set content in container that has children", async () => { - element = createNode("div", { id: "customCode", class: "customCode" }); - element.innerHTML = `
    `; - appendNode(document.body, element); - - const settings = { - selector: ".customCode", - prehidingSelector: ".customCode", - content: "

    Hola!

    ", - meta: { a: 1 }, - }; - - await customCode(settings, decorateProposition); - expect(element.innerHTML).toMatch( - /

    Hola!<\/p>

    <\/div>
    <\/div>/, - ); - }); - - it("should set content in container that has NO children", async () => { - element = createNode("div", { id: "customCode", class: "customCode" }); - appendNode(document.body, element); - - const settings = { - selector: ".customCode", - prehidingSelector: ".customCode", - content: "

    Hola!

    Hello
    ", - meta: { a: 1 }, - }; - - await customCode(settings, decorateProposition); - expect(element.innerHTML).toMatch( - /

    Hola!<\/p>

    Hello<\/div>/, - ); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/createFragment.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/createFragment.spec.js deleted file mode 100644 index 2e76dd568..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/createFragment.spec.js +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createFragment from "../../../../../../../src/components/Personalization/dom-actions/dom/createFragment.js"; - -describe("Personalization::helper", () => { - it("createFragmentTest", () => { - const result = createFragment(`
    foo
    `); - - expect(result.firstElementChild.id).toEqual("foo"); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/getAttribute.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getAttribute.spec.js deleted file mode 100644 index d7f63cd25..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/getAttribute.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getAttribute from "../../../../../../../src/components/Personalization/dom-actions/dom/getAttribute.js"; -import createFragment from "../../../../../../../src/components/Personalization/dom-actions/dom/createFragment.js"; - -describe("Personalization::helper::dom::getAttribute", () => { - it("returns element's attribute if exists", () => { - const element = createFragment(`
    foo
    `); - const name = "id"; - const result = getAttribute(element.firstElementChild, name); - - expect(result).toEqual("foo"); - }); - - it("returns null if element doesn't have this attribute", () => { - const element = createFragment(`
    foo
    `); - const name = "title"; - const result = getAttribute(element.firstElementChild, name); - - expect(result).toBeNull(); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/getChildNodes.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getChildNodes.spec.js deleted file mode 100644 index 9ec5e87fb..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/getChildNodes.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getChildNodes from "../../../../../../../src/components/Personalization/dom-actions/dom/getChildNodes.js"; -import createFragment from "../../../../../../../src/components/Personalization/dom-actions/dom/createFragment.js"; - -describe("Personalization::helper::dom::getChildNodes", () => { - it("returns an array of child nodes", () => { - const element = createFragment( - `
    foo

    hello there

    `, - ); - const result = getChildNodes(element); - - expect(result.length).toEqual(3); - expect(result[0].tagName).toEqual("DIV"); - expect(result[1].tagName).toEqual("H1"); - expect(result[2].id).toEqual("div2"); - }); - - it("returns undefined when there are no children", () => { - const element = createFragment(); - const result = getChildNodes(element); - - expect(result.length).toEqual(1); - expect(result[0].tagName).toBeUndefined(); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/getChildren.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getChildren.spec.js deleted file mode 100644 index 4f8c62841..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/getChildren.spec.js +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getChildren from "../../../../../../../src/components/Personalization/dom-actions/dom/getChildren.js"; -import createFragment from "../../../../../../../src/components/Personalization/dom-actions/dom/createFragment.js"; - -describe("Personalization::helper::dom::getChildren", () => { - it("returns an array of children", () => { - const element = createFragment( - `
    foo

    hello there

    `, - ); - const result = getChildren(element); - - expect(result.length).toEqual(2); - expect(result[0].tagName).toEqual("DIV"); - expect(result[1].tagName).toEqual("H1"); - }); - - it("returns empty array if there are not children", () => { - const element = createFragment(); - const result = getChildren(element); - - expect(result.length).toEqual(0); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/getElementById.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getElementById.spec.js deleted file mode 100644 index 79fce87d2..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/getElementById.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - selectNodes, - removeNode, - appendNode, - createNode, -} from "../../../../../../../src/utils/dom/index.js"; -import { getElementById } from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; - -describe("Personalization::DOM::getElementById", () => { - afterEach(() => { - selectNodes("#fooById").forEach(removeNode); - }); - - it("should return the node if exists", () => { - appendNode(document.head, createNode("style", { id: "fooById" })); - - expect(getElementById("fooById")).not.toBeNull(); - }); - - it("should return array when nodes are NOT present", () => { - expect(getElementById("fooById")).toBeNull(); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/getFirstChild.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getFirstChild.spec.js deleted file mode 100644 index f8ff3c270..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/getFirstChild.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getFirstChild from "../../../../../../../src/components/Personalization/dom-actions/dom/getFirstChild.js"; -import createFragment from "../../../../../../../src/components/Personalization/dom-actions/dom/createFragment.js"; - -describe("Personalization::helper::dom::getFirstChild", () => { - it("returns the first child node of the element", () => { - const element = createFragment( - `

    hello there

    foo
    `, - ); - const result = getFirstChild(element); - - expect(result.tagName).toEqual("H1"); - }); - - it("returns null if there are no child elements", () => { - const element = createFragment(); - const result = getFirstChild(element); - - expect(result).toBeNull(); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/getNextSibling.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getNextSibling.spec.js deleted file mode 100644 index 307a7bd02..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/getNextSibling.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getNextSibling from "../../../../../../../src/components/Personalization/dom-actions/dom/getNextSibling.js"; -import createFragment from "../../../../../../../src/components/Personalization/dom-actions/dom/createFragment.js"; -import getFirstChild from "../../../../../../../src/components/Personalization/dom-actions/dom/getFirstChild.js"; - -describe("Personalization::helper::dom::getNextSibling", () => { - it("returns the element next sibling", () => { - const element = createFragment( - `
    foo

    hello there

    `, - ); - const firstElement = getFirstChild(element); - const nextSibling = getNextSibling(firstElement); - - expect(nextSibling.tagName).toEqual("H1"); - }); - - it("returns null if the element doesn't have a sibling node", () => { - const element = createFragment(`
    foo
    `); - const firstElement = getFirstChild(element); - const nextSibling = getNextSibling(firstElement); - - expect(nextSibling).toBeNull(); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/getNonce.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getNonce.spec.js deleted file mode 100644 index d47c991ce..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/getNonce.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { testResetCachedNonce } from "../../../../../../../src/components/Personalization/dom-actions/dom/getNonce.js"; -import { - selectNodes, - removeNode, - appendNode, - createNode, -} from "../../../../../../../src/utils/dom/index.js"; -import { getNonce } from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; - -describe("Personalization::DOM::getNonce", () => { - afterEach(() => { - selectNodes("#fooById").forEach(removeNode); - }); - - it("should return the nonce if defined", () => { - testResetCachedNonce(); - appendNode( - document.head, - createNode("script", { id: "fooById", nonce: "123" }), - ); - expect(getNonce()).toEqual("123"); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/getParent.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getParent.spec.js deleted file mode 100644 index 41f64f9aa..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/getParent.spec.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getParent from "../../../../../../../src/components/Personalization/dom-actions/dom/getParent.js"; -import { - selectNodes, - removeNode, - appendNode, - createNode, -} from "../../../../../../../src/utils/dom/index.js"; -import { getElementById } from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; - -describe("Personalization::DOM::getParent", () => { - afterEach(() => { - selectNodes("#parentId").forEach(removeNode); - selectNodes("#childId").forEach(removeNode); - }); - - it("returns the parent node if exists", () => { - const parentNode = createNode("div", { id: "parentId" }); - const childNode = createNode("div", { id: "childId" }); - - appendNode(parentNode, childNode); - appendNode(document.head, parentNode); - - const result = getParent(getElementById("childId")); - - expect(result.tagName).toEqual("DIV"); - expect(result.id).toEqual("parentId"); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/helperForEq.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/helperForEq.spec.js deleted file mode 100644 index 6147c03e3..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/helperForEq.spec.js +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - isNotEqSelector, - splitWithEq, -} from "../../../../../../../src/components/Personalization/dom-actions/dom/helperForEq.js"; - -describe("Personalization::DOM::helperForEq::isNotEqSelector", () => { - it("should match when no eq", () => { - const selector = "#id"; - - expect(isNotEqSelector(selector)).toEqual(true); - }); - - it("should not match when eq", () => { - const selector = "#id:eq(0)"; - - expect(isNotEqSelector(selector)).toEqual(false); - }); -}); - -describe("Personalization::DOM::helperForEq::splitWithEq", () => { - it("should split when no eq", () => { - const selector = "#id"; - - expect(splitWithEq(selector)).toEqual(["#id"]); - }); - - it("should split when eq", () => { - const selector = "#id:eq(0)"; - - expect(splitWithEq(selector)).toEqual(["#id", "0"]); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/insertAfter.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/insertAfter.spec.js deleted file mode 100644 index e7a89fc37..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/insertAfter.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import insertAfter from "../../../../../../../src/components/Personalization/dom-actions/dom/insertAfter.js"; -import { - selectNodes, - removeNode, - appendNode, - createNode, -} from "../../../../../../../src/utils/dom/index.js"; -import { - getElementById, - getNextSibling, -} from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; - -describe("Personalization::DOM::insertAfter", () => { - afterEach(() => { - selectNodes("#style1").forEach(removeNode); - selectNodes("#style2").forEach(removeNode); - }); - - it("inserts a node after an element", () => { - const element1 = createNode("style", { id: "style1" }); - const element2 = createNode("style", { id: "style2" }); - appendNode(document.head, element1); - insertAfter(element1, element2); - - const node1 = getElementById("style1"); - const node2 = getNextSibling(node1); - - expect(node2.id).toEqual("style2"); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/insertBefore.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/insertBefore.spec.js deleted file mode 100644 index d855f7fdf..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/insertBefore.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -// eslint-disable-next-line no-unused-vars -import insertBefore from "../../../../../../../src/components/Personalization/dom-actions/dom/insertBefore.js"; diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js deleted file mode 100644 index 24d1b08fa..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - appendNode, - createNode, -} from "../../../../../../../src/utils/dom/index.js"; -import cleanUpDomChanges from "../../../../../helpers/cleanUpDomChanges.js"; -import isDomElement from "../../../../../../../src/components/Personalization/dom-actions/dom/isDomElement.js"; - -describe("Personalization::DOM::isDomElement", () => { - const testElementId = "superfluous123"; - - beforeEach(() => { - const element = createNode("div", { - id: testElementId, - class: `test-element-${testElementId}`, - }); - element.innerHTML = "test element"; - appendNode(document.body, element); - }); - - afterEach(() => { - cleanUpDomChanges(testElementId); - }); - - it("validates dom element", () => { - expect(isDomElement(document.getElementById(testElementId))).toBeTrue(); - }); - - it("validates not a dom element", () => { - expect(isDomElement({}).toBeFalse); - expect(isDomElement([]).toBeFalse); - expect(isDomElement(true).toBeFalse); - expect(isDomElement("something").toBeFalse); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js deleted file mode 100644 index f55ad1a5a..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - createNode, - appendNode, - selectNodes, - removeNode, -} from "../../../../../../../src/utils/dom/index.js"; -import matchesSelectorWithEq from "../../../../../../../src/components/Personalization/dom-actions/dom/matchesSelectorWithEq.js"; - -describe("Personalization::DOM::matchesSelectorWithEq", () => { - afterEach(() => { - selectNodes(".eq").forEach(removeNode); - }); - - it("should match when no eq", () => { - const node = createNode("DIV", { id: "noEq", class: "eq" }); - - appendNode(document.body, node); - - const selector = "#noEq"; - const element = document.getElementById("noEq"); - const result = matchesSelectorWithEq(selector, element); - - expect(result).toEqual(true); - }); - - it("should match when eq and just one element", () => { - const content = ` -
    -
    first
    - -
    second
    - -
    third
    -
    - `; - const node = createNode( - "DIV", - { id: "abc", class: "eq" }, - { - innerHTML: content, - }, - ); - - appendNode(document.body, node); - - const selector = "#abc:eq(0) > div.b:eq(0) > div.c:eq(0)"; - const element = document.getElementById("one"); - const result = matchesSelectorWithEq(selector, element); - - expect(result).toEqual(true); - }); - - it("should match when eq and multiple elements", () => { - const content = ` -
    -
    first
    - -
    second
    - -
    third
    -
    - `; - - const node = createNode( - "DIV", - { id: "abc", class: "eq" }, - { innerHTML: content }, - ); - - appendNode(document.body, node); - - const selector = "#abc:eq(0) > div.b:eq(0) > div.c"; - const one = document.getElementById("one"); - const two = document.getElementById("two"); - const three = document.getElementById("three"); - - expect(matchesSelectorWithEq(selector, one)).toEqual(true); - expect(matchesSelectorWithEq(selector, two)).toEqual(true); - expect(matchesSelectorWithEq(selector, three)).toEqual(true); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/removeAttribute.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/removeAttribute.spec.js deleted file mode 100644 index f895a417c..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/removeAttribute.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import removeAttribute from "../../../../../../../src/components/Personalization/dom-actions/dom/removeAttribute.js"; -import { - createNode, - removeNode, - selectNodes, -} from "../../../../../../../src/utils/dom/index.js"; -import { - getAttribute, - setAttribute, -} from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; - -describe("Personalization::DOM::removeAttribute", () => { - afterEach(() => { - selectNodes("#fooId").forEach(removeNode); - }); - - it("should remove the element's attribute", () => { - const element = createNode("div", { id: "fooId" }); - setAttribute(element, "data-foo", "dummyValue"); - - const attr = getAttribute(element, "data-foo"); - expect(attr).toEqual("dummyValue"); - - removeAttribute(element, "data-foo"); - const removedAttr = getAttribute(element, "data-foo"); - - expect(removedAttr).toBeNull(); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js deleted file mode 100644 index 0839a6f6f..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js +++ /dev/null @@ -1,190 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - createNode, - appendNode, - selectNodes, - removeNode, -} from "../../../../../../../src/utils/dom/index.js"; -import { - escapeIdentifiersInSelector, - parseSelector, - selectNodesWithEq, -} from "../../../../../../../src/components/Personalization/dom-actions/dom/selectNodesWithEq.js"; - -describe("Personalization::DOM::escapeIdentifiersInSelector", () => { - it("should escape when digits only for ID selector", () => { - const result = escapeIdentifiersInSelector("#123 > #foo div.345"); - - expect(result).toEqual("#\\31 23 > #foo div.\\33 45"); - expect(document.querySelector(result)).toEqual(null); - }); - - it("should escape when digits only for class selector", () => { - const result = escapeIdentifiersInSelector(".123"); - - expect(result).toEqual(".\\31 23"); - expect(document.querySelector(result)).toEqual(null); - }); - - it("should escape when hyphen and digits ID selector", () => { - const result = escapeIdentifiersInSelector("#-123"); - - expect(result).toEqual("#-\\31 23"); - expect(document.querySelector(result)).toEqual(null); - }); - - it("should escape when hyphen and digits class selector", () => { - const result = escapeIdentifiersInSelector(".-123"); - - expect(result).toEqual(".-\\31 23"); - expect(document.querySelector(result)).toEqual(null); - }); -}); - -describe("Personalization::DOM::parseSelector", () => { - it("should parse selector when no eq", () => { - const result = parseSelector("#test"); - - expect(result[0]).toEqual({ sel: "#test" }); - }); - - it("should parse selector when eq", () => { - const result = parseSelector( - "HTML > BODY > DIV.wrapper:eq(0) > HEADER.header:eq(0) > DIV.pagehead:eq(0) > P:nth-of-type(1)", - ); - - expect(result[0]).toEqual({ sel: "HTML > BODY > DIV.wrapper", eq: 0 }); - expect(result[1]).toEqual({ sel: " > HEADER.header", eq: 0 }); - expect(result[2]).toEqual({ sel: " > DIV.pagehead", eq: 0 }); - expect(result[3]).toEqual({ sel: " > P:nth-of-type(1)" }); - }); -}); - -describe("Personalization::DOM::selectNodesWithEq", () => { - afterEach(() => { - selectNodes(".eq").forEach(removeNode); - }); - - it("should select when no eq", () => { - appendNode(document.body, createNode("DIV", { id: "noEq", class: "eq" })); - - const result = selectNodesWithEq("#noEq"); - - expect(result[0].tagName).toEqual("DIV"); - expect(result[0].id).toEqual("noEq"); - }); - - it("should select when eq and just one element", () => { - const content = ` -
    -
    first
    - -
    second
    - -
    third
    -
    - `; - - appendNode( - document.body, - createNode("DIV", { id: "abc", class: "eq" }, { innerHTML: content }), - ); - - const result = selectNodesWithEq("#abc:eq(0) > div.b:eq(0) > div.c:eq(0)"); - - expect(result[0].tagName).toEqual("DIV"); - expect(result[0].textContent).toEqual("first"); - }); - - it("should select when eq and multiple elements", () => { - const content = ` -
    -
    first
    - -
    second
    - -
    third
    -
    - `; - - appendNode( - document.body, - createNode("DIV", { id: "abc", class: "eq" }, { innerHTML: content }), - ); - - const result = selectNodesWithEq("#abc:eq(0) > div.b:eq(0) > div.c"); - - expect(result[0].tagName).toEqual("DIV"); - expect(result[0].textContent).toEqual("first"); - expect(result[1].tagName).toEqual("DIV"); - expect(result[1].textContent).toEqual("second"); - expect(result[2].tagName).toEqual("DIV"); - expect(result[2].textContent).toEqual("third"); - }); - - it("should select when eq and no elements", () => { - appendNode(document.body, createNode("DIV", { id: "abc", class: "eq" })); - - const result = selectNodesWithEq("#abc:eq(0) > div.foo"); - - expect(result.length).toEqual(0); - }); - - it("should select when eq and eq greater than number of nodes", () => { - appendNode(document.body, createNode("DIV", { id: "abc", class: "eq" })); - - const result = selectNodesWithEq("#abc:eq(1)"); - - expect(result.length).toEqual(0); - }); - - it("should show eq vs nth-of-child difference", () => { - const content = ` -
    -

    first

    -
    -
    -

    second

    -
    - `; - - appendNode( - document.body, - createNode("DIV", { id: "abc", class: "eq" }, { innerHTML: content }), - ); - - // NOTE: eq has zero based index, while nth-child index starts at 1 - const resultWithEq = selectNodesWithEq("#abc > div p:eq(0)"); - const resultWitNthChild = selectNodesWithEq("#abc > div :nth-child(1)"); - - expect(resultWithEq.length).toEqual(1); - expect(resultWitNthChild.length).toEqual(2); - }); - - it("should show throw errors", () => { - appendNode(document.body, createNode("DIV", { id: "abc", class: "eq" })); - - const selectors = [ - "#abc:eq(bad)", - "#abc:eq(eq())", - "#abc:eq(0))", - "#abc.123", - " > ", - ]; - - selectors.forEach((selector) => { - expect(() => selectNodesWithEq(selector)).toThrow(); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/setAttribute.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/setAttribute.spec.js deleted file mode 100644 index c6130675a..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/setAttribute.spec.js +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import setAttribute from "../../../../../../../src/components/Personalization/dom-actions/dom/setAttribute.js"; -import { - createNode, - removeNode, - selectNodes, -} from "../../../../../../../src/utils/dom/index.js"; -import { getAttribute } from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; - -describe("Personalization::DOM::setAttribute", () => { - afterEach(() => { - selectNodes("#fooId").forEach(removeNode); - }); - - it("should set the attribute for the element", () => { - const element = createNode("div", { id: "fooId" }); - setAttribute(element, "foo-data", "dummyValue"); - - const attr = getAttribute(element, "foo-data"); - - expect(attr).toEqual("dummyValue"); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/setStyle.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/setStyle.spec.js deleted file mode 100644 index 5a352eda7..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/setStyle.spec.js +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import setStyle from "../../../../../../../src/components/Personalization/dom-actions/dom/setStyle.js"; -import { - selectNodes, - removeNode, - createNode, -} from "../../../../../../../src/utils/dom/index.js"; -import { getAttribute } from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; - -describe("Personalization::DOM::setStyle", () => { - afterEach(() => { - selectNodes("#fooDivId").forEach(removeNode); - }); - - it("sets style with priority to the element", () => { - const element = createNode("div", { id: "fooDivId" }); - setStyle(element, "padding", "15px", "important"); - - const style = getAttribute(element, "style"); - - expect(style).toEqual("padding: 15px !important;"); - }); - - it("sets style to the element, without priority", () => { - const element = createNode("div", { id: "fooDivId" }); - setStyle(element, "padding", "15px"); - - const style = getAttribute(element, "style"); - - expect(style).toEqual("padding: 15px;"); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/util.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/util.spec.js deleted file mode 100644 index 35ed66f8e..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/dom/util.spec.js +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { addPxIfMissing } from "../../../../../../../src/components/Personalization/dom-actions/dom/util.js"; - -describe("Personalization::DOM::util", () => { - it("appends 'px' string if missing", () => { - const value = "400"; - const result = addPxIfMissing(value); - - expect(result).toEqual("400px"); - }); - - it("does not append 'px' string if already present", () => { - const value = "400px"; - const result = addPxIfMissing(value); - - expect(result).toEqual("400px"); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/images.spec.js b/test/unit/specs/components/Personalization/dom-actions/images.spec.js deleted file mode 100644 index 72ee289ac..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/images.spec.js +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - createFragment, - getChildNodes, -} from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import { - isImage, - loadImage, -} from "../../../../../../src/components/Personalization/dom-actions/images.js"; -import { IMG } from "../../../../../../src/constants/tagName.js"; -import { createNode } from "../../../../../../src/utils/dom/index.js"; - -describe("Personalization::helper::images", () => { - beforeEach(() => { - cleanUpDomChanges("fooImage"); - }); - - afterEach(() => { - cleanUpDomChanges("fooImage"); - }); - - it("should verify if it is an image", () => { - const fragmentHTML = ""; - const fragment = createFragment(fragmentHTML); - const imageNode = getChildNodes(fragment)[0]; - - expect(isImage(fragment)).toBeFalse(); - expect(isImage(imageNode)).toBeTrue(); - }); - - it("should create an image node", () => { - const result = loadImage("http://foo.com"); - const image = createNode(IMG, { src: "http://foo.com" }); - - expect(result).toEqual(image); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js b/test/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js deleted file mode 100644 index 18c1cb860..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import initDomActionsModules, { - DOM_ACTION_APPEND_HTML, - DOM_ACTION_CUSTOM_CODE, - DOM_ACTION_INSERT_AFTER, - DOM_ACTION_INSERT_BEFORE, - DOM_ACTION_MOVE, - DOM_ACTION_PREPEND_HTML, - DOM_ACTION_REARRANGE, - DOM_ACTION_REMOVE, - DOM_ACTION_REPLACE_HTML, - DOM_ACTION_RESIZE, - DOM_ACTION_SET_ATTRIBUTE, - DOM_ACTION_SET_HTML, - DOM_ACTION_SET_IMAGE_SOURCE, - DOM_ACTION_SET_STYLE, - DOM_ACTION_SET_TEXT, - DOM_ACTION_COLLECT_INTERACTIONS, -} from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -const buildSet = () => { - const result = new Set(); - - // This is to make IE 11 happy - result.add(DOM_ACTION_SET_HTML); - result.add(DOM_ACTION_CUSTOM_CODE); - result.add(DOM_ACTION_SET_TEXT); - result.add(DOM_ACTION_SET_ATTRIBUTE); - result.add(DOM_ACTION_SET_IMAGE_SOURCE); - result.add(DOM_ACTION_SET_STYLE); - result.add(DOM_ACTION_MOVE); - result.add(DOM_ACTION_RESIZE); - result.add(DOM_ACTION_REARRANGE); - result.add(DOM_ACTION_REMOVE); - result.add(DOM_ACTION_INSERT_AFTER); - result.add(DOM_ACTION_INSERT_BEFORE); - result.add(DOM_ACTION_REPLACE_HTML); - result.add(DOM_ACTION_PREPEND_HTML); - result.add(DOM_ACTION_APPEND_HTML); - result.add(DOM_ACTION_COLLECT_INTERACTIONS); - - return result; -}; - -const STANDARD_MODULES = buildSet(); - -describe("Personalization::turbine::initDomActionsModules", () => { - it("should have all the required modules", () => { - const result = initDomActionsModules(() => {}); - const keys = Object.keys(result); - - expect(keys.length).toEqual(STANDARD_MODULES.size); - - Object.keys(result).forEach((key) => { - expect(STANDARD_MODULES.has(key)).toEqual(true); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js b/test/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js deleted file mode 100644 index 5704cc9ef..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, - selectNodes, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_INSERT_AFTER } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::insertAfter", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("insertAfter"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_INSERT_AFTER, - }); - }); - - afterEach(() => { - cleanUpDomChanges("insertAfter"); - }); - - it("should insert after personalized content", () => { - const modules = initDomActionsModules(); - const { insertAfter } = modules; - const child = createNode( - "div", - { id: "a", class: "ia" }, - { innerHTML: "AAA" }, - ); - const element = createNode("div", { id: "insertAfter" }, {}, [child]); - - appendNode(document.body, element); - - const settings = { - selector: "#a", - prehidingSelector: "#a", - content: `
    BBB
    CCC
    `, - meta: { a: 1 }, - }; - - return insertAfter(settings, decorateProposition).then(() => { - const result = selectNodes("div#insertAfter .ia"); - - expect(result[0].innerHTML).toEqual("AAA"); - expect(result[1].innerHTML).toEqual("BBB"); - expect(result[2].innerHTML).toEqual("CCC"); - - expect(getAttribute(result[1], CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect( - getAttribute(result[1], INTERACT_ID_DATA_ATTRIBUTE), - ).not.toBeNull(); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js b/test/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js deleted file mode 100644 index 78421706b..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, - selectNodes, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_INSERT_BEFORE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::insertBefore", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("insertBefore"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_INSERT_BEFORE, - }); - }); - - afterEach(() => { - cleanUpDomChanges("insertBefore"); - }); - - it("should insert before personalized content", () => { - const modules = initDomActionsModules(); - const { insertBefore } = modules; - const child = createNode( - "div", - { id: "a", class: "ib" }, - { innerHTML: "AAA" }, - ); - const element = createNode("div", { id: "insertBefore" }, {}, [child]); - - appendNode(document.body, element); - - const settings = { - selector: "#a", - prehidingSelector: "#a", - content: `
    BBB
    `, - meta: { a: 1 }, - }; - - return insertBefore(settings, decorateProposition).then(() => { - const [insertedElement, existingElement] = selectNodes( - "div#insertBefore .ib", - ); - - expect(insertedElement.innerHTML).toEqual("BBB"); - expect(getAttribute(insertedElement, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect( - getAttribute(insertedElement, INTERACT_ID_DATA_ATTRIBUTE), - ).not.toBeNull(); - - expect(existingElement.innerHTML).toEqual("AAA"); - expect( - getAttribute(existingElement, INTERACT_ID_DATA_ATTRIBUTE), - ).toBeNull(); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/move.spec.js b/test/unit/specs/components/Personalization/dom-actions/move.spec.js deleted file mode 100644 index 7547427c0..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/move.spec.js +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_MOVE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::move", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("move"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_MOVE, - }); - }); - - afterEach(() => { - cleanUpDomChanges("move"); - }); - - it("should move personalized content", () => { - const modules = initDomActionsModules(); - const { move } = modules; - const element = createNode("div", { id: "move" }); - - appendNode(document.body, element); - - const settings = { - selector: "#move", - prehidingSelector: "#move", - content: { left: "100px", top: "100px" }, - meta: { a: 1 }, - }; - - move(settings, decorateProposition).then(() => { - expect(element.style.left).toEqual("100px"); - expect(element.style.top).toEqual("100px"); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); - }); - - it("should move personalized content even if coordinates are not properly formatted", () => { - const modules = initDomActionsModules(); - const { move } = modules; - const element = createNode("div", { id: "move" }); - - appendNode(document.body, element); - - const settings = { - selector: "#move", - prehidingSelector: "#move", - content: { left: "100", top: "100" }, - meta: { a: 1 }, - }; - - move(settings, decorateProposition).then(() => { - expect(element.style.left).toEqual("100px"); - expect(element.style.top).toEqual("100px"); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js b/test/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js deleted file mode 100644 index d4ebcd260..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, - selectNodes, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_PREPEND_HTML } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::prependHtml", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("prependHtml"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_PREPEND_HTML, - }); - }); - - afterEach(() => { - cleanUpDomChanges("prependHtml"); - }); - - it("should prepend personalized content", () => { - const modules = initDomActionsModules(); - const { prependHtml } = modules; - const content = `
  • 3
  • `; - const element = createNode( - "ul", - { id: "prependHtml" }, - { innerHTML: content }, - ); - - appendNode(document.body, element); - - const settings = { - selector: "#prependHtml", - prehidingSelector: "#prependHtml", - content: `
  • 1
  • 2
  • `, - meta: { a: 1 }, - }; - - return prependHtml(settings, decorateProposition).then(() => { - const result = selectNodes("ul#prependHtml li"); - - expect(result.length).toEqual(3); - // first li (prepended) - expect(result[0].innerHTML).toEqual("1"); - expect(getAttribute(result[0], CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect( - getAttribute(result[0], INTERACT_ID_DATA_ATTRIBUTE), - ).not.toBeNull(); - - // second li (prepended) - expect(result[1].innerHTML).toEqual("2"); - expect(getAttribute(result[1], CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect( - getAttribute(result[1], INTERACT_ID_DATA_ATTRIBUTE), - ).not.toBeNull(); - - // third li (pre-existing) - expect(result[2].innerHTML).toEqual("3"); - expect(getAttribute(result[2], CLICK_LABEL_DATA_ATTRIBUTE)).toBeNull(); - expect(getAttribute(result[2], INTERACT_ID_DATA_ATTRIBUTE)).toBeNull(); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js b/test/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js deleted file mode 100644 index c85e09587..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, - selectNodes, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_REARRANGE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::rearrange", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("rearrange"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_REARRANGE, - }); - }); - - afterEach(() => { - cleanUpDomChanges("rearrange"); - }); - - it("should rearrange elements when from < to", () => { - const modules = initDomActionsModules(); - const { rearrange } = modules; - const content = ` -
  • 1
  • -
  • 2
  • -
  • 3
  • - `; - const element = createNode( - "ul", - { id: "rearrange" }, - { innerHTML: content }, - ); - - appendNode(document.body, element); - - const settings = { - selector: "#rearrange", - prehidingSelector: "#rearrange", - content: { from: 0, to: 2 }, - meta: { a: 1 }, - }; - - return rearrange(settings, decorateProposition).then(() => { - const result = selectNodes("li"); - - expect(result[0].textContent).toEqual("2"); - expect(getAttribute(result[0], CLICK_LABEL_DATA_ATTRIBUTE)).toBeNull(); - expect(getAttribute(result[0], INTERACT_ID_DATA_ATTRIBUTE)).toBeNull(); - - expect(result[1].textContent).toEqual("3"); - expect(getAttribute(result[1], CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect( - getAttribute(result[1], INTERACT_ID_DATA_ATTRIBUTE), - ).not.toBeNull(); - - expect(result[2].textContent).toEqual("1"); - expect(getAttribute(result[2], CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect( - getAttribute(result[2], INTERACT_ID_DATA_ATTRIBUTE), - ).not.toBeNull(); - }); - }); - - it("should rearrange elements when from > to", () => { - const modules = initDomActionsModules(); - const { rearrange } = modules; - const content = ` -
  • 1
  • -
  • 2
  • -
  • 3
  • - `; - const element = createNode( - "ul", - { id: "rearrange" }, - { innerHTML: content }, - ); - - appendNode(document.body, element); - - const settings = { - selector: "#rearrange", - prehidingSelector: "#rearrange", - content: { from: 2, to: 0 }, - meta: { a: 1 }, - }; - - return rearrange(settings, decorateProposition).then(() => { - const result = selectNodes("li"); - - expect(result[0].textContent).toEqual("3"); - expect(result[1].textContent).toEqual("1"); - expect(result[2].textContent).toEqual("2"); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/remapCustomCodeOffers.spec.js b/test/unit/specs/components/Personalization/dom-actions/remapCustomCodeOffers.spec.js deleted file mode 100644 index f4a1a210c..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/remapCustomCodeOffers.spec.js +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2021 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import remapCustomCodeOffers from "../../../../../../src/components/Personalization/dom-actions/remapCustomCodeOffers.js"; - -describe("remapCustomCodeOffers", () => { - it("changes target selector to parent for standard body selector", () => { - expect( - remapCustomCodeOffers({ - type: "customCode", - content: "
    superfluous
    ", - selector: "BODY > *:eq(0)", - }), - ).toEqual({ - type: "customCode", - content: "
    superfluous
    ", - selector: "BODY", - }); - }); - - it("does not change selector if non-standard", () => { - expect( - remapCustomCodeOffers({ - type: "customCode", - content: "
    superfluous
    ", - selector: ".whoopie", - }), - ).toEqual({ - type: "customCode", - content: "
    superfluous
    ", - selector: ".whoopie", - }); - }); - - it("only handles customCode type", () => { - expect( - remapCustomCodeOffers({ - type: "somethingSpecial", - content: "
    superfluous
    ", - selector: "BODY > *:eq(0)", - }), - ).toEqual({ - type: "somethingSpecial", - content: "
    superfluous
    ", - selector: "BODY > *:eq(0)", - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/remapHeadOffers.spec.js b/test/unit/specs/components/Personalization/dom-actions/remapHeadOffers.spec.js deleted file mode 100644 index d2e9c9d56..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/remapHeadOffers.spec.js +++ /dev/null @@ -1,13 +0,0 @@ -/* -Copyright 2021 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -// TODO diff --git a/test/unit/specs/components/Personalization/dom-actions/remove.spec.js b/test/unit/specs/components/Personalization/dom-actions/remove.spec.js deleted file mode 100644 index ccb2dbfb9..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/remove.spec.js +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, - selectNodes, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_REMOVE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::remove", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("remove"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_REMOVE, - }); - }); - - afterEach(() => { - cleanUpDomChanges("remove"); - }); - - it("should remove element", () => { - const modules = initDomActionsModules(); - const { remove } = modules; - const content = `
    `; - const element = createNode("div", { id: "remove" }, { innerHTML: content }); - - appendNode(document.body, element); - - const settings = { - selector: "#remove", - prehidingSelector: "#remove", - meta: { a: 1 }, - }; - - return remove(settings, decorateProposition).then(() => { - const result = selectNodes("#child"); - - expect(result.length).toEqual(0); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js b/test/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js deleted file mode 100644 index bdd6b1e9c..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, - selectNodes, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_REPLACE_HTML } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::replaceHtml", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("replaceHtml"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_REPLACE_HTML, - }); - }); - - afterEach(() => { - cleanUpDomChanges("replaceHtml"); - }); - - it("should replace element with personalized content", () => { - const modules = initDomActionsModules(); - const { replaceHtml } = modules; - const child = createNode( - "div", - { id: "a", class: "rh" }, - { innerHTML: "AAA" }, - ); - const element = createNode("div", { id: "replaceHtml" }, {}, [child]); - - appendNode(document.body, element); - - const settings = { - selector: "#a", - prehidingSelector: "#a", - content: `
    BBB
    `, - meta: { a: 1 }, - }; - - return replaceHtml(settings, decorateProposition).then(() => { - const result = selectNodes("div#replaceHtml .rh"); - - expect(result.length).toEqual(1); - expect(result[0].innerHTML).toEqual("BBB"); - - expect(getAttribute(result[0], CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect( - getAttribute(result[0], INTERACT_ID_DATA_ATTRIBUTE), - ).not.toBeNull(); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/resize.spec.js b/test/unit/specs/components/Personalization/dom-actions/resize.spec.js deleted file mode 100644 index f212bd18b..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/resize.spec.js +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_RESIZE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::resize", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("resize"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_RESIZE, - }); - }); - - afterEach(() => { - cleanUpDomChanges("resize"); - }); - - it("should resize personalized content", () => { - const modules = initDomActionsModules(); - const { resize } = modules; - const element = createNode("div", { id: "resize" }); - - appendNode(document.body, element); - - const settings = { - selector: "#resize", - prehidingSelector: "#resize", - content: { width: "100px", height: "100px" }, - meta: { a: 1 }, - }; - - return resize(settings, decorateProposition).then(() => { - expect(element.style.width).toEqual("100px"); - expect(element.style.height).toEqual("100px"); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); - }); - - it("should resize personalized content even if dimensions are not properly formatted", () => { - const modules = initDomActionsModules(); - const { resize } = modules; - const element = createNode("div", { id: "resize" }); - - appendNode(document.body, element); - - const settings = { - selector: "#resize", - prehidingSelector: "#resize", - content: { width: "100", height: "100" }, - meta: { a: 1 }, - }; - - return resize(settings, decorateProposition).then(() => { - expect(element.style.width).toEqual("100px"); - expect(element.style.height).toEqual("100px"); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/scripts.spec.js b/test/unit/specs/components/Personalization/dom-actions/scripts.spec.js deleted file mode 100644 index 3e5b83235..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/scripts.spec.js +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - getInlineScripts, - getRemoteScriptsUrls, - executeInlineScripts, -} from "../../../../../../src/components/Personalization/dom-actions/scripts.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { createFragment } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import { DIV } from "../../../../../../src/constants/tagName.js"; -import { createNode } from "../../../../../../src/utils/dom/index.js"; - -describe("Personalization::helper::scripts", () => { - beforeEach(() => { - cleanUpDomChanges("fooDiv"); - }); - - afterEach(() => { - cleanUpDomChanges("fooDiv"); - }); - - it("should get an inline script", () => { - const fragmentHTML = - ""; - const fragment = createFragment(fragmentHTML); - - const inlineScripts = getInlineScripts(fragment); - expect(inlineScripts.length).toEqual(1); - }); - - it("should return null if inlineScript doesn't have text code", () => { - const fragmentHTML = - ""; - const fragment = createFragment(fragmentHTML); - - const inlineScripts = getInlineScripts(fragment); - expect(inlineScripts.length).toEqual(0); - }); - - it("should get a remote script", () => { - const fragmentHTML = - "
    "; - const fragment = createFragment(fragmentHTML); - const remoteScripts = getRemoteScriptsUrls(fragment); - - expect(remoteScripts.length).toEqual(1); - expect(remoteScripts[0]).toEqual("http://foo.com"); - }); - - it("should get a empty array if remote script doesn't have url attr", () => { - const fragmentHTML = - "
    "; - const fragment = createFragment(fragmentHTML); - const remoteScripts = getRemoteScriptsUrls(fragment); - - expect(remoteScripts.length).toEqual(0); - }); - - it("should execute inline script", () => { - const fragmentHTML = - ""; - const fragment = createFragment(fragmentHTML); - const inlineScripts = getInlineScripts(fragment); - const container = createNode(DIV); - spyOn(container, "appendChild").and.callThrough(); - spyOn(container, "removeChild").and.callThrough(); - - executeInlineScripts(container, inlineScripts); - - expect(container.appendChild).toHaveBeenCalledWith(inlineScripts[0]); - expect(container.removeChild).toHaveBeenCalledWith(inlineScripts[0]); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js b/test/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js deleted file mode 100644 index 94305b318..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_SET_ATTRIBUTE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::setAttribute", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("setAttribute"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_SET_ATTRIBUTE, - }); - }); - - afterEach(() => { - cleanUpDomChanges("setAttribute"); - }); - - it("should set element attribute", () => { - const modules = initDomActionsModules(); - const { setAttribute } = modules; - const element = createNode("div", { id: "setAttribute" }); - - appendNode(document.body, element); - - const settings = { - selector: "#setAttribute", - prehidingSelector: "#setAttribute", - content: { "data-test": "bar" }, - meta: { a: 1 }, - }; - - return setAttribute(settings, decorateProposition).then(() => { - expect(element.getAttribute("data-test")).toEqual("bar"); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/setHtml.spec.js b/test/unit/specs/components/Personalization/dom-actions/setHtml.spec.js deleted file mode 100644 index 186e73da6..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/setHtml.spec.js +++ /dev/null @@ -1,119 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import pause from "../../../../helpers/pause.js"; -import { DOM_ACTION_SET_HTML } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::setHtml", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("setHtml"); - delete window.someEvar123; - - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_SET_HTML, - }); - }); - - afterEach(() => { - cleanUpDomChanges("setHtml"); - cleanUpDomChanges("btn"); - delete window.someEvar123; - }); - - it("should set personalized content", async () => { - const modules = initDomActionsModules(); - const { setHtml } = modules; - const element = createNode("div", { id: "setHtml" }); - element.innerHTML = "foo"; - - appendNode(document.body, element); - - const settings = { - selector: "#setHtml", - prehidingSelector: "#setHtml", - content: "bar", - meta: { a: 1 }, - }; - - await setHtml(settings, decorateProposition); - expect(element.innerHTML).toEqual("bar"); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); - - it("should execute inline JavaScript", async () => { - const modules = initDomActionsModules(); - const { setHtml } = modules; - const element = createNode("div", { id: "setHtml" }); - element.innerHTML = "foo"; - - appendNode(document.body, element); - - const settings = { - selector: "#setHtml", - prehidingSelector: "#setHtml", - content: - "", - meta: { a: 1 }, - }; - - await setHtml(settings, decorateProposition); - await pause(501); - - expect(window.someEvar123).toEqual(1); - - const scriptElements = document.querySelectorAll("#evar123"); - expect(scriptElements.length).toEqual(1); - }); - - it("should execute inline JavaScript with event listeners", async () => { - const modules = initDomActionsModules(); - const { setHtml } = modules; - const button = createNode("button", { id: "btn" }); - const element = createNode("div", { id: "setHtml" }); - element.innerHTML = "foo"; - - appendNode(document.body, button); - appendNode(document.body, element); - - const settings = { - selector: "#setHtml", - prehidingSelector: "#setHtml", - content: ``, - meta: { a: 1 }, - }; - - await setHtml(settings, decorateProposition); - - button.click(); - expect(window.someEvar123).toEqual(2); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js b/test/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js deleted file mode 100644 index e07c83045..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_SET_IMAGE_SOURCE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::setImageSource", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("setImageSource"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_SET_IMAGE_SOURCE, - }); - }); - - afterEach(() => { - cleanUpDomChanges("setImageSource"); - }); - - it("should swap image", () => { - const url = "http://foo.com/a.png"; - const modules = initDomActionsModules(); - const { setImageSource } = modules; - const element = createNode("img", { id: "setImageSource", src: url }); - - appendNode(document.body, element); - - const settings = { - selector: "#setImageSource", - prehidingSelector: "#setImageSource", - content: "http://foo.com/b.png", - meta: { a: 1 }, - }; - - return setImageSource(settings, decorateProposition).then(() => { - expect(element.getAttribute("src")).toEqual("http://foo.com/b.png"); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/setStyles.spec.js b/test/unit/specs/components/Personalization/dom-actions/setStyles.spec.js deleted file mode 100644 index d2ae0ce65..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/setStyles.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_SET_STYLE } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::setStyle", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("setStyle"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_SET_STYLE, - }); - }); - - afterEach(() => { - cleanUpDomChanges("setStyle"); - }); - - it("should set styles", () => { - const modules = initDomActionsModules(); - const { setStyle } = modules; - const element = createNode("div", { id: "setStyle" }); - - appendNode(document.body, element); - - const settings = { - selector: "#setStyle", - prehidingSelector: "#setStyle", - content: { "font-size": "33px", priority: "important" }, - meta: { a: 1 }, - }; - - return setStyle(settings, decorateProposition).then(() => { - expect(element.style.getPropertyValue("font-size")).toEqual("33px"); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/setText.spec.js b/test/unit/specs/components/Personalization/dom-actions/setText.spec.js deleted file mode 100644 index 55d2a8aa5..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/setText.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - appendNode, - createNode, -} from "../../../../../../src/utils/dom/index.js"; -import { initDomActionsModules } from "../../../../../../src/components/Personalization/dom-actions/index.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { DOM_ACTION_SET_TEXT } from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; - -describe("Personalization::actions::setText", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("setText"); - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_SET_TEXT, - }); - }); - - afterEach(() => { - cleanUpDomChanges("setText"); - }); - - it("should set personalized text", async () => { - const itemData = { - type: "setText", - selector: "#setText", - prehidingSelector: "#setText", - content: "bar", - meta: { a: 1 }, - }; - - const modules = initDomActionsModules(); - const { setText } = modules; - const element = createNode("div", { id: "setText" }); - element.textContent = "foo"; - appendNode(document.body, element); - - await setText(itemData, decorateProposition); - - expect(element.textContent).toEqual("bar"); - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "trackingLabel", - ); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); -}); diff --git a/test/unit/specs/components/Personalization/dom-actions/swapImage.spec.js b/test/unit/specs/components/Personalization/dom-actions/swapImage.spec.js deleted file mode 100644 index ed8e3500c..000000000 --- a/test/unit/specs/components/Personalization/dom-actions/swapImage.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -// eslint-disable-next-line no-unused-vars -import swapImage from "../../../../../../src/components/Personalization/dom-actions/swapImage.js"; diff --git a/test/unit/specs/components/Personalization/flicker/index.spec.js b/test/unit/specs/components/Personalization/flicker/index.spec.js deleted file mode 100644 index cb4709e05..000000000 --- a/test/unit/specs/components/Personalization/flicker/index.spec.js +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - selectNodes, - removeNode, -} from "../../../../../../src/utils/dom/index.js"; -import { - hideElements, - showElements, -} from "../../../../../../src/components/Personalization/flicker/index.js"; - -describe("Personalization::flicker", () => { - beforeEach(() => { - selectNodes("style").forEach(removeNode); - }); - - afterEach(() => { - selectNodes("style").forEach(removeNode); - }); - - it("should add prehiding style tags", () => { - const prehidingSelector = ".add"; - - hideElements(prehidingSelector); - - const styles = selectNodes("style"); - - expect(styles.length).toEqual(1); - - const styleDefinition = styles[0].textContent; - - expect(styleDefinition).toEqual( - `${prehidingSelector} { visibility: hidden }`, - ); - }); - - it("should remove prehiding style tags", () => { - const prehidingSelector = ".remove"; - - hideElements(prehidingSelector); - showElements(prehidingSelector); - - const styles = selectNodes("style"); - - expect(styles.length).toEqual(0); - }); -}); diff --git a/test/unit/specs/components/Personalization/handlers/createDecorateProposition.spec.js b/test/unit/specs/components/Personalization/handlers/createDecorateProposition.spec.js deleted file mode 100644 index 5aa6ca1b5..000000000 --- a/test/unit/specs/components/Personalization/handlers/createDecorateProposition.spec.js +++ /dev/null @@ -1,253 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - CLICK_LABEL_DATA_ATTRIBUTE, - INTERACT_ID_DATA_ATTRIBUTE, -} from "../../../../../../src/components/Personalization/handlers/createDecorateProposition.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - appendNode, - createNode, -} from "../../../../../../src/utils/dom/index.js"; -import { getAttribute } from "../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import createDecoratePropositionForTest from "../../../../helpers/createDecoratePropositionForTest.js"; -import { - DOM_ACTION_CLICK, - DOM_ACTION_SET_HTML, -} from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; -import { - DECORATED_ELEMENTS_ONLY, - NEVER, -} from "../../../../../../src/constants/propositionInteractionType.js"; -import { - ADOBE_JOURNEY_OPTIMIZER, - ADOBE_TARGET, -} from "../../../../../../src/constants/decisionProvider.js"; - -describe("Personalization::createDecorateProposition", () => { - let decorateProposition; - - beforeEach(() => { - cleanUpDomChanges("something"); - }); - - afterEach(() => { - cleanUpDomChanges("something"); - }); - - it("sets a data-attribute for interact id and label", () => { - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_CLICK, - trackingLabel: "myTrackingLabel", - }); - - const element = createNode( - "div", - { id: "something" }, - { innerText: "superfluous" }, - ); - appendNode(document.body, element); - - decorateProposition(element); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "myTrackingLabel", - ); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); - - it("sets a data-attribute for interact id and label when autoCollectPropositionInteractions=decoratedElementsOnly", () => { - decorateProposition = createDecoratePropositionForTest({ - autoCollectPropositionInteractions: { - [ADOBE_JOURNEY_OPTIMIZER]: DECORATED_ELEMENTS_ONLY, - [ADOBE_TARGET]: NEVER, - }, - type: DOM_ACTION_CLICK, - trackingLabel: "myTrackingLabel", - }); - - const element = createNode( - "div", - { id: "something" }, - { innerText: "superfluous" }, - ); - appendNode(document.body, element); - - decorateProposition(element); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "myTrackingLabel", - ); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); - - it("does not set a data-attribute for label if no label is specified", () => { - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_CLICK, - trackingLabel: null, - }); - - const element = createNode( - "div", - { id: "something" }, - { innerText: "superfluous" }, - ); - appendNode(document.body, element); - - decorateProposition(element); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toBeNull(); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); - - it("reuses interact ids when one is already present on an element", () => { - const element = createNode( - "div", - { id: "something" }, - { innerText: "superfluous" }, - ); - appendNode(document.body, element); - - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_CLICK, - itemId: "itemId1", - trackingLabel: "myTrackingLabel", - }); - decorateProposition(element); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "myTrackingLabel", - ); - const interactId = getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE); - expect(interactId).not.toBeNull(); - - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_CLICK, - itemId: "itemId2", - trackingLabel: "myOtherTrackingLabel", - }); - decorateProposition(element); - - // tracking label remains the same despite another item targeting the same element with trackingLabel set to "myOtherTrackingLabel" (first one wins) - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "myTrackingLabel", - ); - - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).toEqual( - interactId, - ); - }); - - it("provides a unique interact id for each element", () => { - const element = createNode( - "div", - { id: "something" }, - { - innerHTML: - "
  • one
  • two
  • three
  • ", - }, - ); - appendNode(document.body, element); - - const interactIds = new Set(); - - ["one", "two", "three"].forEach((value, idx) => { - decorateProposition = createDecoratePropositionForTest({ - type: DOM_ACTION_CLICK, - propositionId: `propId_${value}`, - itemId: `itemId_${idx}`, - trackingLabel: `trackingLabel${value}`, - notification: { - id: `notifyId${idx}`, - scope: "web://mywebsite.com", - scopeDetails: { something: true }, - }, - }); - const li = document.querySelector(`#something .${value}`); - decorateProposition(li); - - expect(getAttribute(li, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - `trackingLabel${value}`, - ); - const interactId = getAttribute(li, INTERACT_ID_DATA_ATTRIBUTE); - expect(interactId).not.toBeNull(); - - interactIds.add(interactId); - }); - expect(interactIds.size).toEqual(3); - }); - - it("does not set data-attribute for interact id and label if autoCollectPropositionInteractions does not include the appropriate decisionProvider and dom action is not 'click'", () => { - decorateProposition = createDecoratePropositionForTest({ - autoCollectPropositionInteractions: {}, - type: DOM_ACTION_SET_HTML, - trackingLabel: "myTrackingLabel", - }); - - const element = createNode( - "div", - { id: "something" }, - { innerText: "superfluous" }, - ); - appendNode(document.body, element); - - decorateProposition(element); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toBeNull(); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).toBeNull(); - }); - - it("does not set data-attribute for interact id and label if autoCollectPropositionInteractions does not include the appropriate decisionProvider and dom action is not 'click'", () => { - decorateProposition = createDecoratePropositionForTest({ - autoCollectPropositionInteractions: { - [ADOBE_JOURNEY_OPTIMIZER]: NEVER, - [ADOBE_TARGET]: NEVER, - }, - type: DOM_ACTION_SET_HTML, - trackingLabel: "myTrackingLabel", - }); - - const element = createNode( - "div", - { id: "something" }, - { innerText: "superfluous" }, - ); - appendNode(document.body, element); - - decorateProposition(element); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toBeNull(); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).toBeNull(); - }); - - it("sets data-attribute for interact id and label for all 'click' dom actions, regardless of autoCollectPropositionInteractions", () => { - decorateProposition = createDecoratePropositionForTest({ - autoCollectPropositionInteractions: {}, - type: DOM_ACTION_CLICK, - trackingLabel: "myTrackingLabel", - }); - - const element = createNode( - "div", - { id: "something" }, - { innerText: "superfluous" }, - ); - appendNode(document.body, element); - - decorateProposition(element); - - expect(getAttribute(element, CLICK_LABEL_DATA_ATTRIBUTE)).toEqual( - "myTrackingLabel", - ); - expect(getAttribute(element, INTERACT_ID_DATA_ATTRIBUTE)).not.toBeNull(); - }); -}); diff --git a/test/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js b/test/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js deleted file mode 100644 index d39d8c3b6..000000000 --- a/test/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js +++ /dev/null @@ -1,196 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createProcessDomAction from "../../../../../../src/components/Personalization/handlers/createProcessDomAction.js"; -import cleanUpDomChanges from "../../../../helpers/cleanUpDomChanges.js"; -import { - appendNode, - createNode, -} from "../../../../../../src/utils/dom/index.js"; -import { DOM_ACTION } from "../../../../../../src/constants/schema.js"; -import { - ADOBE_JOURNEY_OPTIMIZER, - ADOBE_TARGET, -} from "../../../../../../src/constants/decisionProvider.js"; -import { - ALWAYS, - NEVER, -} from "../../../../../../src/constants/propositionInteractionType.js"; -import createMockProposition from "../../../../helpers/createMockProposition.js"; - -describe("createProcessDomAction", () => { - let modules; - let logger; - let storeInteractionMeta; - let storeClickMeta; - let processDomAction; - - beforeEach(() => { - cleanUpDomChanges("click-element"); - - modules = { - typeA: jasmine.createSpy("typeA"), - typeB: jasmine.createSpy("typeB"), - }; - logger = jasmine.createSpyObj("logger", ["warn"]); - storeInteractionMeta = jasmine.createSpy("storeInteractionMeta"); - storeClickMeta = jasmine.createSpy("storeClickMeta"); - - processDomAction = createProcessDomAction({ - modules, - logger, - storeInteractionMeta, - storeClickMeta, - autoCollectPropositionInteractions: { - [ADOBE_JOURNEY_OPTIMIZER]: ALWAYS, - [ADOBE_TARGET]: NEVER, - }, - }); - }); - - afterEach(() => { - cleanUpDomChanges("click-element"); - }); - - it("returns an empty object if the item has no data, and logs missing type", () => { - const proposition = createMockProposition({ - schema: DOM_ACTION, - data: undefined, - }); - - expect(processDomAction(proposition.getItems()[0])).toEqual({ - setRenderAttempted: false, - includeInNotification: false, - }); - expect(logger.warn).toHaveBeenCalledWith( - "Invalid DOM action data: missing type.", - undefined, - ); - }); - - it("returns an empty object if the item has no type, and logs missing type", () => { - const proposition = createMockProposition({ - schema: DOM_ACTION, - data: {}, - }); - expect(processDomAction(proposition.getItems()[0])).toEqual({ - setRenderAttempted: false, - includeInNotification: false, - }); - expect(logger.warn).toHaveBeenCalledWith( - "Invalid DOM action data: missing type.", - {}, - ); - }); - - it("returns an empty object if the item has an unknown type, and logs unknown type", () => { - const proposition = createMockProposition({ - schema: DOM_ACTION, - data: { type: "typeC" }, - }); - expect(processDomAction(proposition.getItems()[0])).toEqual({ - setRenderAttempted: false, - includeInNotification: false, - }); - expect(logger.warn).toHaveBeenCalledWith( - "Invalid DOM action data: unknown type.", - { - type: "typeC", - }, - ); - }); - - it("returns an empty object if the item has no selector for a click type, and logs missing selector", () => { - const proposition = createMockProposition({ - schema: DOM_ACTION, - data: { type: "click" }, - }); - expect(processDomAction(proposition.getItems()[0])).toEqual({ - setRenderAttempted: false, - includeInNotification: false, - }); - expect(logger.warn).toHaveBeenCalledWith( - "Invalid DOM action data: missing selector.", - { - type: "click", - }, - ); - }); - - it("handles a click type", async () => { - const element = createNode("div", { - id: "click-element", - class: "click-element", - }); - element.innerHTML = "click element"; - appendNode(document.body, element); - - const proposition = createMockProposition( - { - id: "itemId", - schema: DOM_ACTION, - data: { type: "click", selector: ".click-element" }, - characteristics: { - trackingLabel: "mytrackinglabel", - }, - }, - { - characteristics: { - scopeType: "page", - trackingLabel: "mytrackinglabel", - }, - }, - ); - const clickAction = processDomAction(proposition.getItems()[0]); - - expect(clickAction).toEqual({ - setRenderAttempted: true, - includeInNotification: false, - }); - - expect(storeInteractionMeta).not.toHaveBeenCalled(); - expect(storeClickMeta).toHaveBeenCalledWith({ - selector: ".click-element", - meta: { - id: "id", - scope: "scope", - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - scopeType: "page", - trackingLabel: "mytrackinglabel", - }, - }, - trackingLabel: "mytrackinglabel", - scopeType: "proposition", - }, - }); - }); - - it("handles a non-click known type", () => { - const proposition = createMockProposition({ - schema: DOM_ACTION, - data: { type: "typeA", a: "b" }, - }); - const result = processDomAction(proposition.getItems()[0]); - expect(result).toEqual({ - render: jasmine.any(Function), - setRenderAttempted: true, - includeInNotification: true, - }); - expect(modules.typeA).not.toHaveBeenCalled(); - result.render(); - expect(modules.typeA).toHaveBeenCalledWith( - { type: "typeA", a: "b" }, - jasmine.any(Function), - ); - }); -}); diff --git a/test/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js b/test/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js deleted file mode 100644 index c0da602d8..000000000 --- a/test/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js +++ /dev/null @@ -1,137 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - ADOBE_JOURNEY_OPTIMIZER, - ADOBE_TARGET, -} from "../../../../../../src/constants/decisionProvider.js"; -import createProcessHtmlContent from "../../../../../../src/components/Personalization/handlers/createProcessHtmlContent.js"; -import createInteractionStorage from "../../../../../../src/components/Personalization/createInteractionStorage.js"; -import { HTML_CONTENT_ITEM } from "../../../../../../src/constants/schema.js"; -import { - ALWAYS, - NEVER, -} from "../../../../../../src/constants/propositionInteractionType.js"; -import createMockProposition from "../../../../helpers/createMockProposition.js"; - -describe("createProcessHtmlContent", () => { - let modules; - let logger; - let processHtmlContent; - - beforeEach(() => { - const { storeInteractionMeta } = createInteractionStorage(); - - modules = { - typeA: jasmine.createSpy("typeA"), - typeB: jasmine.createSpy("typeB"), - }; - logger = jasmine.createSpyObj("logger", ["warn"]); - - processHtmlContent = createProcessHtmlContent({ - modules, - logger, - storeInteractionMeta, - autoCollectPropositionInteractions: { - [ADOBE_JOURNEY_OPTIMIZER]: ALWAYS, - [ADOBE_TARGET]: NEVER, - }, - }); - }); - - it("returns an empty object if the item has no data", () => { - const proposition = createMockProposition({ - schema: HTML_CONTENT_ITEM, - data: undefined, - }); - - expect(processHtmlContent(proposition.getItems()[0])).toEqual({ - setRenderAttempted: false, - includeInNotification: false, - }); - expect(logger.warn).not.toHaveBeenCalled(); - }); - - it("returns an empty object if the item has no type", () => { - const proposition = createMockProposition({ - schema: HTML_CONTENT_ITEM, - data: { selector: ".myselector" }, - }); - - expect(processHtmlContent(proposition.getItems()[0])).toEqual({ - setRenderAttempted: false, - includeInNotification: false, - }); - expect(logger.warn).not.toHaveBeenCalled(); - }); - - it("returns an empty object if the item has no selector", () => { - const proposition = createMockProposition({ - schema: HTML_CONTENT_ITEM, - data: { type: "mytype" }, - }); - - expect(processHtmlContent(proposition.getItems()[0])).toEqual({ - setRenderAttempted: false, - includeInNotification: false, - }); - expect(logger.warn).not.toHaveBeenCalled(); - }); - - it("returns an empty object if the item has an unknown type, and logs unknown type", () => { - const proposition = createMockProposition({ - schema: HTML_CONTENT_ITEM, - data: { - type: "typeC", - selector: ".myselector", - content: "mycontent", - }, - }); - - expect(processHtmlContent(proposition.getItems()[0])).toEqual({ - setRenderAttempted: false, - includeInNotification: false, - }); - expect(logger.warn).toHaveBeenCalledWith("Invalid HTML content data", { - type: "typeC", - selector: ".myselector", - content: "mycontent", - }); - }); - - it("handles a known type", () => { - const proposition = createMockProposition({ - schema: HTML_CONTENT_ITEM, - data: { - type: "typeA", - selector: ".myselector", - content: "mycontent", - }, - }); - - const result = processHtmlContent(proposition.getItems()[0]); - expect(result).toEqual({ - render: jasmine.any(Function), - setRenderAttempted: true, - includeInNotification: true, - }); - expect(modules.typeA).not.toHaveBeenCalled(); - result.render(); - expect(modules.typeA).toHaveBeenCalledWith( - { - type: "typeA", - selector: ".myselector", - content: "mycontent", - }, - jasmine.any(Function), - ); - }); -}); diff --git a/test/unit/specs/components/Personalization/handlers/createProcessInAppMessage.spec.js b/test/unit/specs/components/Personalization/handlers/createProcessInAppMessage.spec.js deleted file mode 100644 index eb6445e25..000000000 --- a/test/unit/specs/components/Personalization/handlers/createProcessInAppMessage.spec.js +++ /dev/null @@ -1,139 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createProcessInAppMessage from "../../../../../../src/components/Personalization/handlers/createProcessInAppMessage.js"; - -describe("Personalization::handlers::createProcessInAppMessage", () => { - let item; - let data; - let meta; - let modules; - let logger; - let processInAppMessage; - - beforeEach(() => { - item = { - getData() { - return data; - }, - getProposition() { - return { - getNotification: () => meta, - shouldSuppressDisplay: () => false, - }; - }, - }; - modules = { - defaultContent: jasmine.createSpy("defaultContent"), - }; - logger = jasmine.createSpyObj("logger", ["warn"]); - - processInAppMessage = createProcessInAppMessage({ - modules, - logger, - }); - }); - - it("returns an empty object if the item has no data, and logs missing type", () => { - data = undefined; - expect(processInAppMessage(item)).toEqual({}); - expect(logger.warn).toHaveBeenCalledWith( - "Invalid in-app message data: undefined.", - undefined, - ); - }); - - it("returns an empty object if the item has an unknown type, and logs unknown type", () => { - data = { type: "wtf" }; - expect(processInAppMessage(item)).toEqual({}); - expect(logger.warn).toHaveBeenCalledWith( - "Invalid in-app message data: unknown type.", - data, - ); - }); - - it("handles a valid in app message type", () => { - meta = { - id: "abc", - scope: "web://mywebsite.com", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "21d", - characteristics: { - eventToken: "yadayaya", - }, - activity: { - id: "foo#bar", - }, - }, - }; - data = { - mobileParameters: { - verticalAlign: "center", - horizontalAlign: "center", - uiTakeover: true, - width: 72, - backdropColor: "#4CA206", - height: 63, - }, - webParameters: {}, - content: "", - contentType: "text/html", - qualifiedDate: 1694731987996, - }; - - const result = processInAppMessage(item); - expect(result).toEqual({ - render: jasmine.any(Function), - setRenderAttempted: true, - includeInNotification: true, - }); - expect(modules.defaultContent).not.toHaveBeenCalled(); - result.render(); - expect(modules.defaultContent).toHaveBeenCalledWith({ ...data, meta }); - }); - - it("handles an invalid in app message type, and logs", () => { - meta = { - id: "abc", - scope: "web://mywebsite.com", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "21d", - characteristics: { - eventToken: "yadayaya", - }, - activity: { - id: "foo#bar", - }, - }, - }; - data = { - mobileParameters: { - verticalAlign: "center", - horizontalAlign: "center", - uiTakeover: true, - width: 72, - backdropColor: "#4CA206", - height: 63, - }, - webParameters: {}, - contentType: "text/html", - qualifiedDate: 1694731987996, - }; - - expect(processInAppMessage(item)).toEqual({}); - expect(logger.warn).toHaveBeenCalledWith( - "Invalid in-app message data: missing property 'content'.", - data, - ); - }); -}); diff --git a/test/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js b/test/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js deleted file mode 100644 index 88183414b..000000000 --- a/test/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js +++ /dev/null @@ -1,329 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createProcessPropositions from "../../../../../../src/components/Personalization/handlers/createProcessPropositions.js"; -import injectCreateProposition from "../../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; - -describe("createProcessPropositions", () => { - let schemaProcessors; - let logger; - let createProposition; - let processPropositions; - - let render; - let always; - let noNotification; - let never; - let noRender; - let redirect; - - beforeEach(() => { - render = jasmine.createSpy("render").and.returnValue(Promise.resolve()); - always = (item) => ({ - render: () => render(item.getData()), - setRenderAttempted: true, - includeInNotification: true, - }); - noNotification = (item) => ({ - render: () => render(item.getData()), - setRenderAttempted: true, - includeInNotification: false, - }); - never = () => ({}); - noRender = () => ({ - setRenderAttempted: true, - includeInNotification: true, - }); - redirect = (item) => ({ - render: () => render(item.getData()), - setRenderAttempted: true, - onlyRenderThis: true, - }); - - schemaProcessors = { always, noNotification, never, noRender, redirect }; - logger = jasmine.createSpyObj("logger", [ - "info", - "error", - "logOnContentRendering", - ]); - processPropositions = createProcessPropositions({ - schemaProcessors, - logger, - }); - createProposition = injectCreateProposition({ - preprocess: (data) => data, - isPageWideSurface: () => false, - }); - }); - - it("handles no propositions", async () => { - const result = processPropositions([]); - expect(result).toEqual({ - render: jasmine.any(Function), - returnedPropositions: [], - returnedDecisions: [], - }); - await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); - await expectAsync(result.render()).toBeResolvedTo([]); - }); - - it("processes a proposition with an always item", async () => { - const prop1 = createProposition({ - id: "always1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "always", data: "mydata" }], - }); - const result = processPropositions([prop1]); - expect(result).toEqual({ - render: jasmine.any(Function), - returnedPropositions: [ - { - id: "always1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "always", data: "mydata" }], - renderAttempted: true, - }, - ], - returnedDecisions: [], - }); - await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); - expect(render).not.toHaveBeenCalled(); - await expectAsync(result.render()).toBeResolvedTo([ - { - id: "always1", - scope: "myscope", - scopeDetails: { a: 1 }, - }, - ]); - expect(render).toHaveBeenCalledWith("mydata"); - }); - - it("processes a proposition with a noNotification item", async () => { - const prop1 = createProposition({ - id: "noNotification1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "noNotification", data: "mydata" }], - }); - const result = processPropositions([prop1]); - expect(result).toEqual({ - render: jasmine.any(Function), - returnedPropositions: [ - { - id: "noNotification1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "noNotification", data: "mydata" }], - renderAttempted: true, - }, - ], - returnedDecisions: [], - }); - await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); - expect(render).not.toHaveBeenCalled(); - await expectAsync(result.render()).toBeResolvedTo([]); - expect(render).toHaveBeenCalledWith("mydata"); - }); - - it("processes a proposition with a never item", async () => { - const prop1 = createProposition({ - id: "never1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "never", data: "mydata" }], - }); - const result = processPropositions([prop1]); - expect(result).toEqual({ - render: jasmine.any(Function), - returnedPropositions: [ - { - id: "never1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "never", data: "mydata" }], - renderAttempted: false, - }, - ], - returnedDecisions: [ - { - id: "never1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "never", data: "mydata" }], - }, - ], - }); - await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); - await expectAsync(result.render()).toBeResolvedTo([]); - expect(render).not.toHaveBeenCalled(); - }); - - it("processes a proposition with a noRender item", async () => { - const prop1 = createProposition({ - id: "noRender1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "noRender", data: "mydata" }], - }); - const result = processPropositions([prop1]); - expect(result).toEqual({ - render: jasmine.any(Function), - returnedPropositions: [ - { - id: "noRender1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "noRender", data: "mydata" }], - renderAttempted: true, - }, - ], - returnedDecisions: [], - }); - await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); - await expectAsync(result.render()).toBeResolvedTo([ - { - id: "noRender1", - scope: "myscope", - scopeDetails: { a: 1 }, - }, - ]); - expect(render).not.toHaveBeenCalled(); - }); - - it("processes a proposition with a redirect item", async () => { - const prop1 = createProposition({ - id: "redirect1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "redirect", data: "mydata" }], - }); - const result = processPropositions([prop1]); - expect(result).toEqual({ - render: jasmine.any(Function), - returnedPropositions: [ - { - id: "redirect1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "redirect", data: "mydata" }], - renderAttempted: true, - }, - ], - returnedDecisions: [], - }); - await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); - expect(render).not.toHaveBeenCalled(); - await expectAsync(result.render()).toBeResolvedTo([]); - expect(render).toHaveBeenCalledWith("mydata"); - }); - - it("doesn't render other propositions if one has a redirect", async () => { - const prop1 = createProposition({ - id: "always1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "always", data: "mydata1" }], - }); - const prop2 = createProposition({ - id: "redirect2", - scope: "myscope", - scopeDetails: { a: 2 }, - items: [{ schema: "redirect", data: "mydata2" }], - }); - const prop3 = createProposition({ - id: "always3", - scope: "myscope", - scopeDetails: { a: 3 }, - items: [{ schema: "always", data: "mydata3" }], - }); - const result = processPropositions([prop1, prop2, prop3]); - - expect(result).toEqual({ - render: jasmine.any(Function), - returnedPropositions: [ - { - id: "redirect2", - scope: "myscope", - scopeDetails: { a: 2 }, - items: [{ schema: "redirect", data: "mydata2" }], - renderAttempted: true, - }, - { - id: "always1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "always", data: "mydata1" }], - renderAttempted: false, - }, - { - id: "always3", - scope: "myscope", - scopeDetails: { a: 3 }, - items: [{ schema: "always", data: "mydata3" }], - renderAttempted: false, - }, - ], - returnedDecisions: [ - { - id: "always1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "always", data: "mydata1" }], - }, - { - id: "always3", - scope: "myscope", - scopeDetails: { a: 3 }, - items: [{ schema: "always", data: "mydata3" }], - }, - ], - }); - await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); - expect(render).not.toHaveBeenCalled(); - await expectAsync(result.render()).toBeResolvedTo([]); - expect(render).toHaveBeenCalledWith("mydata2"); - }); - - it("processes nonRenderPropositions", async () => { - const prop1 = createProposition({ - id: "always1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "always", data: "mydata" }], - }); - const result = processPropositions([], [prop1]); - expect(result).toEqual({ - render: jasmine.any(Function), - returnedPropositions: [ - { - id: "always1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "always", data: "mydata" }], - renderAttempted: false, - }, - ], - returnedDecisions: [ - { - id: "always1", - scope: "myscope", - scopeDetails: { a: 1 }, - items: [{ schema: "always", data: "mydata" }], - }, - ], - }); - await expect(logger.logOnContentRendering).not.toHaveBeenCalled(); - await expectAsync(result.render()).toBeResolvedTo([]); - expect(render).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Personalization/handlers/createProcessRedirect.spec.js b/test/unit/specs/components/Personalization/handlers/createProcessRedirect.spec.js deleted file mode 100644 index 31436d704..000000000 --- a/test/unit/specs/components/Personalization/handlers/createProcessRedirect.spec.js +++ /dev/null @@ -1,106 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { defer } from "../../../../../../src/utils/index.js"; -import flushPromiseChains from "../../../../helpers/flushPromiseChains.js"; -import createProcessRedirect from "../../../../../../src/components/Personalization/handlers/createProcessRedirect.js"; - -describe("createProcessRedirect", () => { - let logger; - let executeRedirect; - let collect; - let collectDefer; - let item; - let data; - let proposition; - let meta; - - let processRedirect; - - beforeEach(() => { - logger = jasmine.createSpyObj("logger", ["warn", "logOnContentRendering"]); - executeRedirect = jasmine.createSpy("executeRedirect"); - collectDefer = defer(); - collect = jasmine - .createSpy("collect") - .and.returnValue(collectDefer.promise); - proposition = { - getNotification() { - return meta; - }, - }; - item = { - getData() { - return data; - }, - getProposition() { - return proposition; - }, - }; - - processRedirect = createProcessRedirect({ - logger, - executeRedirect, - collect, - }); - }); - - it("returns an empty object if the item has no data", () => { - data = undefined; - expect(processRedirect(item)).toEqual({}); - expect(logger.warn).toHaveBeenCalledWith( - "Invalid Redirect data", - undefined, - ); - }); - - it("returns an empty object if the item has no content", () => { - data = { a: 1 }; - expect(processRedirect(item)).toEqual({}); - expect(logger.logOnContentRendering).not.toHaveBeenCalled(); - expect(logger.warn).toHaveBeenCalledWith("Invalid Redirect data", { a: 1 }); - }); - - it("redirects", async () => { - data = { content: "mycontent" }; - meta = "mymetavalue"; - const result = processRedirect(item); - expect(result).toEqual({ - render: jasmine.any(Function), - setRenderAttempted: true, - onlyRenderThis: true, - }); - expect(collect).not.toHaveBeenCalled(); - expect(executeRedirect).not.toHaveBeenCalled(); - const renderPromise = result.render(); - await flushPromiseChains(); - expect(collect).toHaveBeenCalledWith({ - decisionsMeta: ["mymetavalue"], - documentMayUnload: true, - }); - expect(executeRedirect).not.toHaveBeenCalled(); - collectDefer.resolve(); - await flushPromiseChains(); - expect(logger.logOnContentRendering).toHaveBeenCalledTimes(1); - expect(executeRedirect).toHaveBeenCalledWith("mycontent"); - expect(await renderPromise).toBeUndefined(); - }); - - it("doesn't eat the exception", async () => { - data = { content: "mycontent" }; - meta = "mymetavalue"; - const result = processRedirect(item); - const renderPromise = result.render(); - collectDefer.reject("myerror"); - expect(logger.logOnContentRendering).not.toHaveBeenCalled(); - await expectAsync(renderPromise).toBeRejectedWith("myerror"); - }); -}); diff --git a/test/unit/specs/components/Personalization/handlers/injectCreateProposition.spec.js b/test/unit/specs/components/Personalization/handlers/injectCreateProposition.spec.js deleted file mode 100644 index ba06288a7..000000000 --- a/test/unit/specs/components/Personalization/handlers/injectCreateProposition.spec.js +++ /dev/null @@ -1,82 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import injectCreateProposition from "../../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; - -describe("injectCreateProposition", () => { - const preprocess = (data) => `preprocessed ${data}`; - const isPageWideSurface = (scope) => scope === "__surface__"; - const createProposition = injectCreateProposition({ - preprocess, - isPageWideSurface, - }); - - it("creates a proposition from nothing", () => { - const proposition = createProposition({}); - - expect(proposition.getScope()).toBeUndefined(); - expect(proposition.getScopeType()).toEqual("proposition"); - expect(proposition.getItems()).toEqual([]); - expect(proposition.getNotification()).toEqual({ - id: undefined, - scope: undefined, - scopeDetails: undefined, - }); - expect(proposition.toJSON()).toEqual({}); - }); - - it("creates a full proposition", () => { - const proposition = createProposition({ - id: "id", - scope: "scope", - scopeDetails: { characteristics: { scopeType: "view" } }, - items: [ - { - schema: "schema", - data: "data", - characteristics: { trackingLabel: "trackingLabel" }, - }, - ], - }); - - expect(proposition.getScope()).toEqual("scope"); - expect(proposition.getScopeType()).toEqual("view"); - const item = proposition.getItems()[0]; - expect(item.getSchema()).toEqual("schema"); - expect(item.getData()).toEqual("preprocessed data"); - expect(item.getProposition()).toEqual(proposition); - expect(item.getTrackingLabel()).toEqual("trackingLabel"); - expect(item.getOriginalItem()).toEqual({ - schema: "schema", - data: "data", - characteristics: { trackingLabel: "trackingLabel" }, - }); - expect(proposition.getNotification()).toEqual({ - id: "id", - scope: "scope", - scopeDetails: { characteristics: { scopeType: "view" } }, - }); - }); - - it("creates a page wide surface proposition", () => { - const proposition = createProposition({ - scope: "__surface__", - }); - expect(proposition.getScopeType()).toEqual("page"); - }); - - it("creates a page wide scope proposition", () => { - const proposition = createProposition({ - scope: "__view__", - }); - expect(proposition.getScopeType()).toEqual("page"); - }); -}); diff --git a/test/unit/specs/components/Personalization/handlers/processDefaultContent.spec.js b/test/unit/specs/components/Personalization/handlers/processDefaultContent.spec.js deleted file mode 100644 index 99aa61302..000000000 --- a/test/unit/specs/components/Personalization/handlers/processDefaultContent.spec.js +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import processDefaultContent from "../../../../../../src/components/Personalization/handlers/processDefaultContent.js"; - -describe("processDefaultContent", () => { - it("always renders the default content", () => { - const result = processDefaultContent(); - expect(result).toEqual({ - render: jasmine.any(Function), - setRenderAttempted: true, - includeInNotification: true, - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js b/test/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js deleted file mode 100644 index 3fc871fe7..000000000 --- a/test/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js +++ /dev/null @@ -1,458 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - buildStyleFromMobileParameters, - createIframe, - createIframeClickHandler, - displayHTMLContentInIframe, -} from "../../../../../../../src/components/Personalization/in-app-message-actions/actions/displayIframeContent.js"; -import cleanUpDomChanges from "../../../../../helpers/cleanUpDomChanges.js"; -import { getNonce } from "../../../../../../../src/components/Personalization/dom-actions/dom/index.js"; -import { testResetCachedNonce } from "../../../../../../../src/components/Personalization/dom-actions/dom/getNonce.js"; -import { TEXT_HTML } from "../../../../../../../src/constants/contentType.js"; - -describe("DOM Actions on Iframe", () => { - beforeEach(() => { - cleanUpDomChanges("alloy-messaging-container"); - cleanUpDomChanges("alloy-overlay-container"); - cleanUpDomChanges("alloy-content-iframe"); - }); - - afterEach(() => { - cleanUpDomChanges("alloy-messaging-container"); - cleanUpDomChanges("alloy-overlay-container"); - cleanUpDomChanges("alloy-content-iframe"); - }); - describe("buildStyleFromParameters", () => { - it("should build the style object correctly", () => { - const mobileParameters = { - verticalAlign: "center", - width: 80, - horizontalAlign: "left", - backdropColor: "rgba(0, 0, 0, 0.7)", - height: 60, - cornerRadius: 10, - horizontalInset: 5, - verticalInset: 10, - uiTakeover: true, - }; - const style = buildStyleFromMobileParameters(mobileParameters); - expect(style.width).toBe("80%"); - expect(style.backgroundColor).toBe("rgba(0, 0, 0, 0.7)"); - expect(style.borderRadius).toBe("10px"); - expect(style.border).toBe("none"); - expect(style.position).toBe("fixed"); - expect(style.overflow).toBe("hidden"); - expect(style.left).toBe("5%"); - expect(style.height).toBe("60vh"); - }); - }); - - describe("createIframe function", () => { - it("should create an iframe element with specified properties", () => { - const mockHtmlContent = - '\u003c!doctype html\u003e\\n\u003chtml\u003e\\n\u003chead\u003e\\n \u003ctitle\u003eBumper Sale!\u003c/title\u003e\\n \u003cstyle\u003e\\n body {\\n margin: 0;\\n padding: 0;\\n font-family: Arial, sans-serif;\\n }\\n\\n #announcement {\\n position: fixed;\\n top: 0;\\n left: 0;\\n width: 100%;\\n height: 100%;\\n background-color: rgba(0, 0, 0, 0.8);\\n display: flex;\\n flex-direction: column;\\n align-items: center;\\n justify-content: center;\\n color: #fff;\\n }\\n\\n #announcement img {\\n max-width: 80%;\\n height: auto;\\n margin-bottom: 20px;\\n }\\n\\n #cross {\\n position: absolute;\\n top: 10px;\\n right: 10px;\\n cursor: pointer;\\n font-size: 24px;\\n color: #fff;\\n }\\n\\n #buttons {\\n display: flex;\\n justify-content: center;\\n margin-top: 20px;\\n }\\n\\n #buttons a {\\n margin: 0 10px;\\n padding: 10px 20px;\\n background-color: #ff5500;\\n color: #fff;\\n text-decoration: none;\\n border-radius: 4px;\\n font-weight: bold;\\n transition: background-color 0.3s ease;\\n }\\n\\n #buttons a:hover {\\n background-color: #ff3300;\\n }\\n \u003c/style\u003e\\n\u003c/head\u003e\\n\u003cbody\u003e\\n\u003cdiv id\u003d"announcement" class\u003d"fullscreen"\u003e\\n \u003cspan id\u003d"cross" class\u003d"dismiss"\u003e✕\u003c/span\u003e\\n \u003ch2\u003eBlack Friday Sale!\u003c/h2\u003e\\n \u003cimg src\u003d"https://source.unsplash.com/800x600/?technology,gadget" alt\u003d"Technology Image"\u003e\\n \u003cp\u003eDon\u0027t miss out on our incredible discounts and deals at our gadgets!\u003c/p\u003e\\n \u003cdiv id\u003d"buttons"\u003e\\n \u003ca class\u003d"forward" href\u003d"http://localhost:3000/"\u003eShop\u003c/a\u003e\\n \u003ca class\u003d"dismiss"\u003eDismiss\u003c/a\u003e\\n \u003c/div\u003e\\n\u003c/div\u003e\\n\\n\u003c/body\u003e\u003c/html\u003e\\n'; - const mockClickHandler = jasmine.createSpy("clickHandler"); - - const iframe = createIframe(mockHtmlContent, mockClickHandler); - expect(iframe).toBeDefined(); - expect(iframe instanceof HTMLIFrameElement).toBe(true); - expect(iframe.src).toContain("blob:"); - }); - - it("should set 'nonce' attribute on script tag if it exists", async () => { - const mockHtmlContentWithScript = - "\n" + - "\n" + - "\n" + - " Bumper Sale!\n" + - " \n" + - "\n" + - "\n" + - '
    \n' + - ' \n' + - "

    Black Friday Sale!

    \n" + - ' Technology Image\n' + - "

    Don't miss out on our incredible discounts and deals at our gadgets!

    \n" + - '
    \n' + - ' Shop\n' + - ' Dismiss\n' + - "
    \n" + - "
    \n" + - "\n" + - "\n" + - "\n"; - - testResetCachedNonce(); - - const childElement = document.createElement("div"); - childElement.setAttribute("nonce", "12345"); - const parentElement = document.createElement("div"); - parentElement.appendChild(childElement); - const originalGetNonce = getNonce(parentElement); - - const mockClickHandler = jasmine.createSpy("clickHandler"); - const iframe = createIframe(mockHtmlContentWithScript, mockClickHandler); - - const blob = await fetch(iframe.src).then((r) => r.blob()); - const text = await blob.text(); - const parser = new DOMParser(); - const iframeDocument = parser.parseFromString(text, TEXT_HTML); - - const scriptTag = iframeDocument.querySelector("script"); - expect(scriptTag).toBeDefined(); - expect(scriptTag.getAttribute("nonce")).toEqual(originalGetNonce); - }); - }); - - describe("createIframeClickHandler", () => { - let container; - let mockedInteract; - let mobileParameters; - - beforeEach(() => { - container = document.createElement("div"); - container.setAttribute("id", "alloy-messaging-container"); - document.body.appendChild(container); - - mockedInteract = jasmine.createSpy("interact"); - - mobileParameters = { - verticalAlign: "center", - width: 80, - horizontalAlign: "left", - backdropColor: "rgba(0, 0, 0, 0.7)", - height: 60, - cornerRadius: 10, - horizontalInset: 5, - verticalInset: 10, - }; - }); - - it("should remove display message when dismiss is clicked and UI takeover is false", () => { - Object.assign(mobileParameters, { - uiTakeover: false, - }); - - const anchor = document.createElement("a"); - anchor.setAttribute("data-uuid", "12345"); - anchor.href = "adbinapp://dismiss?interaction=cancel"; - anchor.innerText = "Cancel"; - - const mockEvent = { - target: anchor, - preventDefault: () => {}, - stopImmediatePropagation: () => {}, - }; - const iframeClickHandler = createIframeClickHandler(mockedInteract); - iframeClickHandler(mockEvent); - const alloyMessagingContainer = document.getElementById( - "alloy-messaging-container", - ); - expect(mockedInteract).toHaveBeenCalledOnceWith("dismiss", { - label: "Cancel", - id: "cancel", - uuid: "12345", - link: "", - }); - expect(alloyMessagingContainer).toBeNull(); - }); - - it("should remove display message when dismiss is clicked and Ui takeover is true", () => { - Object.assign(mobileParameters, { - uiTakeover: true, - }); - - const overlayContainer = document.createElement("div"); - overlayContainer.setAttribute("id", "alloy-overlay-container"); - - document.body.appendChild(overlayContainer); - - const anchor = document.createElement("a"); - anchor.setAttribute("data-uuid", "54321"); - anchor.href = "adbinapp://dismiss?interaction=cancel"; - anchor.innerText = "Aloha"; - - const mockEvent = { - target: anchor, - preventDefault: () => {}, - stopImmediatePropagation: () => {}, - }; - const iframeClickHandler = createIframeClickHandler(mockedInteract); - iframeClickHandler(mockEvent); - const overlayContainerAfterDismissal = document.getElementById( - "alloy-overlay-container", - ); - expect(mockedInteract).toHaveBeenCalledOnceWith("dismiss", { - label: "Aloha", - id: "cancel", - uuid: "54321", - link: "", - }); - - expect(overlayContainerAfterDismissal).toBeNull(); - }); - - it("extracts propositionAction details from anchor tag and sends to interact()", () => { - const mockNavigateToUrl = jasmine.createSpy("mockNavigateToUrl"); - Object.assign(mobileParameters, { - uiTakeover: true, - }); - - const anchor = document.createElement("a"); - anchor.setAttribute("data-uuid", "blippi"); - anchor.href = - "adbinapp://dismiss?interaction=accept&link=https%3A%2F%2Fwww.google.com"; - anchor.innerText = "Woof"; - - const mockEvent = { - target: anchor, - preventDefault: () => {}, - stopImmediatePropagation: () => {}, - }; - const iframeClickHandler = createIframeClickHandler( - mockedInteract, - mockNavigateToUrl, - ); - iframeClickHandler(mockEvent); - const overlayContainerAfterDismissal = document.getElementById( - "alloy-overlay-container", - ); - expect(mockedInteract).toHaveBeenCalledOnceWith("dismiss", { - label: "Woof", - id: "accept", - uuid: "blippi", - link: "https://www.google.com", - }); - expect(mockNavigateToUrl).toHaveBeenCalledOnceWith( - "https://www.google.com", - true, - ); - expect(overlayContainerAfterDismissal).toBeNull(); - }); - }); - - describe("displayHTMLContentInIframe", () => { - let originalAppendChild; - let originalBodyStyle; - let mockCollect; - let originalCreateIframe; - - beforeEach(() => { - mockCollect = jasmine.createSpy("collect"); - originalAppendChild = document.body.appendChild; - document.body.appendChild = jasmine.createSpy("appendChild"); - originalBodyStyle = document.body.style; - document.body.style = {}; - originalCreateIframe = window.createIframe; - - window.createIframe = jasmine - .createSpy("createIframe") - .and.callFake(() => { - const element = document.createElement("iframe"); - element.id = "alloy-content-iframe"; - return element; - }); - }); - - afterEach(() => { - document.body.appendChild = originalAppendChild; - document.body.style = originalBodyStyle; - document.body.innerHTML = ""; - window.createIframe = originalCreateIframe; - }); - - it("should display HTML content in iframe with overlay using mobile parameters", () => { - const settings = { - type: "custom", - webParameters: { info: "this is a placeholder" }, - mobileParameters: { - verticalAlign: "center", - dismissAnimation: "bottom", - verticalInset: 20, - backdropOpacity: 0.78, - cornerRadius: 20, - gestures: {}, - horizontalInset: -14, - uiTakeover: true, - horizontalAlign: "center", - width: 72, - displayAnimation: "bottom", - backdropColor: "#4CA206", - height: 63, - }, - content: - '\n\n\n Bumper Sale!\n \n\n\n
    \n \n

    Black Friday Sale!

    \n Technology Image\n

    Don\'t miss out on our incredible discounts and deals at our gadgets!

    \n
    \n Shop\n Dismiss\n
    \n
    \n\n\n\n', - contentType: TEXT_HTML, - schema: "https://ns.adobe.com/personalization/message/in-app", - meta: { - id: "9441e3c4-d673-4c1b-8fb9-d1c0f7826dcc", - scope: "mobileapp://com.adobe.iamTutorialiOS", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "8794bfb9-3254-478a-860e-04f9da59ad82", - characteristics: { - eventToken: - "eyJtZXNzYWdlRXhlY3V0aW9uIjp7Im1lc3NhZ2VFeGVjdXRpb25JRCI6Ik5BIiwibWVzc2FnZUlEIjoiODc5NGJmYjktMzI1NC00NzhhLTg2MGUtMDRmOWRhNTlhZDgyIiwibWVzc2FnZVB1YmxpY2F0aW9uSUQiOiI1ZmYzZmM5Zi0zZTY2LTRiNzktODRmMS1kNzUzMGYwOWQ1ZTIiLCJtZXNzYWdlVHlwZSI6Im1hcmtldGluZyIsImNhbXBhaWduSUQiOiIyOGJlYTAxMS1lNTk2LTQ0MjktYjhmNy1iNWJkNjMwYzY3NDMiLCJjYW1wYWlnblZlcnNpb25JRCI6ImQ5OTQzODJhLTJjZDAtNDkwYS04NGM4LWM0NTk2NmMwYjYwZiIsImNhbXBhaWduQWN0aW9uSUQiOiJiNDU0OThjYi05NmQxLTQxN2EtODFlYi0yZjA5MTU3YWQ4YzYifSwibWVzc2FnZVByb2ZpbGUiOnsibWVzc2FnZVByb2ZpbGVJRCI6IjQ2MTg5Yjg1LWEwYTYtNDc4NS1hNmJlLTg4OWRiZjU3NjhiOSIsImNoYW5uZWwiOnsiX2lkIjoiaHR0cHM6Ly9ucy5hZG9iZS5jb20veGRtL2NoYW5uZWxzL2luQXBwIiwiX3R5cGUiOiJodHRwczovL25zLmFkb2JlLmNvbS94ZG0vY2hhbm5lbC10eXBlcy9pbkFwcCJ9fX0=", - }, - activity: { - id: "28bea011-e596-4429-b8f7-b5bd630c6743#b45498cb-96d1-417a-81eb-2f09157ad8c6", - }, - }, - }, - }; - - displayHTMLContentInIframe(settings, mockCollect); - - expect(document.body.appendChild).toHaveBeenCalledTimes(2); - }); - - it("should display HTML content in iframe with overlay using web parameters", () => { - const settings = { - webParameters: { - "alloy-overlay-container": { - style: { - position: "fixed", - top: "0", - left: "0", - width: "100%", - height: "100%", - background: "transparent", - opacity: 0.5, - backgroundColor: "#FFFFFF", - }, - params: { - enabled: true, - parentElement: "body", - insertionMethod: "appendChild", - }, - }, - "alloy-messaging-container": { - style: { - width: "72%", - backgroundColor: "orange", - borderRadius: "20px", - border: "none", - position: "fixed", - overflow: "hidden", - left: "50%", - transform: "translateX(-50%) translateY(-50%)", - top: "50%", - display: "flex", - alignItems: "center", - justifyContent: "center", - height: "63vh", - }, - params: { - enabled: true, - parentElement: "body", - insertionMethod: "appendChild", - }, - }, - "alloy-content-iframe": { - style: { - width: "100%", - height: "100%", - }, - params: { - enabled: true, - parentElement: "#alloy-messaging-container", - insertionMethod: "appendChild", - }, - }, - }, - content: - '\n\n\n Bumper Sale!\n \n\n\n
    \n \n

    Black Friday Sale!

    \n Technology Image\n

    Don\'t miss out on our incredible discounts and deals at our gadgets!

    \n
    \n Shop\n Dismiss\n
    \n
    \n\n\n\n', - contentType: TEXT_HTML, - schema: "https://ns.adobe.com/personalization/message/in-app", - }; - - displayHTMLContentInIframe(settings, mockCollect); - expect(document.body.appendChild).toHaveBeenCalledTimes(2); - }); - it("should display HTML content in iframe with no overlay using web parameters", () => { - const settings = { - webParameters: { - "alloy-overlay-container": { - style: { - position: "fixed", - top: "0", - left: "0", - width: "100%", - height: "100%", - background: "transparent", - opacity: 0.5, - backgroundColor: "#FFFFFF", - }, - params: { - enabled: false, - parentElement: "body", - insertionMethod: "appendChild", - }, - }, - "alloy-messaging-container": { - style: { - width: "72%", - backgroundColor: "orange", - borderRadius: "20px", - border: "none", - position: "fixed", - overflow: "hidden", - left: "50%", - transform: "translateX(-50%) translateY(-50%)", - top: "50%", - display: "flex", - alignItems: "center", - justifyContent: "center", - height: "63vh", - }, - params: { - enabled: true, - parentElement: "body", - insertionMethod: "appendChild", - }, - }, - "alloy-content-iframe": { - style: { - width: "100%", - height: "100%", - }, - params: { - enabled: true, - parentElement: "#alloy-messaging-container", - insertionMethod: "appendChild", - }, - }, - }, - content: - '\n\n\n Bumper Sale!\n \n\n\n
    \n \n

    Black Friday Sale!

    \n Technology Image\n

    Don\'t miss out on our incredible discounts and deals at our gadgets!

    \n
    \n Shop\n Dismiss\n
    \n
    \n\n\n\n', - contentType: TEXT_HTML, - schema: "https://ns.adobe.com/personalization/message/in-app", - }; - - displayHTMLContentInIframe(settings, mockCollect); - expect(document.body.appendChild).toHaveBeenCalledTimes(1); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.spec.js b/test/unit/specs/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.spec.js deleted file mode 100644 index 0767a148b..000000000 --- a/test/unit/specs/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import initInAppMessageActionsModules from "../../../../../../src/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.js"; - -describe("Personalization::turbine::initInAppMessageActionsModules", () => { - const noop = () => undefined; - - it("should have all the required modules", () => { - const messagingActionsModules = initInAppMessageActionsModules(noop); - - expect(Object.keys(messagingActionsModules).length).toEqual(1); - - expect(messagingActionsModules.defaultContent).toEqual( - jasmine.any(Function), - ); - }); -}); diff --git a/test/unit/specs/components/Personalization/in-app-message-actions/utils.spec.js b/test/unit/specs/components/Personalization/in-app-message-actions/utils.spec.js deleted file mode 100644 index 485d2a645..000000000 --- a/test/unit/specs/components/Personalization/in-app-message-actions/utils.spec.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { removeElementById } from "../../../../../../src/components/Personalization/in-app-message-actions/utils.js"; - -describe("removeElementById", () => { - beforeEach(() => { - document.body.innerHTML = ` -
    -`; - }); - - it("should remove an element when it exists", () => { - const elementId = "test-element"; - const element = document.getElementById(elementId); - - expect(element).toBeTruthy(); - - removeElementById(elementId); - - expect(document.getElementById(elementId)).toBeNull(); - }); - - it("should do nothing when the element does not exist", () => { - const nonExistentId = "non-existent-element"; - - removeElementById(nonExistentId); - - expect(document.getElementById(nonExistentId)).toBeNull(); - }); -}); diff --git a/test/unit/specs/components/Personalization/responsesMock/eventResponses.js b/test/unit/specs/components/Personalization/responsesMock/eventResponses.js deleted file mode 100644 index d056d6e40..000000000 --- a/test/unit/specs/components/Personalization/responsesMock/eventResponses.js +++ /dev/null @@ -1,449 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -export const SCOPES_FOO1_FOO2_DECISIONS = [ - { - id: "TNT:ABC:A", - scope: "Foo1", - scopeDetails: { - blah: "test", - }, - items: [ - { - schema: "https://ns.adove.com/experience/item-article", - data: { - id: "1", - url: "https://foo.com/article/1", - thumbnailUrl: "https://foo.com/image/1?size=400x300", - }, - }, - { - schema: "https://ns.adove.com/experience/item-article", - data: { - id: "2", - url: "https://foo.com/article/2", - thumbnailUrl: "https://foo.com/image/2?size=400x300", - }, - }, - { - schema: "https://ns.adove.com/experience/item-article", - data: { - id: "3", - url: "https://foo.com/article/3", - thumbnailUrl: "https://foo.com/image/3?size=400x300", - }, - }, - ], - }, - { - id: "TNT:ABC:A", - scope: "Foo2", - items: [ - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "A", - content: "Banner A ....", - }, - }, - ], - }, -]; - -export const PAGE_WIDE_SCOPE_DECISIONS = [ - { - id: "TNT:activity1:experience1", - scope: "__view__", - scopeDetails: { - blah: "test", - }, - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo", - content: "
    Hola Mundo
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo2", - content: "
    here is a target activity
    ", - }, - }, - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "A", - content: "Banner A ....", - }, - }, - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "B", - content: "Banner B ....", - }, - }, - { - schema: "https://ns.adobe.com/personalization/default-content-item", - }, - ], - }, - { - id: "AJO:campaign1:message1", - scope: "web://alloy.test.com/test/page/1", - scopeDetails: { - decisionProvider: "AJO", - }, - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo", - content: "
    Hola Mundo
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo2", - content: "
    here is a target activity
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/default-content-item", - }, - ], - }, -]; -export const PAGE_WIDE_SCOPE_DECISIONS_WITHOUT_DOM_ACTION_SCHEMA_ITEMS = [ - { - id: "TNT:activity1:experience1", - scope: "__view__", - scopeDetails: { - blah: "test", - }, - items: [ - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "A", - content: "Banner A ....", - }, - }, - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "B", - content: "Banner B ....", - }, - }, - ], - }, -]; - -export const PAGE_WIDE_DECISIONS_WITH_DOM_ACTION_SCHEMA_ITEMS = [ - { - id: "TNT:activity1:experience1", - scope: "__view__", - scopeDetails: { - blah: "test", - }, - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo", - content: "
    Hola Mundo
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo2", - content: "
    here is a target activity
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/default-content-item", - }, - ], - }, - { - id: "AJO:campaign1:message1", - scope: "web://alloy.test.com/test/page/1", - scopeDetails: { - decisionProvider: "AJO", - }, - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo", - content: "
    Hola Mundo
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo2", - content: "
    here is a target activity
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/default-content-item", - }, - ], - }, -]; - -export const CART_VIEW_DECISIONS = [ - { - id: "TNT:activity4:experience9", - scope: "cart", - scopeDetails: { - blah: "test", - characteristics: { - scopeType: "view", - }, - }, - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo", - content: "
    welcome to cart view
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo2", - content: "
    here is a target activity for cart view
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/default-content-item", - }, - ], - }, -]; -export const PRODUCTS_VIEW_DECISIONS = [ - { - id: "TNT:activity3:experience4", - scope: "products", - scopeDetails: { - blah: "test", - characteristics: { - scopeType: "view", - }, - }, - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo3", - content: "
    welcome to products view
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo4", - content: "
    here is a target activity for products view
    ", - }, - }, - ], - }, -]; -export const REDIRECT_PAGE_WIDE_SCOPE_DECISION = [ - { - id: "TNT:activity15:experience1", - scope: "__view__", - scopeDetails: { - blah: "test", - }, - items: [ - { - schema: "https://ns.adobe.com/personalization/redirect-item", - data: { - type: "redirect", - content: "http://example.com/redirect/offer", - }, - }, - ], - }, -]; -export const MERGED_METRIC_DECISIONS = [ - { - id: "TNT:activity6:experience1", - scope: "testScope", - scopeDetails: { - eventTokens: { - display: "displayToken1", - click: "clickToken1", - }, - }, - items: [ - { - id: "0", - schema: "https://ns.adobe.com/personalization/html-content-item", - data: { - id: "0", - format: "text/html", - content: "testScope content1", - }, - }, - { - schema: "https://ns.adobe.com/personalization/default-content-item", - }, - { - schema: "https://ns.adobe.com/personalization/measurement", - data: { - type: "click", - format: "application/vnd.adobe.target.metric", - }, - }, - ], - }, -]; - -export const MIXED_PROPOSITIONS = [ - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn0=", - scope: "home", - scopeDetails: { - decisionProvider: "AJO", - }, - items: [ - { - id: "442358", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - format: "application/vnd.adobe.target.dom-action", - selector: "#root", - }, - }, - ], - }, - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", - scope: "home", - scopeDetails: { - decisionProvider: "AJO", - }, - items: [ - { - id: "442359", - schema: "https://ns.adobe.com/personalization/html-content-item", - data: { - content: "

    Some custom content for the home page

    ", - format: "text/html", - id: "1202448", - }, - }, - ], - }, - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", - scope: "home", - scopeDetails: { - decisionProvider: "AJO", - }, - items: [ - { - id: "442360", - schema: "https://ns.adobe.com/personalization/json-content-item", - data: { - content: "{'field1': 'custom content'}", - format: "text/javascript", - id: "1202449", - }, - }, - ], - }, - { - id: "AT:eyJhY3Rpdml0eUlkIjoiMTQxNjY0IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", - scope: "home", - scopeDetails: { - decisionProvider: "AJO", - }, - items: [ - { - id: "xcore:personalized-offer:134ce877e13a04ca", - etag: "4", - schema: - "https://ns.adobe.com/experience/offer-management/content-component-html", - data: { - id: "xcore:personalized-offer:134ce877e13a04ca", - format: "text/html", - language: ["en-us"], - content: "

    An html offer from Offer Decisioning

    ", - characteristics: { - testing: "true", - }, - }, - }, - ], - }, - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", - scope: "__view__", - scopeDetails: { - decisionProvider: "AJO", - }, - items: [ - { - id: "442358", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - format: "application/vnd.adobe.target.dom-action", - selector: "#root", - }, - }, - ], - }, - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn2=", - scope: "__view__", - scopeDetails: { - decisionProvider: "AJO", - }, - items: [ - { - id: "442379", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - format: "application/vnd.adobe.target.dom-action", - selector: "#root", - }, - }, - ], - }, -]; diff --git a/test/unit/specs/components/Personalization/topLevel/buildAlloy.js b/test/unit/specs/components/Personalization/topLevel/buildAlloy.js deleted file mode 100644 index 81a8554b0..000000000 --- a/test/unit/specs/components/Personalization/topLevel/buildAlloy.js +++ /dev/null @@ -1,251 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createEvent from "../../../../../../src/core/createEvent.js"; -import flushPromiseChains from "../../../../helpers/flushPromiseChains.js"; -import createComponent from "../../../../../../src/components/Personalization/createComponent.js"; -import createCollect from "../../../../../../src/utils/createCollect.js"; -import createFetchDataHandler from "../../../../../../src/components/Personalization/createFetchDataHandler.js"; -import collectInteractions from "../../../../../../src/components/Personalization/dom-actions/clicks/collectInteractions.js"; -import isAuthoringModeEnabled from "../../../../../../src/components/Personalization/utils/isAuthoringModeEnabled.js"; -import { - mergeDecisionsMeta, - mergeQuery, -} from "../../../../../../src/utils/event.js"; -import createOnClickHandler from "../../../../../../src/components/Personalization/createOnClickHandler.js"; -import createViewCacheManager from "../../../../../../src/components/Personalization/createViewCacheManager.js"; -import createViewChangeHandler from "../../../../../../src/components/Personalization/createViewChangeHandler.js"; -import createInteractionStorage from "../../../../../../src/components/Personalization/createInteractionStorage.js"; -import createClickStorage from "../../../../../../src/components/Personalization/createClickStorage.js"; -import createApplyPropositions from "../../../../../../src/components/Personalization/createApplyPropositions.js"; -import createSetTargetMigration from "../../../../../../src/components/Personalization/createSetTargetMigration.js"; -import { createCallbackAggregator } from "../../../../../../src/utils/index.js"; -import injectCreateProposition from "../../../../../../src/components/Personalization/handlers/injectCreateProposition.js"; -import createProcessPropositions from "../../../../../../src/components/Personalization/handlers/createProcessPropositions.js"; -import createAsyncArray from "../../../../../../src/components/Personalization/utils/createAsyncArray.js"; -import * as schema from "../../../../../../src/constants/schema.js"; -import createProcessDomAction from "../../../../../../src/components/Personalization/handlers/createProcessDomAction.js"; -import createProcessHtmlContent from "../../../../../../src/components/Personalization/handlers/createProcessHtmlContent.js"; -import createProcessRedirect from "../../../../../../src/components/Personalization/handlers/createProcessRedirect.js"; -import processDefaultContent from "../../../../../../src/components/Personalization/handlers/processDefaultContent.js"; -import { isPageWideSurface } from "../../../../../../src/components/Personalization/utils/surfaceUtils.js"; -import createOnDecisionHandler from "../../../../../../src/components/Personalization/createOnDecisionHandler.js"; -import createNotificationHandler from "../../../../../../src/components/Personalization/createNotificationHandler.js"; -import { - DOM_ACTION_APPEND_HTML, - DOM_ACTION_CLICK, - DOM_ACTION_CUSTOM_CODE, - DOM_ACTION_INSERT_AFTER, - DOM_ACTION_INSERT_BEFORE, - DOM_ACTION_MOVE, - DOM_ACTION_PREPEND_HTML, - DOM_ACTION_REARRANGE, - DOM_ACTION_REMOVE, - DOM_ACTION_REPLACE_HTML, - DOM_ACTION_RESIZE, - DOM_ACTION_SET_ATTRIBUTE, - DOM_ACTION_SET_HTML, - DOM_ACTION_SET_IMAGE_SOURCE, - DOM_ACTION_SET_STYLE, - DOM_ACTION_SET_TEXT, -} from "../../../../../../src/components/Personalization/dom-actions/initDomActionsModules.js"; -import collectClicks from "../../../../../../src/components/Personalization/dom-actions/clicks/collectClicks.js"; - -const createAction = - (renderFunc) => - ({ selector, content }) => { - if (selector === "#error") { - return Promise.reject(new Error(`Error while rendering ${content}`)); - } - return renderFunc(selector, content); - }; - -const buildComponent = ({ - actions, - config, - logger, - eventManager, - getPageLocation, - window, - hideContainers, - showContainers, -}) => { - const initDomActionsModulesMocks = () => { - return { - [DOM_ACTION_SET_HTML]: createAction(actions.setHtml), - [DOM_ACTION_CUSTOM_CODE]: createAction(actions.prependHtml), - [DOM_ACTION_SET_TEXT]: createAction(actions.setText), - [DOM_ACTION_SET_ATTRIBUTE]: createAction(actions.setAttributes), - [DOM_ACTION_SET_IMAGE_SOURCE]: createAction(actions.swapImage), - [DOM_ACTION_SET_STYLE]: createAction(actions.setStyles), - [DOM_ACTION_MOVE]: createAction(actions.setStyles), - [DOM_ACTION_RESIZE]: createAction(actions.setStyles), - [DOM_ACTION_REARRANGE]: createAction(actions.rearrangeChildren), - [DOM_ACTION_REMOVE]: createAction(actions.removeNode), - [DOM_ACTION_INSERT_AFTER]: createAction(actions.insertHtmlAfter), - [DOM_ACTION_INSERT_BEFORE]: createAction(actions.insertHtmlBefore), - [DOM_ACTION_REPLACE_HTML]: createAction(actions.replaceHtml), - [DOM_ACTION_PREPEND_HTML]: createAction(actions.prependHtml), - [DOM_ACTION_APPEND_HTML]: createAction(actions.appendHtml), - [DOM_ACTION_CLICK]: createAction(actions.click), - }; - }; - - const { - targetMigrationEnabled, - prehidingStyle, - autoCollectPropositionInteractions, - } = config; - const collect = createCollect({ eventManager, mergeDecisionsMeta }); - - const { storeInteractionMeta, getInteractionMetas } = - createInteractionStorage(); - - const { storeClickMeta, getClickSelectors, getClickMetas } = - createClickStorage(); - - const preprocess = (action) => action; - const createProposition = injectCreateProposition({ - preprocess, - isPageWideSurface, - }); - - const viewCache = createViewCacheManager({ createProposition }); - const modules = initDomActionsModulesMocks(); - - const schemaProcessors = { - [schema.DEFAULT_CONTENT_ITEM]: processDefaultContent, - [schema.DOM_ACTION]: createProcessDomAction({ - modules, - logger, - storeInteractionMeta, - storeClickMeta, - autoCollectPropositionInteractions, - }), - [schema.HTML_CONTENT_ITEM]: createProcessHtmlContent({ - modules, - logger, - storeInteractionMeta, - autoCollectPropositionInteractions, - }), - [schema.REDIRECT_ITEM]: createProcessRedirect({ - logger, - executeRedirect: (url) => window.location.replace(url), - collect, - }), - }; - - const processPropositions = createProcessPropositions({ - schemaProcessors, - logger, - }); - - const renderedPropositions = createAsyncArray(); - const notificationHandler = createNotificationHandler( - collect, - renderedPropositions, - ); - - const consent = jasmine.createSpyObj("consent", ["current"]); - consent.current.and.returnValue({ state: "in", wasSet: false }); - - const fetchDataHandler = createFetchDataHandler({ - logger, - prehidingStyle, - showContainers, - hideContainers, - mergeQuery, - processPropositions, - createProposition, - notificationHandler, - consent, - }); - const onClickHandler = createOnClickHandler({ - mergeDecisionsMeta, - collectInteractions, - collectClicks, - getInteractionMetas, - getClickMetas, - getClickSelectors, - }); - - const viewChangeHandler = createViewChangeHandler({ - logger, - processPropositions, - viewCache, - }); - const applyPropositions = createApplyPropositions({ - processPropositions, - createProposition, - renderedPropositions, - viewCache, - }); - const setTargetMigration = createSetTargetMigration({ - targetMigrationEnabled, - }); - - const onDecisionHandler = createOnDecisionHandler({ - processPropositions, - createProposition, - notificationHandler, - }); - - return createComponent({ - getPageLocation, - logger, - fetchDataHandler, - viewChangeHandler, - onClickHandler, - isAuthoringModeEnabled, - mergeQuery, - viewCache, - showContainers, - applyPropositions, - setTargetMigration, - mergeDecisionsMeta, - renderedPropositions, - onDecisionHandler, - }); -}; - -export default (mocks) => { - const component = buildComponent(mocks); - const { response } = mocks; - return { - async sendEvent({ - xdm, - data, - renderDecisions, - decisionScopes, - personalization, - }) { - const event = createEvent(); - event.setUserXdm(xdm); - event.setUserData(data); - const callbacks = createCallbackAggregator(); - await component.lifecycle.onBeforeEvent({ - event, - renderDecisions, - decisionScopes, - personalization: personalization || { sendDisplayEvent: true }, - onResponse: callbacks.add, - }); - const results = await callbacks.call({ response }); - const result = Object.assign({}, ...results); - await flushPromiseChains(); - event.finalize(); - return { event, result }; - }, - applyPropositions(args) { - return component.commands.applyPropositions.run(args); - }, - }; -}; diff --git a/test/unit/specs/components/Personalization/topLevel/buildMocks.js b/test/unit/specs/components/Personalization/topLevel/buildMocks.js deleted file mode 100644 index 0101e85ce..000000000 --- a/test/unit/specs/components/Personalization/topLevel/buildMocks.js +++ /dev/null @@ -1,95 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createEvent from "../../../../../../src/core/createEvent.js"; -import createResponse from "../../../../../functional/helpers/createResponse.js"; -import { - ADOBE_JOURNEY_OPTIMIZER, - ADOBE_TARGET, -} from "../../../../../../src/constants/decisionProvider.js"; -import { - ALWAYS, - NEVER, -} from "../../../../../../src/constants/propositionInteractionType.js"; - -export default (decisions) => { - const response = createResponse({ - content: { - handle: decisions.map((payload) => ({ - type: "personalization:decisions", - payload, - })), - }, - }); - - const actions = jasmine.createSpyObj("actions", { - setHtml: () => Promise.resolve(), - setText: () => Promise.resolve(), - setAttributes: () => Promise.resolve(), - swapImage: () => Promise.resolve(), - setStyles: () => Promise.resolve(), - rearrangeChildren: () => Promise.resolve(), - removeNode: () => Promise.resolve(), - replaceHtml: () => Promise.resolve(), - appendHtml: () => Promise.resolve(), - prependHtml: () => Promise.resolve(), - insertHtmlAfter: () => Promise.resolve(), - insertHtmlBefore: () => Promise.resolve(), - click: () => Promise.resolve(), - }); - - const config = { - targetMigrationEnabled: true, - prehidingStyle: "myprehidingstyle", - autoCollectPropositionInteractions: { - [ADOBE_JOURNEY_OPTIMIZER]: ALWAYS, - [ADOBE_TARGET]: NEVER, - }, - }; - const logger = { - warn: spyOn(console, "warn").and.callThrough(), - error: spyOn(console, "error").and.callThrough(), - logOnContentRendering: jasmine - .createSpy("logOnContentRendering") - .and.callThrough(), - logOnContentHiding: jasmine - .createSpy("logOnContentHiding") - .and.callThrough(), - }; - const sendEvent = jasmine.createSpy("sendEvent"); - const eventManager = { - createEvent, - async sendEvent(event) { - event.finalize(); - sendEvent(event.toJSON()); - return Promise.resolve(); - }, - }; - const getPageLocation = () => new URL("http://example.com/home"); - const window = { - location: jasmine.createSpyObj("location", ["replace"]), - }; - const hideContainers = jasmine.createSpy("hideContainers"); - const showContainers = jasmine.createSpy("showContainers"); - - return { - actions, - config, - logger, - sendEvent, - eventManager, - getPageLocation, - window, - hideContainers, - showContainers, - response, - }; -}; diff --git a/test/unit/specs/components/Personalization/topLevel/cartViewDecisions.spec.js b/test/unit/specs/components/Personalization/topLevel/cartViewDecisions.spec.js deleted file mode 100644 index a3292d45f..000000000 --- a/test/unit/specs/components/Personalization/topLevel/cartViewDecisions.spec.js +++ /dev/null @@ -1,274 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { CART_VIEW_DECISIONS } from "../responsesMock/eventResponses.js"; - -import buildMocks from "./buildMocks.js"; -import buildAlloy from "./buildAlloy.js"; -import resetMocks from "./resetMocks.js"; -import flushPromiseChains from "../../../../helpers/flushPromiseChains.js"; - -describe("PersonalizationComponent", () => { - it("CART_VIEW_DECISIONS", async () => { - const mocks = buildMocks(CART_VIEW_DECISIONS); - const alloy = buildAlloy(mocks); - let { event, result } = await alloy.sendEvent( - { - renderDecisions: true, - }, - CART_VIEW_DECISIONS, - ); - expect(event.toJSON()).toEqual({ - query: { - personalization: { - schemas: [ - "https://ns.adobe.com/personalization/default-content-item", - "https://ns.adobe.com/personalization/html-content-item", - "https://ns.adobe.com/personalization/json-content-item", - "https://ns.adobe.com/personalization/redirect-item", - "https://ns.adobe.com/personalization/ruleset-item", - "https://ns.adobe.com/personalization/message/in-app", - "https://ns.adobe.com/personalization/message/content-card", - "https://ns.adobe.com/personalization/dom-action", - ], - decisionScopes: ["__view__"], - surfaces: ["web://example.com/home"], - }, - }, - }); - expect(result).toEqual({ - propositions: [], - decisions: [], - }); - expect(mocks.sendEvent).not.toHaveBeenCalled(); - - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - - resetMocks(mocks); - ({ event, result } = await alloy.sendEvent( - { - renderDecisions: true, - xdm: { - web: { - webPageDetails: { - viewName: "cart", - }, - }, - }, - }, - [], - )); - - expect(event.toJSON()).toEqual({ - xdm: { - _experience: { - decisioning: { - propositions: [ - { - id: "TNT:activity4:experience9", - scope: "cart", - scopeDetails: { - blah: "test", - characteristics: { - scopeType: "view", - }, - }, - }, - ], - propositionEventType: { - display: 1, - }, - }, - }, - web: { - webPageDetails: { - viewName: "cart", - }, - }, - }, - }); - expect(result).toEqual({ - propositions: [ - { - renderAttempted: true, - id: "TNT:activity4:experience9", - scope: "cart", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo", - content: "
    welcome to cart view
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo2", - content: "
    here is a target activity for cart view
    ", - }, - }, - { - schema: - "https://ns.adobe.com/personalization/default-content-item", - }, - ], - scopeDetails: { - blah: "test", - characteristics: { - scopeType: "view", - }, - }, - }, - ], - decisions: [], - }); - expect(mocks.sendEvent).not.toHaveBeenCalled(); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo", - "
    welcome to cart view
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo2", - "
    here is a target activity for cart view
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledTimes(2); - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - }); - - it("CART_VIEW_DECISIONS 2", async () => { - const mocks = buildMocks(CART_VIEW_DECISIONS); - const alloy = buildAlloy(mocks); - const { event, result } = await alloy.sendEvent( - { - renderDecisions: true, - xdm: { - web: { - webPageDetails: { - viewName: "cart", - }, - }, - }, - }, - CART_VIEW_DECISIONS, - ); - - await flushPromiseChains(); - - expect(event.toJSON()).toEqual({ - query: { - personalization: { - schemas: [ - "https://ns.adobe.com/personalization/default-content-item", - "https://ns.adobe.com/personalization/html-content-item", - "https://ns.adobe.com/personalization/json-content-item", - "https://ns.adobe.com/personalization/redirect-item", - "https://ns.adobe.com/personalization/ruleset-item", - "https://ns.adobe.com/personalization/message/in-app", - "https://ns.adobe.com/personalization/message/content-card", - "https://ns.adobe.com/personalization/dom-action", - ], - decisionScopes: ["__view__"], - surfaces: ["web://example.com/home"], - }, - }, - xdm: { - web: { - webPageDetails: { - viewName: "cart", - }, - }, - }, - }); - expect(result).toEqual({ - propositions: [ - { - renderAttempted: true, - id: "TNT:activity4:experience9", - scope: "cart", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo", - content: "
    welcome to cart view
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo2", - content: "
    here is a target activity for cart view
    ", - }, - }, - { - schema: - "https://ns.adobe.com/personalization/default-content-item", - }, - ], - scopeDetails: { - blah: "test", - characteristics: { - scopeType: "view", - }, - }, - }, - ], - decisions: [], - }); - expect(mocks.sendEvent).toHaveBeenCalledWith({ - xdm: { - _experience: { - decisioning: { - propositions: [ - { - id: "TNT:activity4:experience9", - scope: "cart", - scopeDetails: { - blah: "test", - characteristics: { - scopeType: "view", - }, - }, - }, - ], - propositionEventType: { - display: 1, - }, - }, - }, - eventType: "decisioning.propositionDisplay", - web: { - webPageDetails: { - viewName: "cart", - }, - }, - }, - }); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo", - "
    welcome to cart view
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo2", - "
    here is a target activity for cart view
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledTimes(2); - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Personalization/topLevel/mergedMetricDecisions.spec.js b/test/unit/specs/components/Personalization/topLevel/mergedMetricDecisions.spec.js deleted file mode 100644 index 7d1a32d67..000000000 --- a/test/unit/specs/components/Personalization/topLevel/mergedMetricDecisions.spec.js +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { MERGED_METRIC_DECISIONS } from "../responsesMock/eventResponses.js"; - -import buildMocks from "./buildMocks.js"; -import buildAlloy from "./buildAlloy.js"; - -describe("PersonalizationComponent", () => { - it("MERGED_METRIC_DECISIONS", async () => { - const mocks = buildMocks(MERGED_METRIC_DECISIONS); - const alloy = buildAlloy(mocks); - const { event, result } = await alloy.sendEvent( - { - renderDecisions: true, - }, - MERGED_METRIC_DECISIONS, - ); - expect(event.toJSON()).toEqual({ - query: { - personalization: { - schemas: [ - "https://ns.adobe.com/personalization/default-content-item", - "https://ns.adobe.com/personalization/html-content-item", - "https://ns.adobe.com/personalization/json-content-item", - "https://ns.adobe.com/personalization/redirect-item", - "https://ns.adobe.com/personalization/ruleset-item", - "https://ns.adobe.com/personalization/message/in-app", - "https://ns.adobe.com/personalization/message/content-card", - "https://ns.adobe.com/personalization/dom-action", - ], - decisionScopes: ["__view__"], - surfaces: ["web://example.com/home"], - }, - }, - }); - expect(result).toEqual({ - propositions: [ - { - renderAttempted: false, - id: "TNT:activity6:experience1", - scope: "testScope", - items: [ - { - id: "0", - schema: "https://ns.adobe.com/personalization/html-content-item", - data: { - id: "0", - format: "text/html", - content: "testScope content1", - }, - }, - { - schema: - "https://ns.adobe.com/personalization/default-content-item", - }, - { - schema: "https://ns.adobe.com/personalization/measurement", - data: { - type: "click", - format: "application/vnd.adobe.target.metric", - }, - }, - ], - scopeDetails: { - eventTokens: { - display: "displayToken1", - click: "clickToken1", - }, - }, - }, - ], - decisions: [ - { - id: "TNT:activity6:experience1", - scope: "testScope", - items: [ - { - id: "0", - schema: "https://ns.adobe.com/personalization/html-content-item", - data: { - id: "0", - format: "text/html", - content: "testScope content1", - }, - }, - { - schema: - "https://ns.adobe.com/personalization/default-content-item", - }, - { - schema: "https://ns.adobe.com/personalization/measurement", - data: { - type: "click", - format: "application/vnd.adobe.target.metric", - }, - }, - ], - scopeDetails: { - eventTokens: { - display: "displayToken1", - click: "clickToken1", - }, - }, - }, - ], - }); - expect(mocks.sendEvent).not.toHaveBeenCalled(); - - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js b/test/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js deleted file mode 100644 index 1c3edf37d..000000000 --- a/test/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js +++ /dev/null @@ -1,297 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { MIXED_PROPOSITIONS } from "../responsesMock/eventResponses.js"; - -import buildMocks from "./buildMocks.js"; -import buildAlloy from "./buildAlloy.js"; -import resetMocks from "./resetMocks.js"; -import flushPromiseChains from "../../../../helpers/flushPromiseChains.js"; - -describe("PersonalizationComponent", () => { - it("MIXED_PROPOSITIONS", async () => { - const mocks = buildMocks(MIXED_PROPOSITIONS); - const alloy = buildAlloy(mocks); - const { event, result } = await alloy.sendEvent( - { - renderDecisions: true, - }, - MIXED_PROPOSITIONS, - ); - expect(event.toJSON()).toEqual({ - query: { - personalization: { - schemas: [ - "https://ns.adobe.com/personalization/default-content-item", - "https://ns.adobe.com/personalization/html-content-item", - "https://ns.adobe.com/personalization/json-content-item", - "https://ns.adobe.com/personalization/redirect-item", - "https://ns.adobe.com/personalization/ruleset-item", - "https://ns.adobe.com/personalization/message/in-app", - "https://ns.adobe.com/personalization/message/content-card", - "https://ns.adobe.com/personalization/dom-action", - ], - decisionScopes: ["__view__"], - surfaces: ["web://example.com/home"], - }, - }, - }); - expect(result.propositions).toEqual( - jasmine.arrayWithExactContents([ - { - renderAttempted: true, - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", - scope: "__view__", - scopeDetails: { decisionProvider: "AJO" }, - items: [ - { - id: "442358", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - format: "application/vnd.adobe.target.dom-action", - selector: "#root", - }, - }, - ], - }, - { - renderAttempted: true, - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn2=", - scope: "__view__", - scopeDetails: { decisionProvider: "AJO" }, - items: [ - { - id: "442379", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - format: "application/vnd.adobe.target.dom-action", - selector: "#root", - }, - }, - ], - }, - { - renderAttempted: false, - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", - scope: "home", - scopeDetails: { decisionProvider: "AJO" }, - items: [ - { - id: "442359", - schema: "https://ns.adobe.com/personalization/html-content-item", - data: { - content: "

    Some custom content for the home page

    ", - format: "text/html", - id: "1202448", - }, - }, - ], - }, - { - renderAttempted: false, - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", - scope: "home", - scopeDetails: { decisionProvider: "AJO" }, - items: [ - { - id: "442360", - schema: "https://ns.adobe.com/personalization/json-content-item", - data: { - content: "{'field1': 'custom content'}", - format: "text/javascript", - id: "1202449", - }, - }, - ], - }, - { - renderAttempted: false, - id: "AT:eyJhY3Rpdml0eUlkIjoiMTQxNjY0IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", - scope: "home", - scopeDetails: { decisionProvider: "AJO" }, - items: [ - { - id: "xcore:personalized-offer:134ce877e13a04ca", - etag: "4", - schema: - "https://ns.adobe.com/experience/offer-management/content-component-html", - data: { - id: "xcore:personalized-offer:134ce877e13a04ca", - format: "text/html", - language: ["en-us"], - content: "

    An html offer from Offer Decisioning

    ", - characteristics: { - testing: "true", - }, - }, - }, - ], - }, - { - renderAttempted: false, - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn0=", - scope: "home", - scopeDetails: { decisionProvider: "AJO" }, - items: [ - { - id: "442358", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - format: "application/vnd.adobe.target.dom-action", - selector: "#root", - }, - }, - ], - }, - ]), - ); - expect(result.decisions).toEqual( - jasmine.arrayWithExactContents([ - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", - scope: "home", - scopeDetails: { decisionProvider: "AJO" }, - items: [ - { - id: "442359", - schema: "https://ns.adobe.com/personalization/html-content-item", - data: { - content: "

    Some custom content for the home page

    ", - format: "text/html", - id: "1202448", - }, - }, - ], - }, - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", - scope: "home", - scopeDetails: { decisionProvider: "AJO" }, - items: [ - { - id: "442360", - schema: "https://ns.adobe.com/personalization/json-content-item", - data: { - content: "{'field1': 'custom content'}", - format: "text/javascript", - id: "1202449", - }, - }, - ], - }, - { - id: "AT:eyJhY3Rpdml0eUlkIjoiMTQxNjY0IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", - scope: "home", - scopeDetails: { decisionProvider: "AJO" }, - items: [ - { - id: "xcore:personalized-offer:134ce877e13a04ca", - etag: "4", - schema: - "https://ns.adobe.com/experience/offer-management/content-component-html", - data: { - id: "xcore:personalized-offer:134ce877e13a04ca", - format: "text/html", - language: ["en-us"], - content: "

    An html offer from Offer Decisioning

    ", - characteristics: { - testing: "true", - }, - }, - }, - ], - }, - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn0=", - scope: "home", - scopeDetails: { decisionProvider: "AJO" }, - items: [ - { - id: "442358", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - format: "application/vnd.adobe.target.dom-action", - selector: "#root", - }, - }, - ], - }, - ]), - ); - expect(mocks.sendEvent).not.toHaveBeenCalled(); - - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - - resetMocks(mocks); - const applyPropositionsResult = await alloy.applyPropositions({ - propositions: result.propositions, - metadata: { - home: { - selector: "#myhomeselector", - actionType: "appendHtml", - }, - }, - }); - expect(applyPropositionsResult.propositions).toEqual([ - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn0=", - scope: "home", - items: [ - { - id: "442358", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - format: "application/vnd.adobe.target.dom-action", - selector: "#root", - }, - }, - ], - renderAttempted: true, - scopeDetails: { decisionProvider: "AJO" }, - }, - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn1=", - scope: "home", - items: [ - { - id: "442359", - schema: "https://ns.adobe.com/personalization/html-content-item", - data: { - content: "

    Some custom content for the home page

    ", - format: "text/html", - id: "1202448", - selector: "#myhomeselector", - type: "appendHtml", - }, - }, - ], - renderAttempted: true, - scopeDetails: { decisionProvider: "AJO" }, - }, - ]); - expect(applyPropositionsResult.decisions).toBeUndefined(); - - await flushPromiseChains(); - expect(mocks.sendEvent).not.toHaveBeenCalled(); - expect(mocks.actions.appendHtml).toHaveBeenCalledOnceWith( - "#myhomeselector", - "

    Some custom content for the home page

    ", - ); - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js b/test/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js deleted file mode 100644 index 4bcc74e9b..000000000 --- a/test/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js +++ /dev/null @@ -1,182 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { PAGE_WIDE_DECISIONS_WITH_DOM_ACTION_SCHEMA_ITEMS } from "../responsesMock/eventResponses.js"; - -import buildMocks from "./buildMocks.js"; -import buildAlloy from "./buildAlloy.js"; -import pause from "../../../../helpers/pause.js"; - -describe("PersonalizationComponent", () => { - it("PAGE_WIDE_DECISIONS_WITH_DOM_ACTION_SCHEMA_ITEMS", async () => { - const mocks = buildMocks(PAGE_WIDE_DECISIONS_WITH_DOM_ACTION_SCHEMA_ITEMS); - const alloy = buildAlloy(mocks); - const { event, result } = await alloy.sendEvent( - { - renderDecisions: true, - }, - PAGE_WIDE_DECISIONS_WITH_DOM_ACTION_SCHEMA_ITEMS, - ); - expect(event.toJSON()).toEqual({ - query: { - personalization: { - schemas: [ - "https://ns.adobe.com/personalization/default-content-item", - "https://ns.adobe.com/personalization/html-content-item", - "https://ns.adobe.com/personalization/json-content-item", - "https://ns.adobe.com/personalization/redirect-item", - "https://ns.adobe.com/personalization/ruleset-item", - "https://ns.adobe.com/personalization/message/in-app", - "https://ns.adobe.com/personalization/message/content-card", - "https://ns.adobe.com/personalization/dom-action", - ], - decisionScopes: ["__view__"], - surfaces: ["web://example.com/home"], - }, - }, - }); - expect(result).toEqual({ - propositions: [ - { - renderAttempted: true, - id: "TNT:activity1:experience1", - scope: "__view__", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo", - content: "
    Hola Mundo
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo2", - content: "
    here is a target activity
    ", - }, - }, - { - schema: - "https://ns.adobe.com/personalization/default-content-item", - }, - ], - scopeDetails: { - blah: "test", - }, - }, - { - renderAttempted: true, - id: "AJO:campaign1:message1", - scope: "web://alloy.test.com/test/page/1", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo", - content: "
    Hola Mundo
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo2", - content: "
    here is a target activity
    ", - }, - }, - { - schema: - "https://ns.adobe.com/personalization/default-content-item", - }, - ], - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - decisions: [], - }); - - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo", - "
    Hola Mundo
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo2", - "
    here is a target activity
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo", - "
    Hola Mundo
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo2", - "
    here is a target activity
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledTimes(4); - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - - await pause(100); - - expect(mocks.sendEvent).toHaveBeenCalledWith({ - xdm: { - _experience: { - decisioning: { - propositions: [ - { - id: "TNT:activity1:experience1", - scope: "__view__", - scopeDetails: { - blah: "test", - }, - }, - { - id: "AJO:campaign1:message1", - scope: "web://alloy.test.com/test/page/1", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - propositionEventType: { - display: 1, - }, - }, - }, - eventType: "decisioning.propositionDisplay", - }, - }); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo", - "
    Hola Mundo
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo2", - "
    here is a target activity
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo", - "
    Hola Mundo
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo2", - "
    here is a target activity
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledTimes(4); - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js b/test/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js deleted file mode 100644 index 215591abb..000000000 --- a/test/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js +++ /dev/null @@ -1,232 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { PAGE_WIDE_SCOPE_DECISIONS } from "../responsesMock/eventResponses.js"; - -import buildMocks from "./buildMocks.js"; -import buildAlloy from "./buildAlloy.js"; -import pause from "../../../../helpers/pause.js"; - -describe("PersonalizationComponent", () => { - it("PAGE_WIDE_SCOPE_DECISIONS", async () => { - const mocks = buildMocks(PAGE_WIDE_SCOPE_DECISIONS); - const alloy = buildAlloy(mocks); - const { event, result } = await alloy.sendEvent( - { - renderDecisions: true, - }, - PAGE_WIDE_SCOPE_DECISIONS, - ); - expect(event.toJSON()).toEqual({ - query: { - personalization: { - schemas: [ - "https://ns.adobe.com/personalization/default-content-item", - "https://ns.adobe.com/personalization/html-content-item", - "https://ns.adobe.com/personalization/json-content-item", - "https://ns.adobe.com/personalization/redirect-item", - "https://ns.adobe.com/personalization/ruleset-item", - "https://ns.adobe.com/personalization/message/in-app", - "https://ns.adobe.com/personalization/message/content-card", - "https://ns.adobe.com/personalization/dom-action", - ], - decisionScopes: ["__view__"], - surfaces: ["web://example.com/home"], - }, - }, - }); - expect(result.propositions).toEqual( - jasmine.arrayWithExactContents([ - { - renderAttempted: true, - id: "TNT:activity1:experience1", - scope: "__view__", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo", - content: "
    Hola Mundo
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo2", - content: "
    here is a target activity
    ", - }, - }, - { - schema: - "https://ns.adobe.com/personalization/default-content-item", - }, - ], - scopeDetails: { - blah: "test", - }, - }, - { - renderAttempted: true, - id: "AJO:campaign1:message1", - scope: "web://alloy.test.com/test/page/1", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo", - content: "
    Hola Mundo
    ", - }, - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - selector: "#foo2", - content: "
    here is a target activity
    ", - }, - }, - { - schema: - "https://ns.adobe.com/personalization/default-content-item", - }, - ], - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - renderAttempted: false, - id: "TNT:activity1:experience1", - scope: "__view__", - items: [ - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "A", - content: "Banner A ....", - }, - }, - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "B", - content: "Banner B ....", - }, - }, - ], - scopeDetails: { - blah: "test", - }, - }, - ]), - ); - expect(result.decisions).toEqual( - jasmine.arrayWithExactContents([ - { - id: "TNT:activity1:experience1", - scope: "__view__", - items: [ - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "A", - content: "Banner A ....", - }, - }, - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "B", - content: "Banner B ....", - }, - }, - ], - scopeDetails: { - blah: "test", - }, - }, - ]), - ); - - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo", - "
    Hola Mundo
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo2", - "
    here is a target activity
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo", - "
    Hola Mundo
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo2", - "
    here is a target activity
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledTimes(4); - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - - await pause(100); - - expect(mocks.sendEvent).toHaveBeenCalledWith({ - xdm: { - _experience: { - decisioning: { - propositions: [ - { - id: "TNT:activity1:experience1", - scope: "__view__", - scopeDetails: { - blah: "test", - }, - }, - { - id: "AJO:campaign1:message1", - scope: "web://alloy.test.com/test/page/1", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - propositionEventType: { - display: 1, - }, - }, - }, - eventType: "decisioning.propositionDisplay", - }, - }); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo", - "
    Hola Mundo
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo2", - "
    here is a target activity
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo", - "
    Hola Mundo
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledWith( - "#foo2", - "
    here is a target activity
    ", - ); - expect(mocks.actions.setHtml).toHaveBeenCalledTimes(4); - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Personalization/topLevel/pageWideScopeDecisionsWithoutDomActionSchemaItems.spec.js b/test/unit/specs/components/Personalization/topLevel/pageWideScopeDecisionsWithoutDomActionSchemaItems.spec.js deleted file mode 100644 index 809c6832b..000000000 --- a/test/unit/specs/components/Personalization/topLevel/pageWideScopeDecisionsWithoutDomActionSchemaItems.spec.js +++ /dev/null @@ -1,105 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { PAGE_WIDE_SCOPE_DECISIONS_WITHOUT_DOM_ACTION_SCHEMA_ITEMS } from "../responsesMock/eventResponses.js"; - -import buildMocks from "./buildMocks.js"; -import buildAlloy from "./buildAlloy.js"; - -describe("PersonalizationComponent", () => { - it("PAGE_WIDE_SCOPE_DECISIONS_WITHOUT_DOM_ACTION_SCHEMA_ITEMS", async () => { - const mocks = buildMocks( - PAGE_WIDE_SCOPE_DECISIONS_WITHOUT_DOM_ACTION_SCHEMA_ITEMS, - ); - const alloy = buildAlloy(mocks); - const { event, result } = await alloy.sendEvent( - { - renderDecisions: true, - }, - PAGE_WIDE_SCOPE_DECISIONS_WITHOUT_DOM_ACTION_SCHEMA_ITEMS, - ); - expect(event.toJSON()).toEqual({ - query: { - personalization: { - schemas: [ - "https://ns.adobe.com/personalization/default-content-item", - "https://ns.adobe.com/personalization/html-content-item", - "https://ns.adobe.com/personalization/json-content-item", - "https://ns.adobe.com/personalization/redirect-item", - "https://ns.adobe.com/personalization/ruleset-item", - "https://ns.adobe.com/personalization/message/in-app", - "https://ns.adobe.com/personalization/message/content-card", - "https://ns.adobe.com/personalization/dom-action", - ], - decisionScopes: ["__view__"], - surfaces: ["web://example.com/home"], - }, - }, - }); - expect(result).toEqual({ - propositions: [ - { - renderAttempted: false, - id: "TNT:activity1:experience1", - scope: "__view__", - items: [ - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "A", - content: "Banner A ....", - }, - }, - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "B", - content: "Banner B ....", - }, - }, - ], - scopeDetails: { - blah: "test", - }, - }, - ], - decisions: [ - { - id: "TNT:activity1:experience1", - scope: "__view__", - items: [ - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "A", - content: "Banner A ....", - }, - }, - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "B", - content: "Banner B ....", - }, - }, - ], - scopeDetails: { - blah: "test", - }, - }, - ], - }); - expect(mocks.sendEvent).not.toHaveBeenCalled(); - - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Personalization/topLevel/productsViewDecisions.spec.js b/test/unit/specs/components/Personalization/topLevel/productsViewDecisions.spec.js deleted file mode 100644 index 32dae9496..000000000 --- a/test/unit/specs/components/Personalization/topLevel/productsViewDecisions.spec.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { PRODUCTS_VIEW_DECISIONS } from "../responsesMock/eventResponses.js"; - -import buildMocks from "./buildMocks.js"; -import buildAlloy from "./buildAlloy.js"; - -describe("PersonalizationComponent", () => { - it("PRODUCTS_VIEW_DECISIONS", async () => { - const mocks = buildMocks(PRODUCTS_VIEW_DECISIONS); - const alloy = buildAlloy(mocks); - const { event, result } = await alloy.sendEvent( - { - renderDecisions: true, - }, - PRODUCTS_VIEW_DECISIONS, - ); - expect(event.toJSON()).toEqual({ - query: { - personalization: { - schemas: [ - "https://ns.adobe.com/personalization/default-content-item", - "https://ns.adobe.com/personalization/html-content-item", - "https://ns.adobe.com/personalization/json-content-item", - "https://ns.adobe.com/personalization/redirect-item", - "https://ns.adobe.com/personalization/ruleset-item", - "https://ns.adobe.com/personalization/message/in-app", - "https://ns.adobe.com/personalization/message/content-card", - "https://ns.adobe.com/personalization/dom-action", - ], - decisionScopes: ["__view__"], - surfaces: ["web://example.com/home"], - }, - }, - }); - expect(result).toEqual({ - propositions: [], - decisions: [], - }); - expect(mocks.sendEvent).not.toHaveBeenCalled(); - - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Personalization/topLevel/redirectPageWideScopeDecision.spec.js b/test/unit/specs/components/Personalization/topLevel/redirectPageWideScopeDecision.spec.js deleted file mode 100644 index e8d702c05..000000000 --- a/test/unit/specs/components/Personalization/topLevel/redirectPageWideScopeDecision.spec.js +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { REDIRECT_PAGE_WIDE_SCOPE_DECISION } from "../responsesMock/eventResponses.js"; - -import buildMocks from "./buildMocks.js"; -import buildAlloy from "./buildAlloy.js"; - -describe("PersonalizationComponent", () => { - it("REDIRECT_PAGE_WIDE_SCOPE_DECISION", async () => { - const mocks = buildMocks(REDIRECT_PAGE_WIDE_SCOPE_DECISION); - const alloy = buildAlloy(mocks); - const { event } = await alloy.sendEvent( - { - renderDecisions: true, - }, - REDIRECT_PAGE_WIDE_SCOPE_DECISION, - ); - expect(event.toJSON()).toEqual({ - query: { - personalization: { - schemas: [ - "https://ns.adobe.com/personalization/default-content-item", - "https://ns.adobe.com/personalization/html-content-item", - "https://ns.adobe.com/personalization/json-content-item", - "https://ns.adobe.com/personalization/redirect-item", - "https://ns.adobe.com/personalization/ruleset-item", - "https://ns.adobe.com/personalization/message/in-app", - "https://ns.adobe.com/personalization/message/content-card", - "https://ns.adobe.com/personalization/dom-action", - ], - decisionScopes: ["__view__"], - surfaces: ["web://example.com/home"], - }, - }, - }); - // No expectation on the result value because the page will redirect soon. - expect(mocks.sendEvent).toHaveBeenCalledWith({ - xdm: { - _experience: { - decisioning: { - propositions: [ - { - id: "TNT:activity15:experience1", - scope: "__view__", - scopeDetails: { - blah: "test", - }, - }, - ], - propositionEventType: { - display: 1, - }, - }, - }, - eventType: "decisioning.propositionDisplay", - }, - }); - - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Personalization/topLevel/resetMocks.js b/test/unit/specs/components/Personalization/topLevel/resetMocks.js deleted file mode 100644 index ca044175f..000000000 --- a/test/unit/specs/components/Personalization/topLevel/resetMocks.js +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -export default ({ - actions, - logger, - sendEvent, - window, - hideContainers, - showContainers, -}) => { - Object.keys(actions).forEach((key) => { - actions[key].calls.reset(); - }); - logger.warn.calls.reset(); - logger.error.calls.reset(); - sendEvent.calls.reset(); - window.location.replace.calls.reset(); - hideContainers.calls.reset(); - showContainers.calls.reset(); -}; diff --git a/test/unit/specs/components/Personalization/topLevel/scopesFoo1Foo2Decisions.spec.js b/test/unit/specs/components/Personalization/topLevel/scopesFoo1Foo2Decisions.spec.js deleted file mode 100644 index 91865536e..000000000 --- a/test/unit/specs/components/Personalization/topLevel/scopesFoo1Foo2Decisions.spec.js +++ /dev/null @@ -1,151 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { SCOPES_FOO1_FOO2_DECISIONS } from "../responsesMock/eventResponses.js"; - -import buildMocks from "./buildMocks.js"; -import buildAlloy from "./buildAlloy.js"; - -describe("PersonalizationComponent", () => { - it("SCOPES_FOO1_FOO2_DECISIONS", async () => { - const mocks = buildMocks(SCOPES_FOO1_FOO2_DECISIONS); - const alloy = buildAlloy(mocks); - const { event, result } = await alloy.sendEvent( - { - renderDecisions: true, - }, - SCOPES_FOO1_FOO2_DECISIONS, - ); - expect(event.toJSON()).toEqual({ - query: { - personalization: { - schemas: [ - "https://ns.adobe.com/personalization/default-content-item", - "https://ns.adobe.com/personalization/html-content-item", - "https://ns.adobe.com/personalization/json-content-item", - "https://ns.adobe.com/personalization/redirect-item", - "https://ns.adobe.com/personalization/ruleset-item", - "https://ns.adobe.com/personalization/message/in-app", - "https://ns.adobe.com/personalization/message/content-card", - "https://ns.adobe.com/personalization/dom-action", - ], - decisionScopes: ["__view__"], - surfaces: ["web://example.com/home"], - }, - }, - }); - - expect(result).toEqual({ - propositions: [ - { - renderAttempted: false, - id: "TNT:ABC:A", - scope: "Foo1", - items: [ - { - schema: "https://ns.adove.com/experience/item-article", - data: { - id: "1", - url: "https://foo.com/article/1", - thumbnailUrl: "https://foo.com/image/1?size=400x300", - }, - }, - { - schema: "https://ns.adove.com/experience/item-article", - data: { - id: "2", - url: "https://foo.com/article/2", - thumbnailUrl: "https://foo.com/image/2?size=400x300", - }, - }, - { - schema: "https://ns.adove.com/experience/item-article", - data: { - id: "3", - url: "https://foo.com/article/3", - thumbnailUrl: "https://foo.com/image/3?size=400x300", - }, - }, - ], - scopeDetails: { - blah: "test", - }, - }, - { - renderAttempted: false, - id: "TNT:ABC:A", - scope: "Foo2", - items: [ - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "A", - content: "Banner A ....", - }, - }, - ], - }, - ], - decisions: [ - { - id: "TNT:ABC:A", - scope: "Foo1", - items: [ - { - schema: "https://ns.adove.com/experience/item-article", - data: { - id: "1", - url: "https://foo.com/article/1", - thumbnailUrl: "https://foo.com/image/1?size=400x300", - }, - }, - { - schema: "https://ns.adove.com/experience/item-article", - data: { - id: "2", - url: "https://foo.com/article/2", - thumbnailUrl: "https://foo.com/image/2?size=400x300", - }, - }, - { - schema: "https://ns.adove.com/experience/item-article", - data: { - id: "3", - url: "https://foo.com/article/3", - thumbnailUrl: "https://foo.com/image/3?size=400x300", - }, - }, - ], - scopeDetails: { - blah: "test", - }, - }, - { - id: "TNT:ABC:A", - scope: "Foo2", - items: [ - { - schema: "https://ns.adove.com/experience/item", - data: { - id: "A", - content: "Banner A ....", - }, - }, - ], - }, - ], - }); - expect(mocks.sendEvent).not.toHaveBeenCalled(); - - expect(mocks.logger.warn).not.toHaveBeenCalled(); - expect(mocks.logger.error).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/Personalization/utils/addRenderAttemptedToDecisions.spec.js b/test/unit/specs/components/Personalization/utils/addRenderAttemptedToDecisions.spec.js deleted file mode 100644 index bf1df1a0b..000000000 --- a/test/unit/specs/components/Personalization/utils/addRenderAttemptedToDecisions.spec.js +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2021 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import addRenderAttemptedToDecisions from "../../../../../../src/components/Personalization/utils/addRenderAttemptedToDecisions.js"; - -describe("Personalization::addRenderAttemptedToDecisions", () => { - it("adds a renderAttempted flag", () => { - const decisions = [ - { - blah: "123", - }, - { - blah: "345", - }, - ]; - const result = addRenderAttemptedToDecisions({ - decisions, - renderAttempted: true, - }); - expect(result[0].renderAttempted).toEqual(true); - expect(result[1].renderAttempted).toEqual(true); - expect(decisions[0].renderAttempted).toBeUndefined(); - expect(decisions[1].renderAttempted).toBeUndefined(); - }); -}); diff --git a/test/unit/specs/components/Personalization/utils/createAsyncArray.spec.js b/test/unit/specs/components/Personalization/utils/createAsyncArray.spec.js deleted file mode 100644 index 8b12f9236..000000000 --- a/test/unit/specs/components/Personalization/utils/createAsyncArray.spec.js +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createAsyncArray from "../../../../../../src/components/Personalization/utils/createAsyncArray.js"; -import { defer } from "../../../../../../src/utils/index.js"; -import flushPromiseChains from "../../../../helpers/flushPromiseChains.js"; - -describe("Personalization::utils::createAsyncArray", () => { - it("should start with an empty array", async () => { - const asyncArray = createAsyncArray(); - expect(await asyncArray.clear()).toEqual([]); - }); - - it("should add items to the array, and clear the items", async () => { - const asyncArray = createAsyncArray(); - asyncArray.concat(Promise.resolve(["myitem1"])); - expect(await asyncArray.clear()).toEqual(["myitem1"]); - expect(await asyncArray.clear()).toEqual([]); - }); - - it("should add multiple arrays", async () => { - const asyncArray = createAsyncArray(); - asyncArray.concat(Promise.resolve(["myitem1"])); - asyncArray.concat(Promise.resolve(["myitem2"])); - expect(await asyncArray.clear()).toEqual(["myitem1", "myitem2"]); - }); - - it("should wait for items while clearing the array", async () => { - const asyncArray = createAsyncArray(); - const deferred = defer(); - asyncArray.concat(deferred.promise); - const clearPromise = asyncArray.clear(); - await flushPromiseChains(); - expectAsync(clearPromise).toBePending(); - deferred.resolve(["myitem1"]); - expect(await clearPromise).toEqual(["myitem1"]); - }); - - it("should handle rejected promises", async () => { - const asyncArray = createAsyncArray(); - asyncArray.concat(Promise.resolve([1, 2])); - asyncArray.concat(Promise.reject(new Error("Error!"))); - expect(await asyncArray.clear()).toEqual([1, 2]); - }); -}); diff --git a/test/unit/specs/components/Personalization/utils/isAuthoringModeEnabled.spec.js b/test/unit/specs/components/Personalization/utils/isAuthoringModeEnabled.spec.js deleted file mode 100644 index 4f2ea01a4..000000000 --- a/test/unit/specs/components/Personalization/utils/isAuthoringModeEnabled.spec.js +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isAuthoringModeEnabled from "../../../../../../src/components/Personalization/utils/isAuthoringModeEnabled.js"; - -describe("Personalization::isAuthoringModeEnabled", () => { - it("returns true if authoring mode is enabled", () => { - const doc = { - location: { - href: "http://foo.com?adobe_authoring_enabled=1", - }, - }; - expect(isAuthoringModeEnabled(doc)).toEqual(true); - }); - - it("returns false if authoring mode is disabled", () => { - const doc = { - location: { - href: "http://foo.com", - }, - }; - expect(isAuthoringModeEnabled(doc)).toEqual(false); - }); -}); diff --git a/test/unit/specs/components/Personalization/utils/metaUtils.spec.js b/test/unit/specs/components/Personalization/utils/metaUtils.spec.js deleted file mode 100644 index f97d1aa76..000000000 --- a/test/unit/specs/components/Personalization/utils/metaUtils.spec.js +++ /dev/null @@ -1,194 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - cleanMetas, - dedupeMetas, -} from "../../../../../../src/components/Personalization/utils/metaUtils.js"; - -describe("Personalization::metaUtils", () => { - it("cleanMetas", () => { - expect( - cleanMetas([ - { - id: "8f5", - scope: "web://aepdemo.com/", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "39", - characteristics: { - eventToken: "eyJ", - }, - activity: { - id: "12", - }, - }, - trackingLabel: "lbl-buy-now", - scopeType: "page", - }, - { - id: "e9b", - scope: "web://aepdemo.com/", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "03", - characteristics: { - eventToken: "eyJ", - }, - activity: { - id: "4f2", - matchedSurfaces: ["web://aepdemo.com/"], - }, - }, - trackingLabel: "lbl-buy-now", - scopeType: "page", - }, - ]), - ).toEqual([ - { - id: "8f5", - scope: "web://aepdemo.com/", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "39", - characteristics: { - eventToken: "eyJ", - }, - activity: { - id: "12", - }, - }, - }, - { - id: "e9b", - scope: "web://aepdemo.com/", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "03", - characteristics: { - eventToken: "eyJ", - }, - activity: { - id: "4f2", - matchedSurfaces: ["web://aepdemo.com/"], - }, - }, - }, - ]); - }); - - it("dedupeMetas", () => { - expect( - dedupeMetas([ - { - id: "8f5", - scope: "web://aepdemo.com/", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "39", - characteristics: { - eventToken: "eyJ", - }, - activity: { - id: "12", - }, - }, - trackingLabel: "lbl-buy-now", - scopeType: "page", - }, - { - id: "e9b", - scope: "web://aepdemo.com/", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "03", - characteristics: { - eventToken: "eyJ", - }, - activity: { - id: "4f2", - matchedSurfaces: ["web://aepdemo.com/"], - }, - }, - trackingLabel: "lbl-buy-now", - scopeType: "page", - }, - { - id: "8f5", - scope: "web://aepdemo.com/", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "39", - characteristics: { - eventToken: "eyJ", - }, - activity: { - id: "12", - }, - }, - trackingLabel: "lbl-buy-now", - scopeType: "page", - }, - { - id: "e9b", - scope: "web://aepdemo.com/", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "03", - characteristics: { - eventToken: "eyJ", - }, - activity: { - id: "4f2", - matchedSurfaces: ["web://aepdemo.com/"], - }, - }, - trackingLabel: "lbl-buy-now", - scopeType: "page", - }, - ]), - ).toEqual([ - { - id: "8f5", - scope: "web://aepdemo.com/", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "39", - characteristics: { - eventToken: "eyJ", - }, - activity: { - id: "12", - }, - }, - trackingLabel: "lbl-buy-now", - scopeType: "page", - }, - { - id: "e9b", - scope: "web://aepdemo.com/", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "03", - characteristics: { - eventToken: "eyJ", - }, - activity: { - id: "4f2", - matchedSurfaces: ["web://aepdemo.com/"], - }, - }, - trackingLabel: "lbl-buy-now", - scopeType: "page", - }, - ]); - }); -}); diff --git a/test/unit/specs/components/Personalization/utils/surfaceUtils.spec.js b/test/unit/specs/components/Personalization/utils/surfaceUtils.spec.js deleted file mode 100644 index 3a74e3be7..000000000 --- a/test/unit/specs/components/Personalization/utils/surfaceUtils.spec.js +++ /dev/null @@ -1,256 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - buildPageSurface, - isPageWideSurface, - normalizeSurfaces, -} from "../../../../../../src/components/Personalization/utils/surfaceUtils.js"; - -let pageLocation; -let logger; - -const getPageLocation = () => pageLocation; - -describe("Personalization::surfaceUtils", () => { - beforeEach(() => { - logger = jasmine.createSpyObj("logger", ["error", "warn"]); - pageLocation = { - host: "domain.com", - pathname: "/products/test/", - }; - }); - - it("builds page-wide surface from location", () => { - expect(buildPageSurface(getPageLocation)).toEqual( - "web://domain.com/products/test", - ); - pageLocation = { - host: "DOMain.com", - pathname: undefined, - }; - expect(buildPageSurface(getPageLocation)).toEqual("web://domain.com/"); - pageLocation = { - host: "domain.com:8080", - pathname: "/", - }; - expect(buildPageSurface(getPageLocation)).toEqual("web://domain.com:8080/"); - pageLocation = { - host: "domain.com", - pathname: "/a", - }; - expect(buildPageSurface(getPageLocation)).toEqual("web://domain.com/a"); - pageLocation = { - host: "domain.com", - pathname: "/a/", - }; - expect(buildPageSurface(getPageLocation)).toEqual("web://domain.com/a"); - }); - - it("checks for a page-wide surface", () => { - expect(isPageWideSurface("__view__")).toBeFalse(); - expect(isPageWideSurface("name")).toBeFalse(); - expect(isPageWideSurface("web://domain.com")).toBeTrue(); - expect(isPageWideSurface("web://domain.com/path/page.html")).toBeTrue(); - expect(isPageWideSurface("web://domain.com#fragment")).toBeFalse(); - expect(isPageWideSurface("webapp://domain.com")).toBeFalse(); - expect(isPageWideSurface("webapp://domain.com#view")).toBeFalse(); - }); - - it("expands fragment surfaces", () => { - let result = normalizeSurfaces([], getPageLocation, logger); - expect(result).toEqual([]); - result = normalizeSurfaces( - ["web://custom.surface.com", "#fragment1", "test"], - getPageLocation, - logger, - ); - expect(result).toEqual([ - "web://custom.surface.com/", - "web://domain.com/products/test#fragment1", - ]); - expect(logger.warn).toHaveBeenCalledOnceWith("Invalid surface: test"); - }); - - it("validates & normalizes surface type", () => { - const result = normalizeSurfaces( - [ - "test://domain1.com/test", - "web://domain2.com/test", - "we b://domain3.com/test", - "webapp://domain4.com/test", - "webAPP://domain5.com/test", - "://domain5.com/test", - "web:///domain6.com/test", - "mobileapp://domain7.com/test", - ], - getPageLocation, - logger, - ); - expect(result).toEqual([ - "web://domain2.com/test", - "webapp://domain4.com/test", - "webapp://domain5.com/test", - ]); - expect(logger.warn).toHaveBeenCalledTimes(5); - }); - - it("validates & normalizes surface authority", () => { - let result = normalizeSurfaces( - [ - "web://foo.com", - "web://www.example.com", - "web://✪df.ws", - "web://userid:password@example.com:8080", - "web://userid@example.com", - "web://userid@example.com:8080", - "web://userid:password@example.com", - "web://142.42.1.1", - "web://142.42.1.1:8080", - "web://➡.ws", - "web://⌘.ws", - "web://☺.damowmow.com", - "web://مثال.إختبار", - "web://例子.测试", - "web://उदाहरण.परीक्षा", - "web://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", - "web://1337.net", - "web://a.b-c.de", - "web://223.255.255.254", - "web://[::1]", - "web://[ff11:af21:::1]:3000", - ], - getPageLocation, - logger, - ); - expect(result).toEqual([ - "web://foo.com/", - "web://www.example.com/", - "web://✪df.ws/", - "web://userid:password@example.com:8080/", - "web://userid@example.com/", - "web://userid@example.com:8080/", - "web://userid:password@example.com/", - "web://142.42.1.1/", - "web://142.42.1.1:8080/", - "web://➡.ws/", - "web://⌘.ws/", - "web://☺.damowmow.com/", - "web://مثال.إختبار/", - "web://例子.测试/", - "web://उदाहरण.परीक्षा/", - "web://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com/", - "web://1337.net/", - "web://a.b-c.de/", - "web://223.255.255.254/", - "web://[::1]/", - "web://[ff11:af21:::1]:3000/", - ]); - expect(logger.warn).not.toHaveBeenCalled(); - - result = normalizeSurfaces( - [ - "web://foo?.com", - "web://d f.ws", - "web://userid@example.com:8a080", - "web://userid@examp&le.com", - "web:///page", - "web://[::1)", - "web://[ff11:af21:12zx::1]:3000", - ], - getPageLocation, - logger, - ); - expect(result).toEqual([]); - expect(logger.warn).toHaveBeenCalledTimes(7); - }); - - it("validates & normalizes surface path", () => { - let result = normalizeSurfaces( - [ - "web://domain1.com", - "web://domain2.com///", - "web://domain3.com/PROD.1/a", - "web://domain4.com/~prod-1/bb/c/", - "web://domain5.com/例子/☺", - "web://domain6.com/a/%D0%B6%D0%BE%D1%80%D0%B0%D1%82%D0%B5%D1%81%D1%82", - ], - getPageLocation, - logger, - ); - expect(result).toEqual([ - "web://domain1.com/", - "web://domain2.com/", - "web://domain3.com/PROD.1/a", - "web://domain4.com/~prod-1/bb/c", - "web://domain5.com/例子/☺", - "web://domain6.com/a/%D0%B6%D0%BE%D1%80%D0%B0%D1%82%D0%B5%D1%81%D1%82", - ]); - expect(logger.warn).not.toHaveBeenCalled(); - - result = normalizeSurfaces( - [ - "web://domain1.com/pr od", - "web://domain2.com/+/1", - "web://domain3.com/$PROD.1/a", - "web://domain4.com/~prod-1/bb/c/?query=aa", - "web://domain5.com/例子/test*/1", - "web://domain6.com/a/%D0%B6%D0%ZX", - "web://domain7.com/a/%D0%B6%D0%%AF", - ], - getPageLocation, - logger, - ); - expect(result).toEqual([]); - expect(logger.warn).toHaveBeenCalledTimes(7); - }); - - it("validates surface fragment", () => { - let result = normalizeSurfaces( - [ - "web://domain1.com#/home", - "web://domain2.com#home", - "web://domain3.com#PROD.1", - "web://domain4.com#~prod-1/bb/c/", - "web://domain5.com#例子/☺", - "web://domain6.com#my-%D0%B6%D0%BE%D1%80%D0%B0%D1%82%D0%B5%D1%81%D1%82", - ], - getPageLocation, - logger, - ); - expect(result).toEqual([ - "web://domain1.com/#/home", - "web://domain2.com/#home", - "web://domain3.com/#PROD.1", - "web://domain4.com/#~prod-1/bb/c/", - "web://domain5.com/#例子/☺", - "web://domain6.com/#my-%D0%B6%D0%BE%D1%80%D0%B0%D1%82%D0%B5%D1%81%D1%82", - ]); - expect(logger.warn).not.toHaveBeenCalled(); - - result = normalizeSurfaces( - [ - "web://domain1.com/#pr od", - "web://domain2.com/#+/1", - "web://domain3.com/#$PROD.1/a", - "web://domain4.com/#~prod-1/bb/c/?query=aa", - "web://domain5.com/#例子/test*!/1", - "web://domain6.com/#a/%D0%B6%D0%ZX", - "web://domain7.com/#a/%D0%B6%D0%%AF", - ], - getPageLocation, - logger, - ); - expect(result).toEqual([]); - expect(logger.warn).toHaveBeenCalledTimes(7); - }); -}); diff --git a/test/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js b/test/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js deleted file mode 100644 index e11bec5da..000000000 --- a/test/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js +++ /dev/null @@ -1,255 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import "jasmine-expect"; -import { DOM_ACTION } from "@adobe/alloy/libEs5/constants/schema.js"; -import validateApplyPropositionsOptions, { - EMPTY_PROPOSITIONS, -} from "../../../../../src/components/Personalization/validateApplyPropositionsOptions.js"; - -const PROPOSITIONS = [ - { - id: "abc", - scope: "web://aepdemo.com/", - scopeDetails: { decisionProvider: "AJO" }, - items: [ - { - id: "abc", - schema: DOM_ACTION, - data: { - type: "setHtml", - content: "woof", - selector: "#paragraph-text-1", - }, - }, - ], - }, - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNDQyMzU4IiwiZXhwZXJpZW5jZUlkIjoiIn0=", - scope: "__view__", - items: [ - { - id: "442358", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "click", - format: "application/vnd.adobe.target.dom-action", - selector: - "#root > DIV:nth-of-type(1) > UL:nth-of-type(1) > LI:nth-of-type(4) > A:nth-of-type(1)", - }, - }, - ], - scopeDetails: { - decisionProvider: "TGT", - activity: { - id: "442358", - }, - characteristics: { - eventToken: "+8J+6aZYrFL4hGqSwULhUQ==", - analyticsToken: "442358:0:0|32767", - }, - }, - }, -]; - -const METADATA = { - scope1: { - selector: "#home-item1", - actionType: "setHtml", - }, -}; - -describe("Personalization::validateApplyPropositionsOptions", () => { - let loggerSpy; - let logger; - - const resetLogger = () => { - loggerSpy = jasmine.createSpy("logger.warn"); - logger = { - warn: loggerSpy, - }; - }; - - beforeEach(() => { - resetLogger(); - }); - - it("it should log a warning when no options are present", () => { - const result = validateApplyPropositionsOptions({ - logger, - }); - - expect(loggerSpy).toHaveBeenCalled(); - expect(result).toEqual(EMPTY_PROPOSITIONS); - }); - - it("it should log a warning when propositions array is missing from options", () => { - const result = validateApplyPropositionsOptions({ - logger, - options: {}, - }); - - expect(loggerSpy).toHaveBeenCalled(); - expect(loggerSpy.calls.first().args[1].message).toEqual( - "'propositions' is a required option", - ); - - expect(result).toEqual(EMPTY_PROPOSITIONS); - }); - - it("it should log a warning when propositions is empty array", () => { - const result = validateApplyPropositionsOptions({ - logger, - options: { - propositions: [], - }, - }); - - expect(loggerSpy).toHaveBeenCalled(); - expect(loggerSpy.calls.first().args[1].message).toEqual( - "'propositions': Expected a non-empty array, but got [].", - ); - - expect(result).toEqual(EMPTY_PROPOSITIONS); - }); - - it("it should log a warning when propositions are missing required values", () => { - const scopeDetails = { decisionProvider: "AJO" }; - - const tests = [ - { - propositions: [{}], - errorMessage: - "'propositions[0].id' is a required option\n" + - "'propositions[0].scope' is a required option\n" + - "'propositions[0].scopeDetails' is a required option\n" + - "'propositions[0].items' is a required option", - }, - { - propositions: [{ id: "abc" }], - errorMessage: - "'propositions[0].scope' is a required option\n" + - "'propositions[0].scopeDetails' is a required option\n" + - "'propositions[0].items' is a required option", - }, - { - propositions: [{ id: "abc", scope: "web://aepdemo.com/" }], - errorMessage: - "'propositions[0].scopeDetails' is a required option\n" + - "'propositions[0].items' is a required option", - }, - { - propositions: [ - { id: "abc", scope: "web://aepdemo.com/", scopeDetails }, - ], - errorMessage: "'propositions[0].items' is a required option", - }, - { - propositions: [ - { - id: "abc", - scope: "web://aepdemo.com/", - scopeDetails, - items: [], - }, - ], - errorMessage: - "'propositions[0].items': Expected a non-empty array, but got [].", - }, - { - propositions: [ - { - id: "abc", - scope: "web://aepdemo.com/", - scopeDetails, - items: [{}], - }, - ], - errorMessage: - "'propositions[0].items[0].id' is a required option\n" + - "'propositions[0].items[0].schema' is a required option", - }, - { - propositions: [ - { - id: "abc", - scope: "web://aepdemo.com/", - scopeDetails, - items: [{ id: "abc" }], - }, - ], - errorMessage: "'propositions[0].items[0].schema' is a required option", - }, - ]; - - for (let i = 0; i < tests.length; i += 1) { - const { propositions, errorMessage } = tests[i]; - resetLogger(); - - const result = validateApplyPropositionsOptions({ - logger, - options: { - propositions, - }, - }); - - expect(loggerSpy).toHaveBeenCalled(); - - expect(loggerSpy.calls.first().args[1].message).toEqual(errorMessage); - - expect(result).toEqual(EMPTY_PROPOSITIONS); - } - }); - - it("it should not log a warning when extra options are present", () => { - const result = validateApplyPropositionsOptions({ - logger, - options: { - bad: "bad", - propositions: PROPOSITIONS, - }, - }); - - expect(loggerSpy).not.toHaveBeenCalled(); - expect(result).not.toEqual(EMPTY_PROPOSITIONS); - }); - - it("it should log a warning when metadata is not an object", () => { - const result = validateApplyPropositionsOptions({ - logger, - options: { - propositions: PROPOSITIONS, - metadata: [], - }, - }); - - expect(loggerSpy).toHaveBeenCalled(); - expect(loggerSpy.calls.first().args[1].message).toEqual( - "'metadata': Expected an object, but got [].", - ); - - expect(result).toEqual(EMPTY_PROPOSITIONS); - }); - - it("it should not log a warning when propositions and metadata are present", () => { - const result = validateApplyPropositionsOptions({ - logger, - options: { - propositions: PROPOSITIONS, - metadata: METADATA, - }, - }); - - expect(loggerSpy).not.toHaveBeenCalled(); - expect(result).not.toEqual(EMPTY_PROPOSITIONS); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.spec.js b/test/unit/specs/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.spec.js deleted file mode 100644 index 9df10f791..000000000 --- a/test/unit/specs/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import inAppMessageConsequenceAdapter from "../../../../../../src/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.js"; -import { TEXT_HTML } from "../../../../../../src/constants/contentType.js"; - -describe("RulesEngine:inAppMessageConsequenceAdapter", () => { - it("handles cjmiam", () => { - expect( - inAppMessageConsequenceAdapter( - "72042c7c-4e34-44f6-af95-1072ae117424", - "cjmiam", - { - mobileParameters: { - verticalAlign: "center", - dismissAnimation: "top", - verticalInset: 0, - backdropOpacity: 0.2, - cornerRadius: 15, - horizontalInset: 0, - uiTakeover: true, - horizontalAlign: "center", - width: 80, - displayAnimation: "top", - backdropColor: "#000000", - height: 60, - }, - html: "", - }, - ), - ).toEqual({ - schema: "https://ns.adobe.com/personalization/message/in-app", - data: { - mobileParameters: { - verticalAlign: "center", - dismissAnimation: "top", - verticalInset: 0, - backdropOpacity: 0.2, - cornerRadius: 15, - horizontalInset: 0, - uiTakeover: true, - horizontalAlign: "center", - width: 80, - displayAnimation: "top", - backdropColor: "#000000", - height: 60, - }, - webParameters: jasmine.any(Object), - content: "", - contentType: TEXT_HTML, - }, - id: "72042c7c-4e34-44f6-af95-1072ae117424", - }); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.spec.js b/test/unit/specs/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.spec.js deleted file mode 100644 index de2c542d4..000000000 --- a/test/unit/specs/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.spec.js +++ /dev/null @@ -1,70 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import schemaTypeConsequenceAdapter from "../../../../../../src/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.js"; -import { TEXT_HTML } from "../../../../../../src/constants/contentType.js"; - -describe("RulesEngine:schemaTypeConsequenceAdapter", () => { - it("handles schema", () => { - expect( - schemaTypeConsequenceAdapter( - "72042c7c-4e34-44f6-af95-1072ae117424", - "schema", - { - schema: "https://ns.adobe.com/personalization/message/in-app", - data: { - mobileParameters: { - verticalAlign: "center", - dismissAnimation: "top", - verticalInset: 0, - backdropOpacity: 0.2, - cornerRadius: 15, - horizontalInset: 0, - uiTakeover: true, - horizontalAlign: "center", - width: 80, - displayAnimation: "top", - backdropColor: "#000000", - height: 60, - }, - webParameters: jasmine.any(Object), - content: "", - contentType: TEXT_HTML, - }, - id: "72042c7c-4e34-44f6-af95-1072ae117424", - }, - ), - ).toEqual({ - schema: "https://ns.adobe.com/personalization/message/in-app", - data: { - mobileParameters: { - verticalAlign: "center", - dismissAnimation: "top", - verticalInset: 0, - backdropOpacity: 0.2, - cornerRadius: 15, - horizontalInset: 0, - uiTakeover: true, - horizontalAlign: "center", - width: 80, - displayAnimation: "top", - backdropColor: "#000000", - height: 60, - }, - webParameters: jasmine.any(Object), - content: "", - contentType: TEXT_HTML, - }, - id: "72042c7c-4e34-44f6-af95-1072ae117424", - }); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/constants.spec.js b/test/unit/specs/components/RulesEngine/constants.spec.js deleted file mode 100644 index 26ef92385..000000000 --- a/test/unit/specs/components/RulesEngine/constants.spec.js +++ /dev/null @@ -1,11 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ diff --git a/test/unit/specs/components/RulesEngine/contextTestUtils.js b/test/unit/specs/components/RulesEngine/contextTestUtils.js deleted file mode 100644 index 84d11fcc4..000000000 --- a/test/unit/specs/components/RulesEngine/contextTestUtils.js +++ /dev/null @@ -1,153 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createContextProvider from "../../../../../src/components/RulesEngine/createContextProvider.js"; -import createOnResponseHandler from "../../../../../src/components/RulesEngine/createOnResponseHandler.js"; -import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; -import createDecisionProvider from "../../../../../src/components/RulesEngine/createDecisionProvider.js"; -import { injectGetBrowser } from "../../../../../src/utils/index.js"; - -export const proposition = { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scopeDetails: { - activity: { - id: "abc#xyz", - }, - }, - items: [ - { - schema: "https://ns.adobe.com/personalization/mock-action", - data: { - hello: "kitty", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - ], - scope: "web://mywebsite.com", -}; - -export const mockWindow = ({ - title = "My awesome website", - referrer = "https://www.google.com/search?q=adobe+journey+optimizer&oq=adobe+journey+optimizer", - url = "https://pro.mywebsite.org:8080/about?m=1&t=5&name=jimmy#home", - width = 100, - height = 100, - scrollX = 0, - scrollY = 10, - userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36", -}) => ({ - title, - referrer, - url, - width, - height, - scrollX, - scrollY, - navigator: { - userAgent, - }, -}); - -export const payloadWithCondition = (condition) => { - return { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scopeDetails: { - activity: { - id: "abc#xyz", - }, - }, - items: [ - { - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - schema: "https://ns.adobe.com/personalization/ruleset-item", - data: { - version: 1, - rules: [ - { - condition: { - definition: { - conditions: [condition], - logic: "and", - }, - type: "group", - }, - consequences: [ - { - type: "schema", - detail: { - schema: "https://ns.adobe.com/personalization/mock-action", - data: { - hello: "kitty", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - ], - }, - ], - }, - }, - ], - scope: "web://mywebsite.com", - }; -}; -export const mockRulesetResponseWithCondition = (condition) => { - return { - getPayloadsByType: () => [ - payloadWithCondition({ - definition: { - conditions: [condition], - logic: "and", - }, - type: "group", - }), - ], - }; -}; - -const mockEvent = { - getContent: () => ({ query: {} }), - hasQuery: () => true, - getViewName: () => undefined, -}; - -export const setupResponseHandler = (applyResponse, window, condition) => { - const storage = jasmine.createSpyObj("storage", [ - "getItem", - "setItem", - "clear", - ]); - const eventRegistry = createEventRegistry({ storage }); - const decisionProvider = createDecisionProvider({ eventRegistry }); - const getBrowser = injectGetBrowser({ - userAgent: window.navigator.userAgent, - }); - const contextProvider = createContextProvider({ - eventRegistry, - window, - getBrowser, - }); - - const onResponseHandler = createOnResponseHandler({ - renderDecisions: true, - decisionProvider, - applyResponse, - event: mockEvent, - decisionContext: contextProvider.getContext(), - }); - - onResponseHandler({ - response: mockRulesetResponseWithCondition(condition), - }); -}; diff --git a/test/unit/specs/components/RulesEngine/createApplyResponse.spec.js b/test/unit/specs/components/RulesEngine/createApplyResponse.spec.js deleted file mode 100644 index 7c0668ece..000000000 --- a/test/unit/specs/components/RulesEngine/createApplyResponse.spec.js +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createApplyResponse from "../../../../../src/components/RulesEngine/createApplyResponse.js"; - -describe("RulesEngine:createApplyResponse", () => { - const proposition = { - id: "AT:eyJhY3Rpdml0eUlkIjoiMTQxMDY0IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", - scope: "__view__", - items: [], - }; - - it("calls lifecycle.onDecision with propositions", () => { - const lifecycle = jasmine.createSpyObj("lifecycle", { - onDecision: Promise.resolve(), - }); - - const applyResponse = createApplyResponse(lifecycle); - - const mockEvent = { getViewName: () => undefined }; - const personalization = {}; - - applyResponse({ - propositions: [proposition], - event: mockEvent, - personalization, - }); - - expect(lifecycle.onDecision).toHaveBeenCalledWith({ - renderDecisions: false, - propositions: [proposition], - event: mockEvent, - personalization: {}, - }); - }); - - it("calls lifecycle.onDecision with viewName", () => { - const lifecycle = jasmine.createSpyObj("lifecycle", { - onDecision: Promise.resolve(), - }); - - const applyResponse = createApplyResponse(lifecycle); - const mockEvent = { getViewName: () => "oh hai" }; - - applyResponse({ - renderDecisions: true, - event: mockEvent, - personalization: {}, - propositions: [proposition], - }); - - expect(lifecycle.onDecision).toHaveBeenCalledWith({ - renderDecisions: true, - propositions: [proposition], - event: mockEvent, - personalization: {}, - }); - }); - - it("call lifecycle.onDecision even if no propositions", () => { - // this use case is necessary for content cards with no items - const lifecycle = jasmine.createSpyObj("lifecycle", { - onDecision: Promise.resolve(), - }); - - const applyResponse = createApplyResponse(lifecycle); - const mockEvent = { getViewName: () => undefined }; - - applyResponse({ - renderDecisions: true, - propositions: [], - event: mockEvent, - personalization: {}, - }); - - expect(lifecycle.onDecision).toHaveBeenCalledWith({ - renderDecisions: true, - propositions: [], - event: mockEvent, - personalization: {}, - }); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/createConsequenceAdapter.spec.js b/test/unit/specs/components/RulesEngine/createConsequenceAdapter.spec.js deleted file mode 100644 index d96381cea..000000000 --- a/test/unit/specs/components/RulesEngine/createConsequenceAdapter.spec.js +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createConsequenceAdapter from "../../../../../src/components/RulesEngine/createConsequenceAdapter.js"; -import { TEXT_HTML } from "../../../../../src/constants/contentType.js"; - -describe("RulesEngine:createConsequenceAdapter", () => { - const ADAPTED_CONSEQUENCE = { - schema: "https://ns.adobe.com/personalization/message/in-app", - data: { - mobileParameters: { - verticalAlign: "center", - dismissAnimation: "top", - verticalInset: 0, - backdropOpacity: 0.2, - cornerRadius: 15, - horizontalInset: 0, - uiTakeover: true, - horizontalAlign: "center", - width: 80, - displayAnimation: "top", - backdropColor: "#000000", - height: 60, - }, - webParameters: jasmine.any(Object), - content: "", - contentType: TEXT_HTML, - }, - id: "72042c7c-4e34-44f6-af95-1072ae117424", - }; - - it("handles cjmiam", () => { - const consequenceAdapter = createConsequenceAdapter(); - - const adaptedConsequence = consequenceAdapter({ - id: "72042c7c-4e34-44f6-af95-1072ae117424", - type: "cjmiam", - detail: { - mobileParameters: { - verticalAlign: "center", - dismissAnimation: "top", - verticalInset: 0, - backdropOpacity: 0.2, - cornerRadius: 15, - horizontalInset: 0, - uiTakeover: true, - horizontalAlign: "center", - width: 80, - displayAnimation: "top", - backdropColor: "#000000", - height: 60, - }, - html: "", - }, - }); - - expect(adaptedConsequence).toEqual(ADAPTED_CONSEQUENCE); - }); - - it("handles schema", () => { - const consequenceAdapter = createConsequenceAdapter(); - - const adaptedConsequence = consequenceAdapter({ - id: "72042c7c-4e34-44f6-af95-1072ae117424", - type: "schema", - detail: { - ...ADAPTED_CONSEQUENCE, - }, - }); - - expect(adaptedConsequence).toEqual(ADAPTED_CONSEQUENCE); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/createContextProvider.spec.js b/test/unit/specs/components/RulesEngine/createContextProvider.spec.js deleted file mode 100644 index a6a3ef349..000000000 --- a/test/unit/specs/components/RulesEngine/createContextProvider.spec.js +++ /dev/null @@ -1,156 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createContextProvider from "../../../../../src/components/RulesEngine/createContextProvider.js"; -import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; - -describe("RulesEngine:createContextProvider", () => { - let contextProvider; - let eventRegistry; - let storage; - let window; - let mockedTimestamp; - let getBrowser; - - beforeEach(() => { - storage = jasmine.createSpyObj("storage", ["getItem", "setItem", "clear"]); - window = { - title: "My awesome website", - referrer: "https://stage.applookout.net/", - url: "https://my.web-site.net:8080/about?m=1&t=5&name=jimmy#home", - width: 100, - height: 100, - scrollX: 10, - scrollY: 10, - navigator: { - userAgent: - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36", - }, - }; - mockedTimestamp = new Date(Date.UTC(2023, 4, 11, 12, 34, 56)); - jasmine.clock().install(); - jasmine.clock().mockDate(mockedTimestamp); - getBrowser = jasmine.createSpy().and.returnValue("Chrome"); - }); - - afterEach(() => { - jasmine.clock().uninstall(); - }); - it("returns page context", () => { - eventRegistry = createEventRegistry({ storage }); - contextProvider = createContextProvider({ - eventRegistry, - window, - getBrowser, - }); - - expect(contextProvider.getContext()).toEqual( - jasmine.objectContaining({ - "page.title": "My awesome website", - "page.url": - "https://my.web-site.net:8080/about?m=1&t=5&name=jimmy#home", - "page.path": "/about", - "page.query": "m=1&t=5&name=jimmy", - "page.fragment": "home", - "page.domain": "my.web-site.net", - "page.subdomain": "my", - "page.topLevelDomain": "net", - }), - ); - }); - it("returns referring page context", () => { - eventRegistry = createEventRegistry({ storage }); - contextProvider = createContextProvider({ - eventRegistry, - window, - getBrowser, - }); - - expect(contextProvider.getContext()).toEqual( - jasmine.objectContaining({ - "referringPage.url": "https://stage.applookout.net/", - "referringPage.path": "/", - "referringPage.query": "", - "referringPage.fragment": "", - "referringPage.domain": "stage.applookout.net", - "referringPage.subdomain": "stage", - "referringPage.topLevelDomain": "net", - }), - ); - }); - it("returns browser context", () => { - eventRegistry = createEventRegistry({ storage }); - contextProvider = createContextProvider({ - eventRegistry, - window, - getBrowser, - }); - - expect(contextProvider.getContext()).toEqual( - jasmine.objectContaining({ - "browser.name": "Chrome", - }), - ); - }); - it("returns windows context", () => { - eventRegistry = createEventRegistry({ storage }); - contextProvider = createContextProvider({ - eventRegistry, - window, - getBrowser, - }); - - expect(contextProvider.getContext()).toEqual( - jasmine.objectContaining({ - "window.height": 100, - "window.width": 100, - "window.scrollY": 10, - "window.scrollX": 10, - }), - ); - }); - it("includes provided context passed in", () => { - eventRegistry = createEventRegistry({ storage }); - contextProvider = createContextProvider({ - eventRegistry, - window, - getBrowser, - }); - - expect(contextProvider.getContext({ cool: "beans" })).toEqual( - jasmine.objectContaining({ - cool: "beans", - }), - ); - }); - - it("includes events context", () => { - const events = { - abc: { - event: { id: "abc", type: "display" }, - timestamp: new Date().getTime(), - count: 1, - }, - }; - eventRegistry = { - toJSON: () => events, - }; - contextProvider = createContextProvider({ - eventRegistry, - window, - getBrowser, - }); - - expect(contextProvider.getContext({ cool: "beans" }).events).toEqual( - events, - ); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/createDecisionHistory.spec.js b/test/unit/specs/components/RulesEngine/createDecisionHistory.spec.js deleted file mode 100644 index 6da185a03..000000000 --- a/test/unit/specs/components/RulesEngine/createDecisionHistory.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createDecisionHistory from "../../../../../src/components/RulesEngine/createDecisionHistory.js"; -import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; - -describe("RulesEngine:decisionHistory", () => { - let storage; - let history; - - beforeEach(() => { - storage = jasmine.createSpyObj("storage", ["getItem", "setItem", "clear"]); - - history = createDecisionHistory({ - eventRegistry: createEventRegistry({ storage }), - }); - }); - - it("records decision time", () => { - const decision = history.recordQualified({ id: "abc" }); - - expect(Object.getPrototypeOf(decision)).toEqual(Object.prototype); - expect(decision.timestamp).toEqual(jasmine.any(Number)); - }); - - it("preserves first decision time, if decision already recorded", (done) => { - const firstDecision = history.recordQualified({ id: "abc" }); - - setTimeout(() => { - expect(history.recordQualified({ id: "abc" }).firstTimestamp).toEqual( - firstDecision.firstTimestamp, - ); - expect(history.recordQualified({ id: "abc" }).firstTimestamp).toEqual( - firstDecision.timestamp, - ); - done(); - }, 20); - }); - - it("restores history from event storage", () => { - expect(storage.getItem).toHaveBeenCalledWith("events"); - }); - - it("saves history to event storage", (done) => { - history.recordQualified({ id: "abc" }); - - setTimeout(() => { - expect(storage.setItem).toHaveBeenCalledWith( - "events", - jasmine.any(String), - ); - done(); - }, 20); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/createDecisionProvider.spec.js b/test/unit/specs/components/RulesEngine/createDecisionProvider.spec.js deleted file mode 100644 index 25c51b2ce..000000000 --- a/test/unit/specs/components/RulesEngine/createDecisionProvider.spec.js +++ /dev/null @@ -1,474 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createDecisionProvider from "../../../../../src/components/RulesEngine/createDecisionProvider.js"; -import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; - -describe("RulesEngine:createDecisionProvider", () => { - let decisionProvider; - let storage; - let eventRegistry; - - beforeEach(() => { - storage = jasmine.createSpyObj("storage", ["getItem", "setItem", "clear"]); - eventRegistry = createEventRegistry({ storage }); - decisionProvider = createDecisionProvider({ eventRegistry }); - decisionProvider.addPayloads([ - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scope: "web://mywebsite.com", - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - eventToken: "abc", - }, - strategies: [ - { - strategyID: "3VQe3oIqiYq2RAsYzmDTSf", - treatmentID: "yu7rkogezumca7i0i44v", - }, - ], - activity: { - id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", - }, - correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", - }, - items: [ - { - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - schema: "https://ns.adobe.com/personalization/ruleset-item", - data: { - version: 1, - rules: [ - { - condition: { - definition: { - conditions: [ - { - definition: { - conditions: [ - { - definition: { - key: "color", - matcher: "eq", - values: ["orange", "blue"], - }, - type: "matcher", - }, - { - definition: { - key: "action", - matcher: "eq", - values: ["lipstick"], - }, - type: "matcher", - }, - ], - logic: "and", - }, - type: "group", - }, - ], - logic: "and", - }, - type: "group", - }, - consequences: [ - { - type: "schema", - detail: { - schema: - "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - type: "schema", - detail: { - schema: - "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - }, - ], - }, - }, - ], - }, - { - id: "3d5d69cd-acde-4eca-b43b-a54574b67bb0", - scope: "web://mywebsite.com", - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - eventToken: "abc", - }, - strategies: [ - { - strategyID: "3VQe3oIqiYq2RAsYzmDTSf", - treatmentID: "yu7rkogezumca7i0i44v", - }, - ], - activity: { - id: "48ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d168", - }, - correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", - }, - items: [ - { - id: "5229f502-38d6-40c3-9a3a-b5b1a6adc441", - schema: "https://ns.adobe.com/personalization/ruleset-item", - data: { - version: 1, - rules: [ - { - condition: { - definition: { - conditions: [ - { - definition: { - conditions: [ - { - definition: { - key: "xdm.web.webPageDetails.viewName", - matcher: "eq", - values: ["home"], - }, - type: "matcher", - }, - ], - logic: "and", - }, - type: "group", - }, - ], - logic: "and", - }, - type: "group", - }, - consequences: [ - { - type: "schema", - detail: { - schema: - "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "div#spa #spa-content h3", - type: "setHtml", - content: "i can haz?", - prehidingSelector: "div#spa #spa-content h3", - }, - id: "8a0d7a45-70fb-4845-a093-2133b5744c8d", - }, - id: "8a0d7a45-70fb-4845-a093-2133b5744c8d", - }, - { - type: "schema", - detail: { - schema: - "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "div#spa #spa-content p", - type: "setHtml", - content: "ALL YOUR BASE ARE BELONG TO US", - prehidingSelector: "div#spa #spa-content p", - }, - id: "a44af51a-e073-4e8c-92e1-84ac28210043", - }, - id: "a44af51a-e073-4e8c-92e1-84ac28210043", - }, - ], - }, - ], - }, - }, - ], - }, - ]); - }); - it("returns a single payload with items that qualify", () => { - expect( - decisionProvider.evaluate({ color: "blue", action: "lipstick" }), - ).toEqual([ - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scope: "web://mywebsite.com", - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - eventToken: "abc", - }, - strategies: [ - { - strategyID: "3VQe3oIqiYq2RAsYzmDTSf", - treatmentID: "yu7rkogezumca7i0i44v", - }, - ], - activity: { - id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", - }, - correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", - }, - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - }, - ]); - }); - it("returns a different single payload with items that qualify", () => { - expect( - decisionProvider.evaluate({ "xdm.web.webPageDetails.viewName": "home" }), - ).toEqual([ - { - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - eventToken: "abc", - }, - strategies: [ - { - strategyID: "3VQe3oIqiYq2RAsYzmDTSf", - treatmentID: "yu7rkogezumca7i0i44v", - }, - ], - activity: { - id: "48ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d168", - }, - correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", - }, - id: "3d5d69cd-acde-4eca-b43b-a54574b67bb0", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "div#spa #spa-content h3", - type: "setHtml", - content: "i can haz?", - prehidingSelector: "div#spa #spa-content h3", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "8a0d7a45-70fb-4845-a093-2133b5744c8d", - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "div#spa #spa-content p", - type: "setHtml", - content: "ALL YOUR BASE ARE BELONG TO US", - prehidingSelector: "div#spa #spa-content p", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "a44af51a-e073-4e8c-92e1-84ac28210043", - }, - ], - scope: "web://mywebsite.com", - }, - ]); - }); - it("returns two payloads with items that qualify", () => { - expect( - decisionProvider.evaluate({ - color: "blue", - action: "lipstick", - "xdm.web.webPageDetails.viewName": "home", - }), - ).toEqual([ - { - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - eventToken: "abc", - }, - strategies: [ - { - strategyID: "3VQe3oIqiYq2RAsYzmDTSf", - treatmentID: "yu7rkogezumca7i0i44v", - }, - ], - activity: { - id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", - }, - correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", - }, - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - scope: "web://mywebsite.com", - }, - { - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - eventToken: "abc", - }, - strategies: [ - { - strategyID: "3VQe3oIqiYq2RAsYzmDTSf", - treatmentID: "yu7rkogezumca7i0i44v", - }, - ], - activity: { - id: "48ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d168", - }, - correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", - }, - id: "3d5d69cd-acde-4eca-b43b-a54574b67bb0", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "div#spa #spa-content h3", - type: "setHtml", - content: "i can haz?", - prehidingSelector: "div#spa #spa-content h3", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "8a0d7a45-70fb-4845-a093-2133b5744c8d", - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "div#spa #spa-content p", - type: "setHtml", - content: "ALL YOUR BASE ARE BELONG TO US", - prehidingSelector: "div#spa #spa-content p", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "a44af51a-e073-4e8c-92e1-84ac28210043", - }, - ], - scope: "web://mywebsite.com", - }, - ]); - }); - - it("ignores payloads that aren't json-ruleset type", () => { - decisionProvider.addPayload({ - id: "AT:eyJhY3Rpdml0eUlkIjoiMTQxMDY0IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", - scope: "__view__", - scopeDetails: { - decisionProvider: "TGT", - activity: { - id: "141064", - }, - experience: { - id: "0", - }, - strategies: [ - { - algorithmID: "0", - trafficType: "0", - }, - ], - characteristics: { - eventToken: "abc", - }, - correlationID: "141064:0:0:0", - }, - items: [ - { - id: "284525", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "setHtml", - format: "application/vnd.adobe.target.dom-action", - content: "
    oh hai
    ", - selector: "head", - prehidingSelector: "head", - }, - }, - ], - }); - - expect(decisionProvider.evaluate()).toEqual([]); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/createEvaluableRulesetPayload.spec.js b/test/unit/specs/components/RulesEngine/createEvaluableRulesetPayload.spec.js deleted file mode 100644 index 05de49c2a..000000000 --- a/test/unit/specs/components/RulesEngine/createEvaluableRulesetPayload.spec.js +++ /dev/null @@ -1,354 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createEvaluableRulesetPayload from "../../../../../src/components/RulesEngine/createEvaluableRulesetPayload.js"; -import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; -import createDecisionHistory from "../../../../../src/components/RulesEngine/createDecisionHistory.js"; - -describe("RulesEngine:createEvaluableRulesetPayload", () => { - let storage; - let eventRegistry; - let decisionHistory; - - beforeEach(() => { - storage = jasmine.createSpyObj("storage", ["getItem", "setItem", "clear"]); - eventRegistry = createEventRegistry({ storage }); - decisionHistory = createDecisionHistory({ eventRegistry }); - }); - - it("consumes ruleset-items", () => { - const evaluableRulesetPayload = createEvaluableRulesetPayload( - { - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - eventToken: "abc", - }, - strategies: [ - { - strategyID: "3VQe3oIqiYq2RAsYzmDTSf", - treatmentID: "yu7rkogezumca7i0i44v", - }, - ], - activity: { - id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", - }, - correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", - }, - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - items: [ - { - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - schema: "https://ns.adobe.com/personalization/ruleset-item", - data: { - version: 1, - rules: [ - { - condition: { - definition: { - conditions: [ - { - definition: { - conditions: [ - { - definition: { - key: "color", - matcher: "eq", - values: ["orange", "blue"], - }, - type: "matcher", - }, - { - definition: { - key: "action", - matcher: "eq", - values: ["lipstick"], - }, - type: "matcher", - }, - ], - logic: "and", - }, - type: "group", - }, - ], - logic: "and", - }, - type: "group", - }, - consequences: [ - { - type: "schema", - detail: { - schema: - "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - type: "schema", - detail: { - schema: - "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - }, - ], - }, - }, - ], - scope: "web://mywebsite.com", - }, - eventRegistry, - decisionHistory, - ); - - expect( - evaluableRulesetPayload.evaluate({ color: "orange", action: "lipstick" }), - ).toEqual({ - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - eventToken: "abc", - }, - strategies: [ - { - strategyID: "3VQe3oIqiYq2RAsYzmDTSf", - treatmentID: "yu7rkogezumca7i0i44v", - }, - ], - activity: { - id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", - }, - correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", - }, - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - scope: "web://mywebsite.com", - }); - }); - - it("consumes json-content-items", () => { - const evaluableRulesetPayload = createEvaluableRulesetPayload( - { - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - eventToken: "abc", - }, - strategies: [ - { - strategyID: "3VQe3oIqiYq2RAsYzmDTSf", - treatmentID: "yu7rkogezumca7i0i44v", - }, - ], - activity: { - id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", - }, - correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", - }, - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - items: [ - { - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - schema: "https://ns.adobe.com/personalization/json-content-item", - data: { - content: { - version: 1, - rules: [ - { - condition: { - definition: { - conditions: [ - { - definition: { - conditions: [ - { - definition: { - key: "color", - matcher: "eq", - values: ["orange", "blue"], - }, - type: "matcher", - }, - { - definition: { - key: "action", - matcher: "eq", - values: ["lipstick"], - }, - type: "matcher", - }, - ], - logic: "and", - }, - type: "group", - }, - ], - logic: "and", - }, - type: "group", - }, - consequences: [ - { - type: "schema", - detail: { - schema: - "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - type: "schema", - detail: { - schema: - "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - }, - ], - }, - }, - }, - ], - scope: "web://mywebsite.com", - }, - eventRegistry, - decisionHistory, - ); - - expect( - evaluableRulesetPayload.evaluate({ color: "orange", action: "lipstick" }), - ).toEqual({ - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - eventToken: "abc", - }, - strategies: [ - { - strategyID: "3VQe3oIqiYq2RAsYzmDTSf", - treatmentID: "yu7rkogezumca7i0i44v", - }, - ], - activity: { - id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", - }, - correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", - }, - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - scope: "web://mywebsite.com", - }); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/createEvaluateRulesetsCommand.spec.js b/test/unit/specs/components/RulesEngine/createEvaluateRulesetsCommand.spec.js deleted file mode 100644 index eb0aad99b..000000000 --- a/test/unit/specs/components/RulesEngine/createEvaluateRulesetsCommand.spec.js +++ /dev/null @@ -1,216 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createEvaluateRulesetsCommand from "../../../../../src/components/RulesEngine/createEvaluateRulesetsCommand.js"; -import createContextProvider from "../../../../../src/components/RulesEngine/createContextProvider.js"; -import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; -import createDecisionProvider from "../../../../../src/components/RulesEngine/createDecisionProvider.js"; -import createApplyResponse from "../../../../../src/components/RulesEngine/createApplyResponse.js"; -import injectGetBrowser from "../../../../../src/utils/injectGetBrowser.js"; - -describe("RulesEngine:evaluateRulesetsCommand", () => { - let onDecision; - let applyResponse; - let storage; - let eventRegistry; - let getBrowser; - let contextProvider; - let decisionProvider; - let evaluateRulesetsCommand; - - beforeEach(() => { - onDecision = jasmine.createSpy(); - applyResponse = createApplyResponse({ onDecision }); - - storage = jasmine.createSpyObj("storage", ["getItem", "setItem", "clear"]); - eventRegistry = createEventRegistry({ storage }); - getBrowser = injectGetBrowser({ userAgent: window.navigator.userAgent }); - contextProvider = createContextProvider({ - eventRegistry, - window, - getBrowser, - }); - decisionProvider = createDecisionProvider({ eventRegistry }); - - decisionProvider.addPayload({ - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scopeDetails: { - activity: { - id: "abc#xyz", - }, - }, - items: [ - { - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - schema: "https://ns.adobe.com/personalization/ruleset-item", - data: { - version: 1, - rules: [ - { - condition: { - definition: { - conditions: [ - { - definition: { - conditions: [ - { - definition: { - key: "color", - matcher: "eq", - values: ["orange", "blue"], - }, - type: "matcher", - }, - { - definition: { - key: "action", - matcher: "eq", - values: ["greet"], - }, - type: "matcher", - }, - ], - logic: "and", - }, - type: "group", - }, - ], - logic: "and", - }, - type: "group", - }, - consequences: [ - { - type: "schema", - detail: { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - ], - }, - ], - }, - }, - ], - scope: "web://mywebsite.com", - }); - - evaluateRulesetsCommand = createEvaluateRulesetsCommand({ - contextProvider, - decisionProvider, - }); - }); - - it("onDecisions receives renderDecisions=true", () => { - const result = evaluateRulesetsCommand.run({ - renderDecisions: true, - decisionContext: { color: "orange", action: "greet" }, - applyResponse, - }); - - const expectedResult = { - propositions: [ - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scopeDetails: { - activity: { - id: "abc#xyz", - }, - }, - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - ], - scope: "web://mywebsite.com", - }, - ], - }; - - expect(result).toEqual(expectedResult); - expect(onDecision).toHaveBeenCalledOnceWith({ - renderDecisions: true, - event: undefined, - personalization: undefined, - ...expectedResult, - }); - }); - - it("onDecisions receives renderDecisions=false", () => { - const result = evaluateRulesetsCommand.run({ - decisionContext: { color: "orange", action: "greet" }, - applyResponse, - }); - - const expectedResult = { - propositions: [ - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scopeDetails: { - activity: { - id: "abc#xyz", - }, - }, - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - ], - scope: "web://mywebsite.com", - }, - ], - }; - - expect(result).toEqual(expectedResult); - expect(onDecision).toHaveBeenCalledOnceWith({ - renderDecisions: false, - event: undefined, - personalization: undefined, - ...expectedResult, - }); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/createEventRegistry.spec.js b/test/unit/specs/components/RulesEngine/createEventRegistry.spec.js deleted file mode 100644 index beb8a6e7a..000000000 --- a/test/unit/specs/components/RulesEngine/createEventRegistry.spec.js +++ /dev/null @@ -1,418 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createEventRegistry, { - createEventPruner, -} from "../../../../../src/components/RulesEngine/createEventRegistry.js"; - -describe("RulesEngine:createEventRegistry", () => { - let storage; - let mockedTimestamp; - beforeEach(() => { - storage = jasmine.createSpyObj("storage", ["getItem", "setItem", "clear"]); - mockedTimestamp = new Date("2023-05-24T08:00:00Z"); - jasmine.clock().install(); - jasmine.clock().mockDate(mockedTimestamp); - }); - - afterEach(() => { - jasmine.clock().uninstall(); - }); - - it("registers events", () => { - const eventRegistry = createEventRegistry({ storage }); - - const getContent = () => ({ - xdm: { - eventType: "decisioning.propositionDisplay", - _experience: { - decisioning: { - propositions: [ - { - id: "111", - scope: "mobileapp://com.adobe.aguaAppIos", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "ccaa539e-ca14-4d42-ac9a-0a17e69a63e4", - activity: { - id: "111#aaa", - }, - }, - }, - { - id: "222", - scope: "mobileapp://com.adobe.aguaAppIos", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "ccaa539e-ca14-4d42-ac9a-0a17e69a63e4", - activity: { - id: "222#bbb", - }, - }, - }, - { - id: "333", - scope: "web://something", - scopeDetails: { - decisionProvider: "TGT", - correlationID: "aasfsdf", - activity: { - id: "333#ccc", - }, - }, - }, - ], - propositionEventType: { - display: 1, - }, - }, - }, - }, - }); - - const event = { - getContent, - }; - - eventRegistry.addExperienceEdgeEvent(event); - expect(eventRegistry.toJSON()).toEqual({ - display: { - "111#aaa": { - event: jasmine.objectContaining({ - "iam.id": "111#aaa", - "iam.eventType": "display", - }), - firstTimestamp: jasmine.any(Number), - timestamp: jasmine.any(Number), - count: 1, - }, - "222#bbb": { - event: jasmine.objectContaining({ - "iam.id": "222#bbb", - "iam.eventType": "display", - }), - firstTimestamp: jasmine.any(Number), - timestamp: jasmine.any(Number), - count: 1, - }, - }, - }); - }); - - it("does not register invalid events", () => { - const eventRegistry = createEventRegistry({ storage }); - - eventRegistry.addExperienceEdgeEvent({ - getContent: () => ({ - xdm: { - eventType: "display", - }, - }), - }); - eventRegistry.addExperienceEdgeEvent({ - getContent: () => ({ - xdm: { - eventType: "display", - _experience: {}, - }, - }), - }); - eventRegistry.addExperienceEdgeEvent({ - getContent: () => ({ - xdm: { - eventType: "display", - _experience: { - decisioning: {}, - }, - }, - }), - }); - eventRegistry.addExperienceEdgeEvent({ - getContent: () => ({}), - }); - - expect(eventRegistry.toJSON()).toEqual({}); - }); - - it("does not register events without type and id", () => { - const eventRegistry = createEventRegistry({ storage }); - - expect(eventRegistry.addEvent({}, "trigger")).toBeUndefined(); - expect(eventRegistry.addEvent({}, "trigger", undefined)).toBeUndefined(); - expect(eventRegistry.addEvent({})).toBeUndefined(); - - expect(eventRegistry.toJSON()).toEqual({}); - - expect( - eventRegistry.addEvent({ something: "special" }, "display", "abc#123"), - ).toEqual({ - event: { - "iam.id": "abc#123", - "iam.eventType": "display", - "iam.action": undefined, - something: "special", - }, - firstTimestamp: jasmine.any(Number), - timestamp: jasmine.any(Number), - count: 1, - }); - - expect(eventRegistry.toJSON()).toEqual({ - display: { - "abc#123": { - event: { - "iam.id": "abc#123", - "iam.eventType": "display", - "iam.action": undefined, - something: "special", - }, - firstTimestamp: jasmine.any(Number), - timestamp: jasmine.any(Number), - count: 1, - }, - }, - }); - }); - - it("increments count and sets timestamp", (done) => { - const eventRegistry = createEventRegistry({ storage }); - - const getContent = () => ({ - xdm: { - eventType: "decisioning.propositionDisplay", - _experience: { - decisioning: { - propositions: [ - { - id: "111", - scope: "mobileapp://com.adobe.aguaAppIos", - scopeDetails: { - decisionProvider: "AJO", - correlationID: "ccaa539e-ca14-4d42-ac9a-0a17e69a63e4", - activity: { - id: "111#aaa", - }, - }, - }, - ], - propositionEventType: { - display: 1, - }, - }, - }, - }, - }); - - const event = { - getContent, - }; - let lastEventTime = 0; - eventRegistry.addExperienceEdgeEvent(event); - expect(eventRegistry.getEvent("display", "111#aaa")).toEqual({ - event: jasmine.objectContaining({ - "iam.id": "111#aaa", - "iam.eventType": "display", - }), - firstTimestamp: jasmine.any(Number), - timestamp: jasmine.any(Number), - count: 1, - }); - expect( - eventRegistry.getEvent("display", "111#aaa").timestamp, - ).toBeGreaterThan(lastEventTime); - - lastEventTime = eventRegistry.getEvent("display", "111#aaa").timestamp; - - setTimeout(() => { - eventRegistry.addExperienceEdgeEvent(event); // again - - expect(eventRegistry.getEvent("display", "111#aaa")).toEqual({ - event: jasmine.objectContaining({ - "iam.id": "111#aaa", - "iam.eventType": "display", - }), - firstTimestamp: jasmine.any(Number), - timestamp: jasmine.any(Number), - count: 2, - }); - expect( - eventRegistry.getEvent("display", "111#aaa").timestamp, - ).toBeGreaterThan(lastEventTime); - done(); - }, 50); - - jasmine.clock().tick(60); - }); - - it("limits events to 1000 events", () => { - const prune = createEventPruner(); - const events = {}; - events["decisioning.propositionDisplay"] = {}; - events["decisioning.propositionInteract"] = {}; - - for (let i = 0; i < 2000; i += 1) { - events["decisioning.propositionDisplay"][i] = { - event: { - "iam.id": i, - "iam.eventType": "decisioning.propositionDisplay", - }, - firstTimestamp: "2023-05-23T08:00:00Z", - timestamp: mockedTimestamp, - count: 1, - }; - - events["decisioning.propositionInteract"][i] = { - event: { - "iam.id": i, - "iam.eventType": "decisioning.propositionInteract", - }, - firstTimestamp: "2023-05-23T08:00:00Z", - timestamp: mockedTimestamp, - count: 1, - }; - - const pruned = prune(events); - const interactEvents = Object.values( - pruned["decisioning.propositionInteract"], - ); - - const displayEvents = Object.values( - pruned["decisioning.propositionDisplay"], - ); - expect(interactEvents.length).not.toBeGreaterThan(1000); - expect(displayEvents.length).not.toBeGreaterThan(1000); - - if (i > 1000) { - expect(interactEvents[0].event["iam.id"]).toEqual(i - 999); - expect(displayEvents[0].event["iam.id"]).toEqual(i - 999); - } - - if (i > 0) { - expect( - interactEvents[0].timestamp < - interactEvents[interactEvents.length - 1].timestamp, - ); - expect( - displayEvents[0].timestamp < - displayEvents[interactEvents.length - 1].timestamp, - ); - } - } - }); - - it("has configurable limits", () => { - const prune = createEventPruner(10); - - const events = {}; - events["decisioning.propositionDisplay"] = {}; - - for (let i = 0; i < 20; i += 1) { - events["decisioning.propositionDisplay"][i] = { - event: { - "iam.id": i, - "iam.eventType": "decisioning.propositionDisplay", - }, - firstTimestamp: 1, - timestamp: 1, - count: 1, - }; - - const pruned = prune(events); - - const displayEvents = Object.values( - pruned["decisioning.propositionDisplay"], - ); - - expect(displayEvents.length).not.toBeGreaterThan(10); - } - }); - - it("should filter events based on expiration date", () => { - const pruner = createEventPruner(4, 2); - - const events = {}; - events["decisioning.propositionDisplay"] = { - 1: { - event: { - "iam.id": 1, - "iam.eventType": "decisioning.propositionInteract", - }, - firstTimestamp: "2023-05-20T10:00:00Z", - timestamp: mockedTimestamp, - count: 1, - }, - 2: { - event: { - "iam.id": 2, - "iam.eventType": "decisioning.propositionInteract", - }, - firstTimestamp: "2023-05-24T15:00:00Z", - timestamp: mockedTimestamp, - count: 1, - }, - }; - events["decisioning.propositionInteract"] = { - 3: { - event: { - "iam.id": 3, - "iam.eventType": "decisioning.propositionInteract", - }, - firstTimestamp: "2023-05-23T08:00:00Z", - timestamp: mockedTimestamp, - count: 1, - }, - 4: { - event: { - "iam.id": 4, - "iam.eventType": "decisioning.propositionInteract", - }, - firstTimestamp: "2023-05-23T08:00:00Z", - timestamp: mockedTimestamp, - count: 1, - }, - }; - - const prunedEvents = pruner(events); - expect(prunedEvents).toEqual({ - "decisioning.propositionDisplay": { - 2: { - event: { - "iam.id": 2, - "iam.eventType": "decisioning.propositionInteract", - }, - firstTimestamp: "2023-05-24T15:00:00Z", - timestamp: mockedTimestamp, - count: 1, - }, - }, - "decisioning.propositionInteract": { - 3: { - event: { - "iam.id": 3, - "iam.eventType": "decisioning.propositionInteract", - }, - firstTimestamp: "2023-05-23T08:00:00Z", - timestamp: mockedTimestamp, - count: 1, - }, - 4: { - event: { - "iam.id": 4, - "iam.eventType": "decisioning.propositionInteract", - }, - firstTimestamp: "2023-05-23T08:00:00Z", - timestamp: mockedTimestamp, - count: 1, - }, - }, - }); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/createOnResponseHandler.spec.js b/test/unit/specs/components/RulesEngine/createOnResponseHandler.spec.js deleted file mode 100644 index ba3075834..000000000 --- a/test/unit/specs/components/RulesEngine/createOnResponseHandler.spec.js +++ /dev/null @@ -1,394 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createOnResponseHandler from "../../../../../src/components/RulesEngine/createOnResponseHandler.js"; -import createDecisionProvider from "../../../../../src/components/RulesEngine/createDecisionProvider.js"; -import createApplyResponse from "../../../../../src/components/RulesEngine/createApplyResponse.js"; -import createEventRegistry from "../../../../../src/components/RulesEngine/createEventRegistry.js"; - -describe("RulesEngine:createOnResponseHandler", () => { - let lifecycle; - let storage; - let eventRegistry; - let decisionProvider; - let applyResponse; - - beforeEach(() => { - lifecycle = jasmine.createSpyObj("lifecycle", { - onDecision: Promise.resolve(), - }); - - storage = jasmine.createSpyObj("storage", ["getItem", "setItem", "clear"]); - eventRegistry = createEventRegistry({ storage }); - decisionProvider = createDecisionProvider({ eventRegistry }); - applyResponse = createApplyResponse(lifecycle); - }); - - it("calls lifecycle.onDecision with propositions based on decisionContext", () => { - const event = { - getViewName: () => undefined, - hasQuery: () => true, - getContent: () => ({ - query: {}, - xdm: { - web: { - webPageDetails: { - viewName: "contact", - URL: "https://mywebsite.com", - }, - webReferrer: { - URL: "https://google.com", - }, - }, - timestamp: new Date().toISOString(), - implementationDetails: { - name: "https://ns.adobe.com/experience/alloy", - version: "2.15.0", - environment: "browser", - }, - }, - data: { - moo: "woof", - }, - }), - }; - - const decisionContext = { - color: "orange", - action: "lipstick", - }; - const personalization = { surfaces: ["#woof"] }; - - const responseHandler = createOnResponseHandler({ - renderDecisions: true, - decisionProvider, - applyResponse, - event, - personalization, - decisionContext, - }); - - const response = { - getPayloadsByType: () => [ - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scopeDetails: { - activity: { - id: "abc#xyz", - }, - }, - items: [ - { - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - schema: "https://ns.adobe.com/personalization/ruleset-item", - data: { - version: 1, - rules: [ - { - condition: { - definition: { - conditions: [ - { - definition: { - conditions: [ - { - definition: { - key: "color", - matcher: "eq", - values: ["orange", "blue"], - }, - type: "matcher", - }, - { - definition: { - key: "action", - matcher: "eq", - values: ["lipstick"], - }, - type: "matcher", - }, - ], - logic: "and", - }, - type: "group", - }, - ], - logic: "and", - }, - type: "group", - }, - consequences: [ - { - type: "schema", - detail: { - schema: - "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - type: "schema", - detail: { - schema: - "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - }, - ], - }, - }, - ], - scope: "web://target.jasonwaters.dev/aep.html", - }, - ], - }; - - responseHandler({ - response, - }); - - expect(lifecycle.onDecision).toHaveBeenCalledWith({ - event, - personalization, - renderDecisions: true, - propositions: [ - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scopeDetails: { - activity: { - id: "abc#xyz", - }, - }, - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - scope: "web://target.jasonwaters.dev/aep.html", - }, - ], - }); - }); - - it("calls lifecycle.onDecision with propositions based on xdm and event data", () => { - const event = { - getViewName: () => "home", - hasQuery: () => true, - getContent: () => ({ - query: {}, - xdm: { - web: { - webPageDetails: { - viewName: "contact", - URL: "https://mywebsite.com", - }, - webReferrer: { - URL: "https://google.com", - }, - }, - timestamp: new Date().toISOString(), - implementationDetails: { - name: "https://ns.adobe.com/experience/alloy", - version: "12345", - environment: "browser", - }, - }, - data: { - moo: "woof", - }, - }), - }; - - const decisionContext = {}; - - const personalization = {}; - - const responseHandler = createOnResponseHandler({ - renderDecisions: true, - decisionProvider, - applyResponse, - event, - personalization, - decisionContext, - }); - - const response = { - getPayloadsByType: () => [ - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scopeDetails: { - activity: { - id: "abc#xyz", - }, - }, - items: [ - { - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - schema: "https://ns.adobe.com/personalization/ruleset-item", - data: { - version: 1, - rules: [ - { - condition: { - definition: { - conditions: [ - { - definition: { - conditions: [ - { - definition: { - key: "xdm.web.webPageDetails.viewName", - matcher: "eq", - values: ["contact"], - }, - type: "matcher", - }, - { - definition: { - key: "xdm.implementationDetails.version", - matcher: "eq", - values: ["12345"], - }, - type: "matcher", - }, - { - definition: { - key: "data.moo", - matcher: "eq", - values: ["woof"], - }, - type: "matcher", - }, - ], - logic: "and", - }, - type: "group", - }, - ], - logic: "and", - }, - type: "group", - }, - consequences: [ - { - type: "schema", - detail: { - schema: - "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - }, - ], - }, - }, - ], - scope: "web://target.jasonwaters.dev/aep.html", - }, - ], - }; - - responseHandler({ - response, - }); - - expect(lifecycle.onDecision).toHaveBeenCalledWith({ - renderDecisions: true, - propositions: [ - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scopeDetails: { - activity: { - id: "abc#xyz", - }, - }, - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - qualifiedDate: jasmine.any(Number), - displayedDate: undefined, - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - scope: "web://target.jasonwaters.dev/aep.html", - }, - ], - event, - personalization, - }); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/createSubscribeRulesetItems.spec.js b/test/unit/specs/components/RulesEngine/createSubscribeRulesetItems.spec.js deleted file mode 100644 index b14c4cb0f..000000000 --- a/test/unit/specs/components/RulesEngine/createSubscribeRulesetItems.spec.js +++ /dev/null @@ -1,1494 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - DOM_ACTION, - MESSAGE_CONTENT_CARD, -} from "../../../../../src/constants/schema.js"; -import createSubscribeRulesetItems from "../../../../../src/components/RulesEngine/createSubscribeRulesetItems.js"; -import { PropositionEventType } from "../../../../../src/constants/propositionEventType.js"; - -describe("RulesEngine:subscribeRulesetItems", () => { - let collect; - let subscribeRulesetItems; - - const PROPOSITIONS = [ - { - id: "abc", - items: [ - { - schema: DOM_ACTION, - data: { - selector: "a", - type: "setAttribute", - content: { - src: "img/test.png", - }, - prehidingSelector: "a", - qualifiedDate: 1694198274647, - displayedDate: 1694198274647, - }, - id: "aabbcc", - }, - ], - scope: "web://something.com", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - items: [ - { - schema: DOM_ACTION, - data: { - selector: "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - qualifiedDate: 1683042673380, - displayedDate: 1683042673395, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - schema: DOM_ACTION, - data: { - selector: "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - qualifiedDate: 1683042673387, - displayedDate: 1683042673395, - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", - items: [ - { - schema: MESSAGE_CONTENT_CARD, - data: { - expiryDate: 1712190456, - publishedDate: 1677752640000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/lumon.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "a handshake is available upon request.", - title: "Welcome to Lumon!", - }, - contentType: "application/json", - qualifiedDate: 1683042628060, - displayedDate: 1683042628070, - }, - id: "a48ca420-faea-467e-989a-5d179d9f562d", - }, - { - schema: MESSAGE_CONTENT_CARD, - data: { - expiryDate: 1712190456, - publishedDate: 1677839040000, - - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/achievement.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Great job, you completed your profile.", - title: "Achievement Unlocked!", - }, - contentType: "application/json", - qualifiedDate: 1683042628064, - displayedDate: 1683042628070, - }, - id: "b7173290-588f-40c6-a05c-43ed5ec08b28", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", - items: [ - { - schema: MESSAGE_CONTENT_CARD, - data: { - expiryDate: 1712190456, - publishedDate: 1678098240000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/twitter.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Posting on social media helps us spread the word.", - title: "Thanks for sharing!", - }, - contentType: "application/json", - qualifiedDate: 1683042658312, - displayedDate: 1683042658316, - }, - id: "cfcb1af7-7bc2-45b2-a86a-0aa93fe69ce7", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "d1f7d411-a549-47bc-a4d8-c8e638b0a46b", - items: [ - { - schema: MESSAGE_CONTENT_CARD, - data: { - expiryDate: 1712190456, - publishedDate: 1678184640000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/gold-coin.jpg", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Now you're ready to earn!", - title: "Funds deposited!", - }, - contentType: "application/json", - qualifiedDate: 1683042653905, - displayedDate: 1683042653909, - }, - id: "0263e171-fa32-4c7a-9611-36b28137a81d", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ]; - - beforeEach(() => { - collect = jasmine.createSpy().and.returnValue(Promise.resolve()); - subscribeRulesetItems = createSubscribeRulesetItems({ collect }); - }); - - it("has a command defined", () => { - const { command } = subscribeRulesetItems; - - expect(command).toEqual({ - optionsValidator: jasmine.any(Function), - run: jasmine.any(Function), - }); - }); - - it("calls the callback with list of content cards", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy("callback"); - - // register a subscription. equivalent to alloy("subscribeRulesetItems", ... - command.run({ - surfaces: ["web://mywebsite.com/my-cards"], - schemas: [MESSAGE_CONTENT_CARD], - callback, - }); - - refresh(PROPOSITIONS); - expect(callback).toHaveBeenCalledOnceWith( - { - propositions: [ - { - id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", - items: [ - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1677752640000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/lumon.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "a handshake is available upon request.", - title: "Welcome to Lumon!", - }, - contentType: "application/json", - qualifiedDate: 1683042628060, - displayedDate: 1683042628070, - }, - id: "a48ca420-faea-467e-989a-5d179d9f562d", - }, - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1677839040000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/achievement.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Great job, you completed your profile.", - title: "Achievement Unlocked!", - }, - contentType: "application/json", - qualifiedDate: 1683042628064, - displayedDate: 1683042628070, - }, - id: "b7173290-588f-40c6-a05c-43ed5ec08b28", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", - items: [ - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1678098240000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/twitter.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Posting on social media helps us spread the word.", - title: "Thanks for sharing!", - }, - contentType: "application/json", - qualifiedDate: 1683042658312, - displayedDate: 1683042658316, - }, - id: "cfcb1af7-7bc2-45b2-a86a-0aa93fe69ce7", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "d1f7d411-a549-47bc-a4d8-c8e638b0a46b", - items: [ - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1678184640000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/gold-coin.jpg", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Now you're ready to earn!", - title: "Funds deposited!", - }, - contentType: "application/json", - qualifiedDate: 1683042653905, - displayedDate: 1683042653909, - }, - id: "0263e171-fa32-4c7a-9611-36b28137a81d", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - }, - jasmine.any(Function), - ); - }); - - it("calls the callback with list of content cards at time of subscription (when there are existing propositions)", () => { - const { command, refresh } = subscribeRulesetItems; - refresh(PROPOSITIONS); - - const callback = jasmine.createSpy("callback"); - - command.run({ - surfaces: ["web://mywebsite.com/my-cards"], - schemas: [MESSAGE_CONTENT_CARD], - callback, - }); - - expect(callback).toHaveBeenCalledOnceWith( - { - propositions: [ - { - id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", - items: [ - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1677752640000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/lumon.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "a handshake is available upon request.", - title: "Welcome to Lumon!", - }, - contentType: "application/json", - qualifiedDate: 1683042628060, - displayedDate: 1683042628070, - }, - id: "a48ca420-faea-467e-989a-5d179d9f562d", - }, - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1677839040000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/achievement.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Great job, you completed your profile.", - title: "Achievement Unlocked!", - }, - contentType: "application/json", - qualifiedDate: 1683042628064, - displayedDate: 1683042628070, - }, - id: "b7173290-588f-40c6-a05c-43ed5ec08b28", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", - items: [ - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1678098240000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/twitter.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Posting on social media helps us spread the word.", - title: "Thanks for sharing!", - }, - contentType: "application/json", - qualifiedDate: 1683042658312, - displayedDate: 1683042658316, - }, - id: "cfcb1af7-7bc2-45b2-a86a-0aa93fe69ce7", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "d1f7d411-a549-47bc-a4d8-c8e638b0a46b", - items: [ - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1678184640000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/gold-coin.jpg", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Now you're ready to earn!", - title: "Funds deposited!", - }, - contentType: "application/json", - qualifiedDate: 1683042653905, - displayedDate: 1683042653909, - }, - id: "0263e171-fa32-4c7a-9611-36b28137a81d", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - }, - jasmine.any(Function), - ); - }); - - it("calls the callback with list of dom action items", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - // register a subscription. equivalent to alloy("subscribeRulesetItems", ... - command.run({ - surfaces: ["web://mywebsite.com/my-cards"], - schemas: [DOM_ACTION], - callback, - }); - - refresh(PROPOSITIONS); - expect(callback).toHaveBeenCalledOnceWith( - { - propositions: [ - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - qualifiedDate: 1683042673380, - displayedDate: 1683042673395, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - qualifiedDate: 1683042673387, - displayedDate: 1683042673395, - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - }, - jasmine.any(Function), - ); - }); - - it("calls the callback with list of dom action items at time of subscription (when there are existing propositions)", () => { - const { command, refresh } = subscribeRulesetItems; - refresh(PROPOSITIONS); - - const callback = jasmine.createSpy("callback"); - - command.run({ - surfaces: ["web://mywebsite.com/my-cards"], - schemas: [DOM_ACTION], - callback, - }); - - expect(callback).toHaveBeenCalledOnceWith( - { - propositions: [ - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - qualifiedDate: 1683042673380, - displayedDate: 1683042673395, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - qualifiedDate: 1683042673387, - displayedDate: 1683042673395, - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - }, - jasmine.any(Function), - ); - }); - - it("calls the callback with list of all schema-based items for single schema", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - // register a subscription. equivalent to alloy("subscribeRulesetItems", ... - command.run({ - surfaces: ["web://mywebsite.com/my-cards"], - callback, - }); - - refresh(PROPOSITIONS); - expect(callback).toHaveBeenCalledOnceWith( - { - propositions: [ - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - qualifiedDate: 1683042673380, - displayedDate: 1683042673395, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - qualifiedDate: 1683042673387, - displayedDate: 1683042673395, - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", - items: [ - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1677752640000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/lumon.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "a handshake is available upon request.", - title: "Welcome to Lumon!", - }, - contentType: "application/json", - qualifiedDate: 1683042628060, - displayedDate: 1683042628070, - }, - id: "a48ca420-faea-467e-989a-5d179d9f562d", - }, - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1677839040000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/achievement.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Great job, you completed your profile.", - title: "Achievement Unlocked!", - }, - contentType: "application/json", - qualifiedDate: 1683042628064, - displayedDate: 1683042628070, - }, - id: "b7173290-588f-40c6-a05c-43ed5ec08b28", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", - items: [ - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1678098240000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/twitter.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Posting on social media helps us spread the word.", - title: "Thanks for sharing!", - }, - contentType: "application/json", - qualifiedDate: 1683042658312, - displayedDate: 1683042658316, - }, - id: "cfcb1af7-7bc2-45b2-a86a-0aa93fe69ce7", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "d1f7d411-a549-47bc-a4d8-c8e638b0a46b", - items: [ - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1678184640000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/gold-coin.jpg", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Now you're ready to earn!", - title: "Funds deposited!", - }, - contentType: "application/json", - qualifiedDate: 1683042653905, - displayedDate: 1683042653909, - }, - id: "0263e171-fa32-4c7a-9611-36b28137a81d", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - }, - jasmine.any(Function), - ); - }); - - it("filters out all surfaces", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy("callback"); - - // register a subscription. equivalent to alloy("subscribeRulesetItems", ... - command.run({ - surfaces: [], - schemas: [MESSAGE_CONTENT_CARD], - callback, - }); - - refresh(PROPOSITIONS); - expect(callback).toHaveBeenCalledOnceWith( - { propositions: [] }, - jasmine.any(Function), - ); - }); - - it("filters on surface", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - // register a subscription. equivalent to alloy("subscribeRulesetItems", ... - command.run({ - surfaces: ["web://something.com"], - callback, - }); - - refresh(PROPOSITIONS); - expect(callback).toHaveBeenCalledOnceWith( - { - propositions: [ - { - id: "abc", - items: [ - jasmine.objectContaining({ - schema: DOM_ACTION, - data: { - selector: "a", - type: "setAttribute", - content: { - src: "img/test.png", - }, - prehidingSelector: "a", - qualifiedDate: 1694198274647, - displayedDate: 1694198274647, - }, - id: "aabbcc", - }), - ], - scope: "web://something.com", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - }, - jasmine.any(Function), - ); - }); - - it("returns all surfaces and schemas", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - // register a subscription. equivalent to alloy("subscribeRulesetItems", ... - command.run({ - callback, - }); - - refresh(PROPOSITIONS); - expect(callback).toHaveBeenCalledOnceWith( - { - propositions: [ - { - id: "abc", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: "a", - type: "setAttribute", - content: { - src: "img/test.png", - }, - prehidingSelector: "a", - qualifiedDate: 1694198274647, - displayedDate: 1694198274647, - }, - id: "aabbcc", - }, - ], - scope: "web://something.com", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - items: [ - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - qualifiedDate: 1683042673380, - displayedDate: 1683042673395, - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - type: "setHtml", - content: "Hello Treatment A!", - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(1) > H1:nth-of-type(1)", - qualifiedDate: 1683042673387, - displayedDate: 1683042673395, - }, - id: "10da709c-aa1a-40e5-84dd-966e2e8a1d5f", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", - items: [ - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1677752640000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/lumon.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "a handshake is available upon request.", - title: "Welcome to Lumon!", - }, - contentType: "application/json", - qualifiedDate: 1683042628060, - displayedDate: 1683042628070, - }, - id: "a48ca420-faea-467e-989a-5d179d9f562d", - }, - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1677839040000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/achievement.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Great job, you completed your profile.", - title: "Achievement Unlocked!", - }, - contentType: "application/json", - qualifiedDate: 1683042628064, - displayedDate: 1683042628070, - }, - id: "b7173290-588f-40c6-a05c-43ed5ec08b28", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "1ae11bc5-96dc-41c7-8f71-157c57a5290e", - items: [ - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1678098240000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/twitter.png", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Posting on social media helps us spread the word.", - title: "Thanks for sharing!", - }, - contentType: "application/json", - qualifiedDate: 1683042658312, - displayedDate: 1683042658316, - }, - id: "cfcb1af7-7bc2-45b2-a86a-0aa93fe69ce7", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - { - id: "d1f7d411-a549-47bc-a4d8-c8e638b0a46b", - items: [ - { - schema: - "https://ns.adobe.com/personalization/message/content-card", - data: { - expiryDate: 1712190456, - publishedDate: 1678184640000, - meta: { - surface: "web://mywebsite.com/my-cards", - }, - content: { - imageUrl: "img/gold-coin.jpg", - actionTitle: "Shop the sale!", - actionUrl: "https://luma.com/sale", - body: "Now you're ready to earn!", - title: "Funds deposited!", - }, - contentType: "application/json", - qualifiedDate: 1683042653905, - displayedDate: 1683042653909, - }, - id: "0263e171-fa32-4c7a-9611-36b28137a81d", - }, - ], - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - }, - jasmine.any(Function), - ); - }); - - it("does not invoke callback if unsubscribed", async () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy("callback"); - - // register a subscription. equivalent to alloy("subscribeRulesetItems", ... - const { unsubscribe } = await command.run({ - callback, - }); - - expect(unsubscribe instanceof Function).toBeTrue(); - unsubscribe(); - - refresh(PROPOSITIONS); - expect(callback).not.toHaveBeenCalled(); - }); - - it("collects interact events", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - command.run({ surface: "web://mywebsite.com/my-cards", callback }); - - refresh(PROPOSITIONS); - - const [{ propositions = [] }, collectEvent] = callback.calls.first().args; - - collectEvent(PropositionEventType.INTERACT, [propositions[0]]); - - expect(collect).toHaveBeenCalledWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - ], - eventType: "decisioning.propositionInteract", - documentMayUnload: true, - }); - }); - - it("collects only one interact event per proposition", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - command.run({ surface: "web://mywebsite.com/my-cards", callback }); - - refresh(PROPOSITIONS); - - const [{ propositions = [] }, collectEvent] = callback.calls.first().args; - - collectEvent(PropositionEventType.INTERACT, [ - propositions[0], - propositions[0], - propositions[0], - ]); - - expect(collect).toHaveBeenCalledWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - ], - eventType: "decisioning.propositionInteract", - documentMayUnload: true, - }); - }); - - it("collects separate interact events for each distinct proposition", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - command.run({ surface: "web://mywebsite.com/my-cards", callback }); - - refresh(PROPOSITIONS); - - const [{ propositions = [] }, collectEvent] = callback.calls.first().args; - - collectEvent(PropositionEventType.INTERACT, [propositions[0]]); - - expect(collect).toHaveBeenCalledWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - ], - eventType: "decisioning.propositionInteract", - documentMayUnload: true, - }); - - collectEvent(PropositionEventType.INTERACT, [propositions[0]]); - - expect(collect).toHaveBeenCalledWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - ], - eventType: "decisioning.propositionInteract", - documentMayUnload: true, - }); - - expect(collect).toHaveBeenCalledTimes(2); - }); - - it("collects multiple interact events for distinct propositions", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - command.run({ surface: "web://mywebsite.com/my-cards", callback }); - - refresh(PROPOSITIONS); - - const [{ propositions = [] }, collectEvent] = callback.calls.first().args; - - collectEvent(PropositionEventType.INTERACT, [ - propositions[0], - propositions[1], - ]); - - expect(collect).toHaveBeenCalledOnceWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - eventType: "decisioning.propositionInteract", - documentMayUnload: true, - }); - }); - - it("collects display events", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - command.run({ surface: "web://mywebsite.com/my-cards", callback }); - - refresh(PROPOSITIONS); - - const [{ propositions = [] }, collectEvent] = callback.calls.first().args; - - collectEvent(PropositionEventType.DISPLAY, [propositions[0]]); - - expect(collect).toHaveBeenCalledWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - ], - eventType: "decisioning.propositionDisplay", - documentMayUnload: true, - }); - }); - - it("collects only one display event per proposition", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - command.run({ surface: "web://mywebsite.com/my-cards", callback }); - - refresh(PROPOSITIONS); - - const [{ propositions = [] }, collectEvent] = callback.calls.first().args; - - collectEvent(PropositionEventType.DISPLAY, [propositions[0]]); - collectEvent(PropositionEventType.DISPLAY, [ - propositions[0], - propositions[0], - ]); - - expect(collect).toHaveBeenCalledOnceWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - ], - eventType: "decisioning.propositionDisplay", - documentMayUnload: true, - }); - }); - - it("collects multiple display events for distinct propositions", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - command.run({ surface: "web://mywebsite.com/my-cards", callback }); - - refresh(PROPOSITIONS); - - const [{ propositions = [] }, collectEvent] = callback.calls.first().args; - - collectEvent(PropositionEventType.DISPLAY, [ - propositions[0], - propositions[1], - ]); - - expect(collect).toHaveBeenCalledOnceWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - eventType: "decisioning.propositionDisplay", - documentMayUnload: true, - }); - }); - - it("collects display events only once per session", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - command.run({ surface: "web://mywebsite.com/my-cards", callback }); - - refresh(PROPOSITIONS); - - const [{ propositions = [] }, collectEvent] = callback.calls.first().args; - - collectEvent(PropositionEventType.DISPLAY, [ - propositions[0], - propositions[1], - ]); - - collectEvent(PropositionEventType.DISPLAY, [ - propositions[0], - propositions[1], - ]); - - collectEvent(PropositionEventType.DISPLAY, [propositions[2]]); - - expect(collect).toHaveBeenCalledTimes(2); - - expect(collect).toHaveBeenCalledWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - eventType: "decisioning.propositionDisplay", - documentMayUnload: true, - }); - - expect(collect).toHaveBeenCalledWith({ - decisionsMeta: [ - { - id: "1a3d874f-39ee-4310-bfa9-6559a10041a4", - scope: "web://mywebsite.com/my-cards", - scopeDetails: { decisionProvider: "AJO" }, - }, - ], - eventType: "decisioning.propositionDisplay", - documentMayUnload: true, - }); - }); - - it("collects dismiss events", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - command.run({ surface: "web://mywebsite.com/my-cards", callback }); - - refresh(PROPOSITIONS); - - const [{ propositions = [] }, collectEvent] = callback.calls.first().args; - - collectEvent(PropositionEventType.DISMISS, [propositions[0]]); - - expect(collect).toHaveBeenCalledWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - ], - documentMayUnload: true, - eventType: "decisioning.propositionDismiss", - }); - }); - - it("collects only one dismiss event per proposition", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - command.run({ surface: "web://mywebsite.com/my-cards", callback }); - - refresh(PROPOSITIONS); - - const [{ propositions = [] }, collectEvent] = callback.calls.first().args; - - collectEvent(PropositionEventType.DISMISS, [ - propositions[0], - propositions[0], - propositions[0], - ]); - - expect(collect).toHaveBeenCalledOnceWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - ], - eventType: "decisioning.propositionDismiss", - documentMayUnload: true, - }); - }); - - it("collects separate dismiss events for each distinct proposition", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - command.run({ surface: "web://mywebsite.com/my-cards", callback }); - - refresh(PROPOSITIONS); - - const [{ propositions = [] }, collectEvent] = callback.calls.first().args; - - collectEvent(PropositionEventType.DISMISS, [propositions[0]]); - - expect(collect).toHaveBeenCalledWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - ], - eventType: "decisioning.propositionDismiss", - documentMayUnload: true, - }); - - collectEvent(PropositionEventType.DISMISS, [propositions[0]]); - - expect(collect).toHaveBeenCalledWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - ], - eventType: "decisioning.propositionDismiss", - documentMayUnload: true, - }); - - expect(collect).toHaveBeenCalledTimes(2); - }); - - it("collects multiple dismiss events for distinct propositions", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy(); - - command.run({ surface: "web://mywebsite.com/my-cards", callback }); - - refresh(PROPOSITIONS); - - const [{ propositions = [] }, collectEvent] = callback.calls.first().args; - - collectEvent(PropositionEventType.DISMISS, [ - propositions[0], - propositions[1], - ]); - - expect(collect).toHaveBeenCalledOnceWith({ - decisionsMeta: [ - { - id: "abc", - scope: "web://something.com", - scopeDetails: { decisionProvider: "AJO" }, - }, - { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scope: "web://mywebsite.com/my-cards", - scopeDetails: { - decisionProvider: "AJO", - }, - }, - ], - eventType: "decisioning.propositionDismiss", - documentMayUnload: true, - }); - }); - - it("does not call collect event when there are no propositions", () => { - const { command, refresh } = subscribeRulesetItems; - - const callback = jasmine.createSpy("callback"); - - command.run({ - surfaces: ["web://mywebsite.com/my-cards"], - schemas: [MESSAGE_CONTENT_CARD], - callback, - }); - - refresh([]); - - const [{ propositions = [] }, collectEvent] = callback.calls.first().args; - - collectEvent(PropositionEventType.DISPLAY, propositions); - - expect(collect).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/decisioningContext.browser.spec.js b/test/unit/specs/components/RulesEngine/decisioningContext.browser.spec.js deleted file mode 100644 index b87da1c8d..000000000 --- a/test/unit/specs/components/RulesEngine/decisioningContext.browser.spec.js +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - mockWindow, - setupResponseHandler, - proposition, -} from "./contextTestUtils.js"; - -describe("RulesEngine:globalContext:browser", () => { - let applyResponse; - beforeEach(() => { - applyResponse = jasmine.createSpy(); - }); - it("satisfies rule based on matched browser", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "browser.name", - matcher: "eq", - values: ["chrome"], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched browser", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "browser.name", - matcher: "co", - values: ["Edge"], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/decisioningContext.page.spec.js b/test/unit/specs/components/RulesEngine/decisioningContext.page.spec.js deleted file mode 100644 index 7bc89133d..000000000 --- a/test/unit/specs/components/RulesEngine/decisioningContext.page.spec.js +++ /dev/null @@ -1,349 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - mockWindow, - setupResponseHandler, - proposition, -} from "./contextTestUtils.js"; - -describe("RulesEngine:globalContext:page", () => { - let applyResponse; - beforeEach(() => { - applyResponse = jasmine.createSpy(); - }); - - it("satisfies rule based on matched page url", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", - }), - { - definition: { - key: "page.url", - matcher: "eq", - values: [ - "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", - ], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched page url", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", - }), - { - definition: { - key: "page.url", - matcher: "eq", - values: [ - "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", - ], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfy rule based on matched domain", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", - }), - { - definition: { - key: "page.domain", - matcher: "eq", - values: ["pro.mywebsite.org"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched domain", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", - }), - { - definition: { - key: "page.domain", - matcher: "eq", - values: ["pro.mywebsite.com"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfied rule based on matched page subdomain", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", - }), - { - definition: { - key: "page.subdomain", - matcher: "eq", - values: ["pro"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - // Note that we have custom parse url [refer to implementation] which will give empty string in case of www - it("does not satisfy rule due to unmatched page subdomain", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://www.mywebsite.org:8080/about?m=1&t=5&name=bob#home", - }), - { - definition: { - key: "page.subdomain", - matcher: "eq", - values: ["www"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched page topLevelDomain", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", - }), - { - definition: { - key: "page.topLevelDomain", - matcher: "eq", - values: ["org"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched page topLevelDomain", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", - }), - { - definition: { - key: "page.topLevelDomain", - matcher: "eq", - values: ["com"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched page path", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", - }), - { - definition: { - key: "page.path", - matcher: "eq", - values: ["/about"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched page path", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", - }), - { - definition: { - key: "page.path", - matcher: "eq", - values: ["/home"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched page query", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", - }), - { - definition: { - key: "page.query", - matcher: "co", - values: ["name=bob"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched page query", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", - }), - { - definition: { - key: "page.query", - matcher: "co", - values: ["name=bob"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched page fragment", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#home", - }), - { - definition: { - key: "page.fragment", - matcher: "eq", - values: ["home"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched page fragment", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - url: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#about", - }), - { - definition: { - key: "page.fragment", - matcher: "eq", - values: ["home"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/decisioningContext.referringPage.spec.js b/test/unit/specs/components/RulesEngine/decisioningContext.referringPage.spec.js deleted file mode 100644 index da0df74df..000000000 --- a/test/unit/specs/components/RulesEngine/decisioningContext.referringPage.spec.js +++ /dev/null @@ -1,288 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - mockWindow, - setupResponseHandler, - proposition, -} from "./contextTestUtils.js"; - -describe("RulesEngine:globalContext:referringPage", () => { - let applyResponse; - beforeEach(() => { - applyResponse = jasmine.createSpy(); - }); - - it("satisfies rule based on matched domain", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - referrer: - "https://business.adobe.com/search?q=adobe+journey+optimizer&oq=adobe+journey+optimizer#home", - }), - { - definition: { - key: "referringPage.domain", - matcher: "eq", - values: ["business.adobe.com"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched domain", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - referrer: - "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", - }), - { - definition: { - key: "referringPage.domain", - matcher: "co", - values: ["business.adobe.com"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched referringPage subdomain", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - referrer: - "https://business.adobe.com/search?q=adobe+journey+optimizer&oq=adobe+journey+optimizer#home", - }), - { - definition: { - key: "referringPage.subdomain", - matcher: "co", - values: ["business"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched subdomain", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - referrer: - "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", - }), - { - definition: { - key: "referringPage.subdomain", - matcher: "eq", - values: ["business"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched referringPage topLevelDomain", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "referringPage.topLevelDomain", - matcher: "eq", - values: ["com"], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched topLevelDomain", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - referrer: - "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", - }), - { - definition: { - key: "referringPage.topLevelDomain", - matcher: "eq", - values: ["com"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched referringPage path", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "referringPage.path", - matcher: "co", - values: ["/search"], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched referringPage path", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - referrer: - "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", - }), - { - definition: { - key: "referringPage.path", - matcher: "co", - values: ["/search"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched referringPage query", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "referringPage.query", - matcher: "co", - values: ["q=adobe+journey+optimizer&oq=adobe+journey+optimizer"], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched referringPage query", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - referrer: - "https://pro.mywebsite.org:8080/about?m=1&t=5&name=richard#home", - }), - { - definition: { - key: "referringPage.query", - matcher: "co", - values: ["q=adobe+journey+optimizer&oq=adobe+journey+optimizer"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched referringPage fragment", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - referrer: - "https://business.adobe.com/search?q=adobe+journey+optimizer&oq=adobe+journey+optimizer#home", - }), - { - definition: { - key: "referringPage.fragment", - matcher: "co", - values: ["home"], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule based on unmatched referringPage fragment", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - referrer: "https://pro.mywebsite.org:8080/about?m=1&t=5&name=bob#about", - }), - { - definition: { - key: "referringPage.fragment", - matcher: "co", - values: ["home"], - }, - type: "matcher", - }, - ); - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/decisioningContext.sdkVersion.spec.js b/test/unit/specs/components/RulesEngine/decisioningContext.sdkVersion.spec.js deleted file mode 100644 index b4c8ddc26..000000000 --- a/test/unit/specs/components/RulesEngine/decisioningContext.sdkVersion.spec.js +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import libraryVersion from "../../../../../src/constants/libraryVersion.js"; -import { - mockWindow, - setupResponseHandler, - proposition, -} from "./contextTestUtils.js"; - -describe("RulesEngine:globalContext:sdkVersion", () => { - let applyResponse; - const currentVersion = libraryVersion; - beforeEach(() => { - applyResponse = jasmine.createSpy(); - }); - - it("satisfies rule based on matched alloy sdk version", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "~sdkver", - matcher: "eq", - values: [currentVersion], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched dk version", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "~sdkver", - matcher: "eq", - values: ["2.18.0-beta.0"], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/decisioningContext.timestamp.spec.js b/test/unit/specs/components/RulesEngine/decisioningContext.timestamp.spec.js deleted file mode 100644 index 5fa4a6017..000000000 --- a/test/unit/specs/components/RulesEngine/decisioningContext.timestamp.spec.js +++ /dev/null @@ -1,367 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - mockWindow, - setupResponseHandler, - proposition, -} from "./contextTestUtils.js"; - -let mockedTimestamp; -describe("RulesEngine:globalContext:timeContext", () => { - let applyResponse; - beforeEach(() => { - applyResponse = jasmine.createSpy(); - mockedTimestamp = new Date(Date.UTC(2023, 4, 11, 13, 34, 56)); - jasmine.clock().install(); - jasmine.clock().mockDate(mockedTimestamp); - }); - afterEach(() => { - jasmine.clock().uninstall(); - }); - - it("satisfies rule based on matched pageLoadTimestamp", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "pageLoadTimestamp", - matcher: "eq", - values: [mockedTimestamp.getTime()], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched pageLoadTimestamp", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "pageLoadTimestamp", - matcher: "eq", - values: [mockedTimestamp.getTime() + 1], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched currentTimestamp", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "currentTimestamp", - matcher: "eq", - values: [mockedTimestamp.getTime()], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched currentTimestamp", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "currentTimestamp", - matcher: "eq", - values: [mockedTimestamp.getTime() + 1], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched currentDate", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "currentDate", - matcher: "eq", - values: [mockedTimestamp.getDate()], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched currentDate", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "currentDate", - matcher: "eq", - values: [mockedTimestamp.getDate() + 1], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched currentDay", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "~state.com.adobe.module.lifecycle/lifecyclecontextdata.dayofweek", - matcher: "eq", - values: [mockedTimestamp.getDay() + 1], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched currentDay", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "~state.com.adobe.module.lifecycle/lifecyclecontextdata.dayofweek", - matcher: "eq", - values: [mockedTimestamp.getDay()], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched currentHour", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "~state.com.adobe.module.lifecycle/lifecyclecontextdata.hourofday", - matcher: "eq", - values: [mockedTimestamp.getHours()], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched currentHour", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "~state.com.adobe.module.lifecycle/lifecyclecontextdata.hourofday", - matcher: "eq", - values: [mockedTimestamp.getHours() + 1], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched currentMinute", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "currentMinute", - matcher: "eq", - values: [mockedTimestamp.getMinutes()], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched currentMinute", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "currentMinute", - matcher: "eq", - values: [mockedTimestamp.getMinutes() + 1], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched currentMonth", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "currentMonth", - matcher: "eq", - values: [mockedTimestamp.getMonth()], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched currentMonth", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "currentMonth", - matcher: "eq", - values: [mockedTimestamp.getMonth() + 1], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched currentYear", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "currentYear", - matcher: "eq", - values: [mockedTimestamp.getFullYear()], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched currentYear", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "currentYear", - matcher: "eq", - values: [mockedTimestamp.getFullYear() + 1], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched pageVisitDuration", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "pageVisitDuration", - matcher: "eq", - values: [0], - }, - type: "matcher", - }); - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched pageVisitDuration", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "pageVisitDuration", - matcher: "eq", - values: [1], - }, - type: "matcher", - }); - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - it("satisfies rule based on matched ~timestampu", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "~timestampu", - matcher: "eq", - values: [mockedTimestamp.getTime() / 1000], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("satisfies rule based on matched ~timestampz", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "~timestampz", - matcher: "eq", - values: [mockedTimestamp.toISOString()], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/decisioningContext.window.spec.js b/test/unit/specs/components/RulesEngine/decisioningContext.window.spec.js deleted file mode 100644 index 9c032e78a..000000000 --- a/test/unit/specs/components/RulesEngine/decisioningContext.window.spec.js +++ /dev/null @@ -1,185 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - mockWindow, - setupResponseHandler, - proposition, -} from "./contextTestUtils.js"; - -describe("RulesEngine:globalContext:window", () => { - let applyResponse; - beforeEach(() => { - applyResponse = jasmine.createSpy(); - }); - - it("satisfies rule based on matched window height", () => { - setupResponseHandler(applyResponse, mockWindow({}), { - definition: { - key: "window.height", - matcher: "gt", - values: [90], - }, - type: "matcher", - }); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched window height", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - height: 50, - }), - { - definition: { - key: "window.height", - matcher: "gt", - values: [90], - }, - type: "matcher", - }, - ); - - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched window width", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - width: 200, - }), - { - definition: { - key: "window.width", - matcher: "gt", - values: [90], - }, - type: "matcher", - }, - ); - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched window width", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - width: 50, - }), - { - definition: { - key: "window.width", - matcher: "gt", - values: [90], - }, - type: "matcher", - }, - ); - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched window scrollX", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - scrollX: 200, - }), - { - definition: { - key: "window.scrollX", - matcher: "gt", - values: [90], - }, - type: "matcher", - }, - ); - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [proposition], - }), - ); - }); - - it("does not satisfy rule due to unmatched window scrollX", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - scrollX: 50, - }), - { - definition: { - key: "window.scrollX", - matcher: "gt", - values: [90], - }, - type: "matcher", - }, - ); - expect(applyResponse).toHaveBeenCalledOnceWith( - jasmine.objectContaining({ - propositions: [], - }), - ); - }); - - it("satisfies rule based on matched window scrollY", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - scrollY: 200, - }), - { - definition: { - key: "window.scrollY", - matcher: "gt", - values: [90], - }, - type: "matcher", - }, - ); - }); - - it("does not satisfy rule due to unmatched window scrollY", () => { - setupResponseHandler( - applyResponse, - mockWindow({ - scrollY: 50, - }), - { - definition: { - key: "window.scrollY", - matcher: "gt", - values: [90], - }, - type: "matcher", - }, - ); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/index.spec.js b/test/unit/specs/components/RulesEngine/index.spec.js deleted file mode 100644 index 59945682d..000000000 --- a/test/unit/specs/components/RulesEngine/index.spec.js +++ /dev/null @@ -1,203 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createRulesEngine from "../../../../../src/components/RulesEngine/index.js"; -import { defer } from "../../../../../src/utils/index.js"; -import { - mockRulesetResponseWithCondition, - proposition, -} from "./contextTestUtils.js"; - -describe("createRulesEngine:commands:evaluateRulesets", () => { - let mergeData; - let mockEvent; - let onResponseHandler; - let awaitConsentDeferred; - let consent; - let getBrowser; - let persistentStorage; - let createNamespacedStorage; - - beforeEach(() => { - mergeData = jasmine.createSpy(); - awaitConsentDeferred = defer(); - consent = jasmine.createSpyObj("consent", { - awaitConsent: awaitConsentDeferred.promise, - }); - getBrowser = jasmine.createSpy().and.returnValue("foo"); - window.referrer = - "https://www.google.com/search?q=adobe+journey+optimizer&oq=adobe+journey+optimizer"; - persistentStorage = jasmine.createSpyObj("persistentStorage", [ - "getItem", - "setItem", - "clear", - ]); - createNamespacedStorage = jasmine.createSpy().and.returnValue({ - persistent: persistentStorage, - }); - mockEvent = { - getContent: () => ({}), - hasQuery: () => true, - getViewName: () => undefined, - mergeData, - }; - }); - - const setUpDecisionEngine = ({ personalizationStorageEnabled }) => { - const config = { - orgId: "exampleOrgId", - personalizationStorageEnabled, - }; - const rulesEngine = createRulesEngine({ - config, - createNamespacedStorage, - consent, - getBrowser, - }); - rulesEngine.lifecycle.onComponentsRegistered(() => {}); - return rulesEngine; - }; - - it("should run the evaluateRulesets command and satisfy the rule based on global context", async () => { - const rulesEngine = setUpDecisionEngine({ - personalizationStorageEnabled: true, - }); - await awaitConsentDeferred.resolve(); - onResponseHandler = (onResponse) => { - onResponse({ - response: mockRulesetResponseWithCondition({ - definition: { - key: "referringPage.path", - matcher: "eq", - values: ["/search"], - }, - type: "matcher", - }), - }); - }; - rulesEngine.lifecycle.onBeforeEvent({ - event: mockEvent, - renderDecisions: true, - personalization: { decisionContext: {} }, - onResponse: onResponseHandler, - }); - const result = rulesEngine.commands.evaluateRulesets.run({}); - expect(result).toEqual({ - propositions: [proposition], - }); - }); - - it("should run the evaluateRulesets command and does not satisfy rule due to unmatched global context", async () => { - const rulesEngine = setUpDecisionEngine({ - personalizationStorageEnabled: true, - }); - await awaitConsentDeferred.resolve(); - onResponseHandler = (onResponse) => { - onResponse({ - response: mockRulesetResponseWithCondition({ - definition: { - key: "referringPage.path", - matcher: "eq", - values: ["/about"], - }, - type: "matcher", - }), - }); - }; - rulesEngine.lifecycle.onBeforeEvent({ - event: mockEvent, - renderDecisions: true, - personalization: { decisionContext: {} }, - onResponse: onResponseHandler, - }); - const result = rulesEngine.commands.evaluateRulesets.run({}); - expect(result).toEqual({ - propositions: [], - }); - }); - - it("should run the evaluateRulesets command and return propositions with renderDecisions true", async () => { - const rulesEngine = setUpDecisionEngine({ - personalizationStorageEnabled: true, - }); - await awaitConsentDeferred.resolve(); - onResponseHandler = (onResponse) => { - onResponse({ - response: mockRulesetResponseWithCondition({ - definition: { - key: "referringPage.path", - matcher: "eq", - values: ["/search"], - }, - type: "matcher", - }), - }); - }; - rulesEngine.lifecycle.onBeforeEvent({ - event: mockEvent, - renderDecisions: true, - personalization: { decisionContext: {} }, - onResponse: onResponseHandler, - }); - const result = rulesEngine.commands.evaluateRulesets.run({}); - expect(result).toEqual({ - propositions: [proposition], - }); - }); - - it("should run the evaluateRulesets command returns propositions with renderDecisions false", async () => { - const rulesEngine = setUpDecisionEngine({ - personalizationStorageEnabled: true, - }); - await awaitConsentDeferred.resolve(); - onResponseHandler = (onResponse) => { - onResponse({ - response: mockRulesetResponseWithCondition({ - definition: { - key: "referringPage.path", - matcher: "eq", - values: ["/search"], - }, - type: "matcher", - }), - }); - }; - rulesEngine.lifecycle.onBeforeEvent({ - event: mockEvent, - renderDecisions: false, - personalization: { decisionContext: {} }, - onResponse: onResponseHandler, - }); - const result = rulesEngine.commands.evaluateRulesets.run({}); - expect(result).toEqual({ - propositions: [proposition], - }); - }); - it("should clear the local storage when personalizationStorageEnabled is false", async () => { - setUpDecisionEngine({ personalizationStorageEnabled: false }); - await awaitConsentDeferred.resolve(); - expect(persistentStorage.clear).toHaveBeenCalled(); - }); - - it("should set eventRegistry storage when consent is obtained", async () => { - setUpDecisionEngine({ personalizationStorageEnabled: true }); - await awaitConsentDeferred.resolve(); - await expectAsync(awaitConsentDeferred.promise).toBeResolved(); - expect(persistentStorage.getItem).toHaveBeenCalled(); - }); - - it("should clear the local storage when consent is not obtained", async () => { - setUpDecisionEngine({ personalizationStorageEnabled: true }); - await awaitConsentDeferred.reject(); - await expectAsync(awaitConsentDeferred.promise).toBeRejected(); - expect(persistentStorage.clear).toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/RulesEngine/utils.spec.js b/test/unit/specs/components/RulesEngine/utils.spec.js deleted file mode 100644 index 01ce8b8d5..000000000 --- a/test/unit/specs/components/RulesEngine/utils.spec.js +++ /dev/null @@ -1,219 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - createInMemoryStorage, - createRestoreStorage, - createSaveStorage, - getActivityId, - getExpirationDate, -} from "../../../../../src/components/RulesEngine/utils.js"; - -describe("RulesEngine:utils", () => { - let storage; - let inMemoryStorage; - - beforeEach(() => { - storage = jasmine.createSpyObj("storage", ["getItem", "setItem", "clear"]); - inMemoryStorage = createInMemoryStorage(); - }); - - it("restores from storage", () => { - storage.getItem.and.returnValue( - '{ "something": true, "color": "orange", "person": { "height": 5.83 } }', - ); - const restore = createRestoreStorage(storage, "zoink"); - - expect(restore({ good: true })).toEqual({ - something: true, - color: "orange", - person: { height: 5.83 }, - }); - - expect(storage.getItem).toHaveBeenCalledWith("zoink"); - }); - - it("uses default value if storage unavailable", () => { - storage.getItem.and.returnValue(undefined); - const restore = createRestoreStorage(storage, "zoink"); - - expect(restore({ good: true })).toEqual({ good: true }); - - expect(storage.getItem).toHaveBeenCalledWith("zoink"); - }); - - it("saves to storage", (done) => { - storage.getItem.and.returnValue( - '{ "something": true, "color": "orange", "person": { "height": 5.83 } }', - ); - const save = createSaveStorage(storage, "zoink"); - - save({ - something: true, - color: "orange", - person: { height: 5.83 }, - }); - - setTimeout(() => { - expect(storage.setItem).toHaveBeenCalledWith( - "zoink", - '{"something":true,"color":"orange","person":{"height":5.83}}', - ); - - done(); - }, 20); - }); - it("should return the date of expiration", () => { - const mockedTimestamp = new Date(Date.UTC(2023, 8, 2, 13, 34, 56)); - jasmine.clock().install(); - jasmine.clock().mockDate(mockedTimestamp); - const retentionPeriod = 10; - const expectedDate = new Date(mockedTimestamp); - expectedDate.setDate(expectedDate.getDate() - retentionPeriod); - const result = getExpirationDate(retentionPeriod); - expect(result).toEqual(expectedDate); - jasmine.clock().uninstall(); - }); - it("should return the activityId", () => { - const proposition = { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scope: "web://mywebsite.com", - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - eventToken: "abc", - }, - strategies: [ - { - strategyID: "3VQe3oIqiYq2RAsYzmDTSf", - treatmentID: "yu7rkogezumca7i0i44v", - }, - ], - activity: { - id: "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", - }, - correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", - }, - items: [ - { - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - schema: "https://ns.adobe.com/personalization/ruleset-item", - data: { - version: 1, - rules: [ - { - condition: { - definition: { - conditions: [ - { - definition: { - conditions: [ - { - definition: { - key: "color", - matcher: "eq", - values: ["orange", "blue"], - }, - type: "matcher", - }, - { - definition: { - key: "action", - matcher: "eq", - values: ["lipstick"], - }, - type: "matcher", - }, - ], - logic: "and", - }, - type: "group", - }, - ], - logic: "and", - }, - type: "group", - }, - consequences: [ - { - type: "schema", - detail: { - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - selector: - "HTML > BODY > DIV.offer:eq(0) > IMG:nth-of-type(1)", - type: "setAttribute", - content: { - src: "img/demo-marketing-offer1-exp-A.png", - }, - prehidingSelector: - "HTML > BODY > DIV:nth-of-type(2) > IMG:nth-of-type(1)", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - id: "79129ecf-6430-4fbd-955a-b4f1dfdaa6fe", - }, - ], - }, - ], - }, - }, - ], - }; - expect(getActivityId(proposition)).toEqual( - "39ae8d4b-b55e-43dc-a143-77f50195b487#b47fde8b-57c1-4bbe-ae22-64d5b782d183", - ); - }); - - it("should return the activityId as undefined", () => { - const proposition = { - id: "2e4c7b28-b3e7-4d5b-ae6a-9ab0b44af87e", - scope: "web://mywebsite.com", - scopeDetails: { - decisionProvider: "AJO", - characteristics: { - eventToken: "abc", - }, - strategies: [ - { - strategyID: "3VQe3oIqiYq2RAsYzmDTSf", - treatmentID: "yu7rkogezumca7i0i44v", - }, - ], - correlationID: "02c77ea8-7c0e-4d33-8090-4a5bfd3d7503", - }, - }; - expect(getActivityId(proposition)).toEqual(undefined); - }); - it("should set and retrieve an item from in-memory storage", () => { - const key = "testKey"; - const value = "testValue"; - inMemoryStorage.setItem(key, value); - const retrievedValue = inMemoryStorage.getItem(key); - expect(retrievedValue).toEqual(value); - }); - - it("should return null for a non-existent item", () => { - const key = "nonExistentKey"; - const retrievedValue = inMemoryStorage.getItem(key); - expect(retrievedValue).toBeNull(); - }); - - it("should overwrite the value for an existing key", () => { - const key = "existingKey"; - const originalValue = "originalValue"; - const updatedValue = "updatedValue"; - inMemoryStorage.setItem(key, originalValue); - inMemoryStorage.setItem(key, updatedValue); - const retrievedValue = inMemoryStorage.getItem(key); - expect(retrievedValue).toEqual(updatedValue); - }); -}); diff --git a/test/unit/specs/components/StreamingMedia/configValidators.spec.js b/test/unit/specs/components/StreamingMedia/configValidators.spec.js deleted file mode 100644 index 5f76adc61..000000000 --- a/test/unit/specs/components/StreamingMedia/configValidators.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import configValidators from "../../../../../src/components/StreamingMedia/configValidators.js"; -import testConfigValidators from "../../../helpers/testConfigValidators.js"; - -describe("Streaming Media config validators", () => { - testConfigValidators({ - configValidators, - validConfigurations: [ - {}, - { - streamingMedia: { - channel: "test-channel", - playerName: "test-player-name", - }, - }, - { - streamingMedia: { - channel: "test-channel", - playerName: "test-player-name", - appVersion: "test-app-version", - }, - }, - { - streamingMedia: { - channel: "test-channel", - playerName: "test-player-name", - appVersion: "test-app-version", - mainPingInterval: 10, - adPingInterval: 1, - }, - }, - ], - invalidConfigurations: [ - { streamingMedia: "" }, - { streamingMedia: {} }, - { streamingMedia: { channel: "test-channel" } }, - { streamingMedia: { playerName: "test-player-name" } }, - ], - defaultValues: {}, - }); - - it("provides default values when Streaming media configured", () => { - const config = configValidators({ - streamingMedia: { - channel: "test-channel", - playerName: "test-player-name", - }, - }); - expect(config.streamingMedia.adPingInterval).toBe(10); - expect(config.streamingMedia.mainPingInterval).toBe(10); - }); -}); diff --git a/test/unit/specs/components/StreamingMedia/createMediaEventManager.spec.js b/test/unit/specs/components/StreamingMedia/createMediaEventManager.spec.js deleted file mode 100644 index b560bdf0c..000000000 --- a/test/unit/specs/components/StreamingMedia/createMediaEventManager.spec.js +++ /dev/null @@ -1,148 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -// tests for createMediaEventManager.js - -import createMediaEventManager from "../../../../../src/components/StreamingMedia/createMediaEventManager.js"; - -describe("StreamingMedia::createMediaEventManager", () => { - let config; - let eventManager; - let consent; - let sendEdgeNetworkRequest; - let mediaEventManager; - let setTimestamp; - - beforeEach(() => { - config = { - streamingMedia: { - playerName: "player1", - channel: "channel1", - version: "1.0.0", - }, - }; - eventManager = jasmine.createSpyObj("eventManager", [ - "createEvent", - "sendEvent", - ]); - consent = jasmine.createSpyObj("consent", ["awaitConsent"]); - sendEdgeNetworkRequest = jasmine - .createSpy("sendEdgeNetworkRequest") - .and.returnValue(Promise.resolve()); - setTimestamp = jasmine.createSpy("setTimestamp"); - mediaEventManager = createMediaEventManager({ - config, - eventManager, - consent, - sendEdgeNetworkRequest, - setTimestamp, - }); - }); - - it("should create a media event with user xdm", () => { - const options = { xdm: {} }; - const event = { - setUserXdm: jasmine.createSpy("setUserXdm"), - toJSON: () => ({ a: 1 }), - }; - - eventManager.createEvent.and.returnValue(event); - - const result = mediaEventManager.createMediaEvent({ options }); - - expect(result.toJSON()).toEqual(event.toJSON()); - }); - - it("should create a media session with player name, channel, and version", () => { - const options = { - xdm: { - mediaCollection: { - playerName: "player1", - channel: "channel1", - version: "1.0.0", - sessionDetails: {}, - }, - }, - }; - - const event = { - setUserXdm: jasmine.createSpy("setUserXdm"), - mergeXdm: jasmine.createSpy("mergeXdm"), - toJSON: () => ({ a: 1 }), - }; - - eventManager.createEvent.and.returnValue(event); - - const result = mediaEventManager.createMediaSession(options); - - expect(result.toJSON()).toEqual(event.toJSON()); - }); - - it("should augment media event with playhead, qoeDataDetails, and sessionID", () => { - const event = { - mergeXdm: jasmine.createSpy("mergeXdm"), - }; - const playerId = "player1"; - const getPlayerDetails = jasmine - .createSpy("getPlayerDetails") - .and.returnValue({ - playhead: 10, - qoeDataDetails: { duration: 60 }, - }); - const sessionID = "session1"; - - const result = mediaEventManager.augmentMediaEvent({ - event, - playerId, - getPlayerDetails, - sessionID, - }); - - expect(result).toBe(event); - expect(getPlayerDetails).toHaveBeenCalledWith({ playerId }); - expect(event.mergeXdm).toHaveBeenCalledWith({ - mediaCollection: { - playhead: 10, - qoeDataDetails: { duration: 60 }, - sessionID: "session1", - }, - }); - }); - - it("should track media session with event, playerId, and getPlayerDetails", () => { - const event = {}; - const playerId = "player1"; - const getPlayerDetails = () => {}; - const mediaOptions = { playerId, getPlayerDetails, legacy: false }; - - mediaEventManager.trackMediaSession({ - event, - mediaOptions, - }); - expect(eventManager.sendEvent).toHaveBeenCalledWith(event, { - mediaOptions, - edgeConfigOverrides: undefined, - }); - }); - - it("should track media event with action and send request to Edge Network", async () => { - const event = jasmine.createSpyObj("event", ["finalize"]); - const action = "play"; - - consent.awaitConsent.and.returnValue(Promise.resolve()); - - await mediaEventManager.trackMediaEvent({ event, action }); - - expect(event.finalize).toHaveBeenCalled(); - expect(sendEdgeNetworkRequest).toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/StreamingMedia/createMediaRequest.spec.js b/test/unit/specs/components/StreamingMedia/createMediaRequest.spec.js deleted file mode 100644 index d3bd65995..000000000 --- a/test/unit/specs/components/StreamingMedia/createMediaRequest.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createMediaRequest from "../../../../../src/components/StreamingMedia/createMediaRequest.js"; - -describe("StreamingMedia::createMediaRequest", () => { - it("should call createRequest with correct parameters", () => { - const mediaRequestPayload = {}; // replace with valid payload - const action = "testAction"; - const edgeSubPath = "/va"; - const result = createMediaRequest({ mediaRequestPayload, action }); - - expect(result.getAction()).toEqual(action); - expect(result.getEdgeSubPath()).toEqual(edgeSubPath); - expect(result.getUseSendBeacon()).toEqual(false); - }); -}); diff --git a/test/unit/specs/components/StreamingMedia/createMediaResponseHandler.spec.js b/test/unit/specs/components/StreamingMedia/createMediaResponseHandler.spec.js deleted file mode 100644 index e071c3ec0..000000000 --- a/test/unit/specs/components/StreamingMedia/createMediaResponseHandler.spec.js +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createMediaResponseHandler from "../../../../../src/components/StreamingMedia/createMediaResponseHandler.js"; - -describe("createMediaResponseHandler", () => { - let trackMediaEvent; - let mediaSessionCacheManager; - let config; - let logger; - let mediaResponseHandler; - let response; - const getPlayerDetails = () => {}; - - beforeEach(() => { - response = { - getPayloadsByType: jasmine.createSpy(), - }; - mediaSessionCacheManager = { - getSession: jasmine.createSpy().and.returnValue({ - getPlayerDetails: jasmine.createSpy(), - sessionPromise: Promise.resolve({ sessionId: "123" }), - }), - stopPing: jasmine.createSpy(), - savePing: jasmine.createSpy(), - }; - config = { - streamingMedia: { - adPingInterval: 5, - mainPingInterval: 10, - }, - }; - logger = { - info: jasmine.createSpy(), - }; - trackMediaEvent = jasmine.createSpy(); - mediaResponseHandler = createMediaResponseHandler({ - mediaSessionCacheManager, - logger, - config, - trackMediaEvent, - }); - }); - - it("should return empty object when no media payload", async () => { - response.getPayloadsByType.and.returnValue([]); - - const result = await mediaResponseHandler({ - response, - playerId: "player1", - getPlayerDetails, - }); - await expect(result).toEqual({}); - await expect(mediaSessionCacheManager.savePing).not.toHaveBeenCalled(); - }); - - it("should return session id", async () => { - response.getPayloadsByType.and.returnValue([{ sessionId: "123" }]); - - const result = await mediaResponseHandler({ - response, - playerId: "player1", - getPlayerDetails, - }); - await expect(result).toEqual({ sessionId: "123" }); - await expect(mediaSessionCacheManager.savePing).toHaveBeenCalled(); - }); - - it("should return sessionId when no player or getPlayerDetails function", async () => { - response.getPayloadsByType.and.returnValue([{ sessionId: "123" }]); - - const result = await mediaResponseHandler({ - response, - }); - await expect(result).toEqual({ sessionId: "123" }); - await expect(mediaSessionCacheManager.savePing).not.toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/StreamingMedia/createMediaSessionCacheManager.spec.js b/test/unit/specs/components/StreamingMedia/createMediaSessionCacheManager.spec.js deleted file mode 100644 index 9c2aecf0b..000000000 --- a/test/unit/specs/components/StreamingMedia/createMediaSessionCacheManager.spec.js +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createMediaSessionCacheManager from "../../../../../src/components/StreamingMedia/createMediaSessionCacheManager.js"; - -describe("StreamingMedia::createMediaSessionCacheManager", () => { - let mediaSessionCacheManager; - - beforeEach(() => { - mediaSessionCacheManager = createMediaSessionCacheManager(); - }); - - it("getSession should return correct session", () => { - const playerId = "player1"; - const sessionDetails = { id: "session1" }; - mediaSessionCacheManager.storeSession({ playerId, sessionDetails }); - - const result = mediaSessionCacheManager.getSession(playerId); - - expect(result).toEqual(sessionDetails); - }); - - it("stopPing should stop the Ping", () => { - const playerId = "player1"; - const sessionDetails = { id: "session1", pingId: 1 }; - - mediaSessionCacheManager.storeSession({ playerId, sessionDetails }); - - const result = mediaSessionCacheManager.getSession(playerId); - - mediaSessionCacheManager.stopPing({ playerId }); - - expect(result.pingId).toEqual(null); - }); - - it("storeSession should store the session", () => { - const playerId = "player1"; - const sessionDetails = { id: "session1" }; - mediaSessionCacheManager.storeSession({ playerId, sessionDetails }); - - const session = mediaSessionCacheManager.getSession(playerId); - - expect(session).toEqual(sessionDetails); - }); - - it("savePing should save the Ping", () => { - const playerId = "player1"; - const sessionDetails = { id: "session1" }; - mediaSessionCacheManager.storeSession({ playerId, sessionDetails }); - mediaSessionCacheManager.savePing({ playerId, pingId: 1 }); - - const session = mediaSessionCacheManager.getSession(playerId); - - expect(session.pingId).toEqual(1); - }); -}); diff --git a/test/unit/specs/components/StreamingMedia/createStreamingMediaComponent.spec.js b/test/unit/specs/components/StreamingMedia/createStreamingMediaComponent.spec.js deleted file mode 100644 index 7b03c89df..000000000 --- a/test/unit/specs/components/StreamingMedia/createStreamingMediaComponent.spec.js +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createStreamingMediaComponent from "../../../../../src/components/StreamingMedia/createStreamingMediaComponent.js"; - -describe("StreamingMedia::createComponent", () => { - const config = { - streamingMedia: { - channel: "testChannel", - playerName: "testPlayerName", - appVersion: "testAppVersion", - }, - }; - let logger; - let mediaComponent; - let trackMediaEvent; - let mediaResponseHandler; - let trackMediaSession; - - const build = (configs) => { - mediaComponent = createStreamingMediaComponent({ - config: configs, - logger, - trackMediaEvent, - mediaResponseHandler, - trackMediaSession, - }); - }; - - beforeEach(() => { - logger = jasmine.createSpyObj("logger", ["warn"]); - mediaResponseHandler = jasmine.createSpy(); - trackMediaEvent = jasmine.createSpy(); - trackMediaSession = jasmine.createSpy(); - build(config); - }); - - it("should call trackSession when with invalid config", async () => { - build({}); - const options = { - playerId: "testPlayerId", - getPlayerDetails: () => {}, - xdm: { - mediaCollection: { - sessionDetails: { - playerName: "testPlayerName", - }, - }, - }, - }; - - const createMediaSession = mediaComponent.commands.createMediaSession; - await createMediaSession.run(options); - expect(trackMediaSession).toHaveBeenCalled(); - }); - - it("should not send media event if no valid configs", async () => { - build({}); - const options = { - playerId: "testPlayerId", - xdm: { - mediaCollection: {}, - }, - }; - - const { sendMediaEvent } = mediaComponent.commands; - return expectAsync(sendMediaEvent.run(options)).toBeRejected(); - }); -}); diff --git a/test/unit/specs/components/StreamingMedia/createTrackMediaEvent.spec.js b/test/unit/specs/components/StreamingMedia/createTrackMediaEvent.spec.js deleted file mode 100644 index a850cd78f..000000000 --- a/test/unit/specs/components/StreamingMedia/createTrackMediaEvent.spec.js +++ /dev/null @@ -1,95 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createTrackMediaEvent from "../../../../../src/components/StreamingMedia/createTrackMediaEvent.js"; -import MediaEvents from "../../../../../src/components/StreamingMedia/constants/eventTypes.js"; - -describe("createTrackMediaEvent", () => { - let trackMediaEvent; - let mediaEventManager; - let mediaSessionCacheManager; - let config; - - beforeEach(() => { - mediaEventManager = { - createMediaEvent: jasmine.createSpy(), - augmentMediaEvent: jasmine.createSpy(), - trackMediaEvent: jasmine.createSpy().and.returnValue(Promise.resolve()), - }; - mediaSessionCacheManager = { - getSession: jasmine.createSpy().and.returnValue({ - getPlayerDetails: jasmine.createSpy(), - sessionPromise: Promise.resolve({ sessionId: "123" }), - }), - stopPing: jasmine.createSpy(), - savePing: jasmine.createSpy(), - }; - config = { - streamingMedia: { - adPingInterval: 5, - mainPingInterval: 10, - }, - }; - trackMediaEvent = createTrackMediaEvent({ - mediaEventManager, - mediaSessionCacheManager, - config, - }); - }); - - it("should send a media event", async () => { - const options = { - playerId: "player1", - xdm: { - eventType: "media.play", - }, - }; - - await trackMediaEvent(options); - - expect(mediaEventManager.createMediaEvent).toHaveBeenCalledWith({ - options, - }); - expect(mediaSessionCacheManager.getSession).toHaveBeenCalledWith( - options.playerId, - ); - expect(mediaEventManager.augmentMediaEvent).toHaveBeenCalled(); - expect(mediaEventManager.trackMediaEvent).toHaveBeenCalled(); - }); - - it("should stop the Ping for session complete event", async () => { - const options = { - playerId: "player1", - xdm: { - eventType: MediaEvents.SESSION_COMPLETE, - }, - }; - - await trackMediaEvent(options); - - expect(mediaSessionCacheManager.stopPing).toHaveBeenCalledWith({ - playerId: options.playerId, - }); - }); - - it("should save the Ping for non-session complete event", async () => { - const options = { - playerId: "player1", - xdm: { - eventType: "media.play", - }, - }; - - await trackMediaEvent(options); - - expect(mediaSessionCacheManager.savePing).toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/components/StreamingMedia/createTrackMediaSession.spec.js b/test/unit/specs/components/StreamingMedia/createTrackMediaSession.spec.js deleted file mode 100644 index 86e642120..000000000 --- a/test/unit/specs/components/StreamingMedia/createTrackMediaSession.spec.js +++ /dev/null @@ -1,127 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createTrackMediaSession from "../../../../../src/components/StreamingMedia/createTrackMediaSession.js"; -import PlaybackState from "../../../../../src/components/StreamingMedia/constants/playbackState.js"; - -describe("createTrackMediaSession", () => { - let trackMediaSession; - let mediaEventManager; - let mediaSessionCacheManager; - let config; - let logger; - - beforeEach(() => { - logger = { - warn: jasmine.createSpy(), - }; - mediaEventManager = { - createMediaSession: jasmine.createSpy(), - augmentMediaEvent: jasmine.createSpy(), - trackMediaSession: jasmine.createSpy().and.returnValue(Promise.resolve()), - }; - mediaSessionCacheManager = { - storeSession: jasmine.createSpy(), - }; - config = { - streamingMedia: { - playerName: "testPlayerName", - channel: "testChannel", - adPingInterval: 5, - mainPingInterval: 10, - }, - }; - trackMediaSession = createTrackMediaSession({ - config, - logger, - mediaEventManager, - mediaSessionCacheManager, - }); - }); - it("should track a session", async () => { - const sessionPromise = Promise.resolve("123"); - const playerId = "testPlayerId"; - const playerName = "testPlayerName"; - const eventType = "media.sessionStart"; - const event = { eventType }; - const getPlayerDetails = () => {}; - - const options = { - playerId, - getPlayerDetails, - xdm: { - mediaCollection: { - sessionDetails: { - playerName, - }, - }, - }, - }; - mediaEventManager.createMediaSession.and.returnValue({ eventType }); - mediaEventManager.augmentMediaEvent.and.returnValue({ - eventType, - xdm: { - mediaCollection: { - sessionDetails: { - playerName, - }, - playhead: 0, - }, - }, - }); - mediaEventManager.trackMediaSession.and.returnValue(sessionPromise); - - await trackMediaSession(options); - - expect(mediaEventManager.createMediaSession).toHaveBeenCalledWith(options); - - expect(mediaEventManager.augmentMediaEvent).toHaveBeenCalledWith({ - event, - playerId, - getPlayerDetails, - }); - - expect(mediaEventManager.trackMediaSession).toHaveBeenCalledWith({ - event, - mediaOptions: { - playerId, - getPlayerDetails, - legacy: false, - }, - edgeConfigOverrides: undefined, - }); - - expect(mediaSessionCacheManager.storeSession).toHaveBeenCalledWith({ - playerId, - sessionDetails: { - sessionPromise, - getPlayerDetails, - playbackState: PlaybackState.MAIN, - }, - }); - }); - it("should not track session when no valid configs", async () => { - config = {}; - trackMediaSession = createTrackMediaSession({ - config, - mediaEventManager, - mediaSessionCacheManager, - }); - const options = { - playerId: "player1", - xdm: { - eventType: "media.sessionStart", - }, - getPlayerDetails: "", - }; - return expectAsync(trackMediaSession(options)).toBeRejected(); - }); -}); diff --git a/test/unit/specs/components/StreamingMedia/validateMediaEventOptions.spec.js b/test/unit/specs/components/StreamingMedia/validateMediaEventOptions.spec.js deleted file mode 100644 index 9296f8f34..000000000 --- a/test/unit/specs/components/StreamingMedia/validateMediaEventOptions.spec.js +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import validateMediaEventOptions from "../../../../../src/components/StreamingMedia/validateMediaEventOptions.js"; - -describe("StreamingMedia::validateMediaEventOptions", () => { - it("should not fail when payerId and xdm are used", () => { - const options = { - playerId: "playerId", - xdm: { - eventType: "media.play", - mediaCollection: { - playhead: 0, - sessionID: "sessionID", - }, - }, - }; - - expect(() => { - validateMediaEventOptions({ options }); - }).not.toThrowError(); - }); - - it("should not fail when xdm with playhead is used", () => { - const options = { - xdm: { - eventType: "media.play", - mediaCollection: { - playhead: 0, - sessionID: "sessionID", - }, - }, - }; - - expect(() => { - validateMediaEventOptions({ options }); - }).not.toThrowError(); - }); - - it("should throw an error when invalid options are passed", () => { - const options = { - xdm: { - eventType: "media.play", - mediaCollection: { - playhead: "0", - sessionID: "sessionID", - }, - }, - }; - - expect(() => { - validateMediaEventOptions({ options }); - }).toThrowError(); - }); -}); diff --git a/test/unit/specs/components/StreamingMedia/validateMediaSessionOptions.spec.js b/test/unit/specs/components/StreamingMedia/validateMediaSessionOptions.spec.js deleted file mode 100644 index f11ab351f..000000000 --- a/test/unit/specs/components/StreamingMedia/validateMediaSessionOptions.spec.js +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import validateMediaSessionOptions from "../../../../../src/components/StreamingMedia/validateMediaSessionOptions.js"; - -describe("StreamingMedia::validateMediaSessionOptions", () => { - it("should not fail when playerId, callback and xdm are used", () => { - const options = { - playerId: "playerId", - getPlayerDetails: () => {}, - xdm: { - eventType: "eventType", - mediaCollection: { - sessionDetails: {}, - }, - }, - }; - - expect(() => { - validateMediaSessionOptions({ options }); - }).not.toThrowError(); - }); - - it("should not fail when playerId, callback and xdm are used", () => { - const options = { - xdm: { - eventType: "eventType", - mediaCollection: { - playhead: 0, - sessionDetails: {}, - }, - }, - }; - - expect(() => { - validateMediaSessionOptions({ options }); - }).not.toThrowError(); - }); - - it("should throw an error when invalid options are passed", () => { - const options = { - xdm: { - eventType: "eventType", - mediaCollection: { - playhead: "0", - sessionID: "sessionID", - }, - }, - }; - - expect(() => { - validateMediaSessionOptions({ options }); - }).toThrowError(); - }); -}); diff --git a/test/unit/specs/core/buildAndValidateConfig.spec.js b/test/unit/specs/core/buildAndValidateConfig.spec.js deleted file mode 100644 index 291243b39..000000000 --- a/test/unit/specs/core/buildAndValidateConfig.spec.js +++ /dev/null @@ -1,116 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import buildAndValidateConfig from "../../../../src/core/buildAndValidateConfig.js"; -import createConfig from "../../../../src/core/config/createConfig.js"; -import { boolean, objectOf } from "../../../../src/utils/validation/index.js"; - -describe("buildAndValidateConfig", () => { - let options; - let componentCreators; - let coreConfigValidators; - let logger; - let setDebugEnabled; - - beforeEach(() => { - options = {}; - const componentCreator = () => {}; - componentCreator.configValidators = objectOf({ - idSyncEnabled: boolean().default(true), - }); - componentCreators = [componentCreator]; - coreConfigValidators = objectOf({ - debugEnabled: boolean().default(false), - errorsEnabled: boolean(), - }) - .noUnknownFields() - .required(); - logger = { - enabled: false, - info: jasmine.createSpy(), - logOnBeforeCommand: jasmine.createSpy(), - logOnInstanceConfigured: jasmine.createSpy(), - }; - setDebugEnabled = jasmine.createSpy(); - }); - - it("adds validators and validates options", () => { - expect(() => { - buildAndValidateConfig({ - options: { idSyncEnabled: "invalid value" }, - componentCreators, - coreConfigValidators, - createConfig, - logger, - setDebugEnabled, - }); - }).toThrowError(); - }); - - it("sets debug enabled based on config", () => { - options.debugEnabled = true; - buildAndValidateConfig({ - options, - componentCreators, - coreConfigValidators, - createConfig, - logger, - setDebugEnabled, - }); - expect(setDebugEnabled).toHaveBeenCalledWith(true, { fromConfig: true }); - }); - - it("logs and returns computed configuration", () => { - logger.enabled = true; - buildAndValidateConfig({ - options, - componentCreators, - coreConfigValidators, - createConfig, - logger, - setDebugEnabled, - }); - expect(logger.logOnInstanceConfigured).toHaveBeenCalledWith({ - config: { - debugEnabled: false, - idSyncEnabled: true, - }, - }); - }); - - it("throws an error for unknown fields", () => { - logger.enabled = true; - options.foo = "bar"; - expect(() => - buildAndValidateConfig({ - options, - componentCreators, - coreConfigValidators, - createConfig, - logger, - setDebugEnabled, - }), - ).toThrowError(); - }); - - it("returns config", () => { - const result = buildAndValidateConfig({ - options, - componentCreators, - coreConfigValidators, - createConfig, - logger, - setDebugEnabled, - }); - expect(result).toEqual({ idSyncEnabled: true, debugEnabled: false }); - }); -}); diff --git a/test/unit/specs/core/componentCreators.spec.js b/test/unit/specs/core/componentCreators.spec.js deleted file mode 100644 index fff06b535..000000000 --- a/test/unit/specs/core/componentCreators.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import * as componentCreators from "../../../../src/core/componentCreators.js"; - -describe("componentCreators", () => { - it("is an object of component creators", () => { - const c = Object.keys(componentCreators).reduce((acc, key) => { - acc.push(componentCreators[key]); - return acc; - }, []); - - expect(c).toEqual(jasmine.any(Array)); - - c.forEach((componentCreator) => { - expect(componentCreator).toEqual(jasmine.any(Function)); - expect(componentCreator.namespace).toEqual(jasmine.any(String)); - - if (componentCreator.configValidators) { - // should export a validator function - expect(componentCreator.configValidators).toEqual( - jasmine.any(Function), - ); - } - }); - }); -}); diff --git a/test/unit/specs/core/config/createConfig.spec.js b/test/unit/specs/core/config/createConfig.spec.js deleted file mode 100644 index e969ef1e3..000000000 --- a/test/unit/specs/core/config/createConfig.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createConfig from "../../../../../src/core/config/createConfig.js"; - -let testConfig = {}; - -describe("createConfig", () => { - beforeEach(() => { - testConfig = { - a: 123, - b: "abc", - c: { - a1: "xyz", - }, - neg: { - neg: false, - }, - }; - }); - it("supports being instantiated with a config", () => { - const cfg = createConfig(testConfig); - expect(cfg.a).toEqual(123); - }); - it("supports getting a value assigned to a key", () => { - const cfg = createConfig(testConfig); - expect(cfg.a).toEqual(123); - }); - it("returns undefined when a missing key is requested", () => { - const cfg = createConfig(testConfig); - expect(cfg.missing).toBe(undefined); - }); - it("supports adding a key value mapping", () => { - const cfg = createConfig(testConfig); - cfg.d = "ABC"; - expect(cfg.d).toEqual("ABC"); - }); - - describe("changing config", () => { - it("does not change the provided options", () => { - const cfg = createConfig(testConfig); - cfg.d = "NEW VALUE"; - expect(testConfig.d).toBe(undefined); - }); - }); - - describe("changing provided options", () => { - it("does not change the config", () => { - const cfg = createConfig(testConfig); - testConfig.a = 456; - expect(cfg.a).toBe(123); - }); - }); -}); diff --git a/test/unit/specs/core/config/createCoreConfigs.spec.js b/test/unit/specs/core/config/createCoreConfigs.spec.js deleted file mode 100644 index 2c6669964..000000000 --- a/test/unit/specs/core/config/createCoreConfigs.spec.js +++ /dev/null @@ -1,134 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createCoreConfigs from "../../../../../src/core/config/createCoreConfigs.js"; - -describe("createCoreConfigs", () => { - let validator; - const baseConfig = { datastreamId: "1234", orgId: "org1" }; - - beforeEach(() => { - validator = createCoreConfigs(); - }); - - describe("debugEnabled", () => { - it("validates debugEnabled=undefined", () => { - const config = validator(baseConfig); - expect(config.debugEnabled).toBe(false); - }); - - it("validates debugEnabled=true", () => { - const config = validator({ - debugEnabled: true, - ...baseConfig, - }); - expect(config.debugEnabled).toBe(true); - }); - it("validates debugEnabled=false", () => { - const config = validator({ - debugEnabled: false, - ...baseConfig, - }); - expect(config.debugEnabled).toBe(false); - }); - - it("validates debugEnabled=123", () => { - expect(() => { - validator({ debugEnabled: 123, ...baseConfig }); - }).toThrowError(); - }); - }); - - [ - { datastreamId: "asdfasdf", orgId: "" }, - { datastreamId: "asdfasdf", orgId: "" }, - { - datastreamId: "myproperty1", - orgId: "53A16ACB5CC1D3760A495C99@AdobeOrg", - }, - { - datastreamId: "myproperty1", - orgId: "53A16ACB5CC1D3760A495C99@AdobeOrg", - }, - { - datastreamId: "myproperty1", - edgeDomain: "stats.firstparty.com", - orgId: "53A16ACB5CC1D3760A495C99@AdobeOrg", - }, - { - datastreamId: "myproperty1", - edgeDomain: "STATS.FIRSTPARTY.COM", - orgId: "53A16ACB5CC1D3760A495C99@AdobeOrg", - }, - { - datastreamId: "myproperty1", - edgeDomain: "STATS.FIRSTPARTY.COM", - orgId: "53A16ACB5CC1D3760A495C99@AdobeOrg", - }, - { - datastreamId: "myproperty1", - edgeDomain: "STATS.FIRSTPARTY.COM", - orgId: "53A16ACB5CC1D3760A495C99@AdobeOrg", - configurationOverrides: { - experience_platform: { - datasets: { - event: "werewr", - profile: "www", - }, - }, - }, - }, - ].forEach((cfg, i) => { - it(`validates configuration (${i})`, () => { - validator(cfg); - }); - }); - - [ - {}, - { datastreamId: "myproperty1", edgeDomain: "" }, - { datastreamId: "myproperty1", edgeDomain: "stats firstparty.com" }, - { - datastreamId: "myproperty1", - edgeDomain: "stats firstparty.com", - prehidingStyle: "", - }, - { - datastreamId: "myproperty1", - edgeBasePath: 123, - }, - ].forEach((cfg, i) => { - it(`invalidates configuration (${i})`, () => { - expect(() => validator(cfg)).toThrowError(); - }); - }); - - it("invalidates duplicate configIds", () => { - const config1 = { datastreamId: "property1", orgId: "ims1" }; - const config2 = { datastreamId: "property2", orgId: "ims2" }; - const config3 = { datastreamId: "property1", orgId: "ims3" }; - - validator(config1); - validator(config2); - expect(() => validator("", config3)).toThrowError(); - }); - - it("invalidates duplicate orgIds", () => { - const config1 = { datastreamId: "a", orgId: "a" }; - const config2 = { datastreamId: "b", orgId: "b" }; - const config3 = { datastreamId: "c", orgId: "a" }; - - validator(config1); - validator(config2); - expect(() => validator("", config3)).toThrowError(); - }); -}); diff --git a/test/unit/specs/core/consent/createConsent.spec.js b/test/unit/specs/core/consent/createConsent.spec.js deleted file mode 100644 index cc97e269b..000000000 --- a/test/unit/specs/core/consent/createConsent.spec.js +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createConsent from "../../../../../src/core/consent/createConsent.js"; - -describe("createConsent", () => { - let state; - let subject; - let logger; - - beforeEach(() => { - state = jasmine.createSpyObj("state", [ - "in", - "out", - "pending", - "awaitConsent", - "withConsent", - ]); - logger = jasmine.createSpyObj("logger", ["warn"]); - subject = createConsent({ generalConsentState: state, logger }); - }); - - it("sets consent to in", () => { - subject.setConsent({ general: "in" }); - expect(state.in).toHaveBeenCalled(); - expect(state.out).not.toHaveBeenCalled(); - expect(state.pending).not.toHaveBeenCalled(); - expect(logger.warn).not.toHaveBeenCalled(); - }); - it("sets consent to out", () => { - subject.setConsent({ general: "out" }); - expect(state.in).not.toHaveBeenCalled(); - expect(state.out).toHaveBeenCalled(); - expect(state.pending).not.toHaveBeenCalled(); - expect(logger.warn).not.toHaveBeenCalled(); - }); - it("sets consent to pending", () => { - subject.setConsent({ general: "pending" }); - expect(state.in).not.toHaveBeenCalled(); - expect(state.out).not.toHaveBeenCalled(); - expect(state.pending).toHaveBeenCalled(); - expect(logger.warn).not.toHaveBeenCalled(); - }); - it("logs unknown consent values", () => { - subject.setConsent({ general: "foo" }); - expect(state.in).not.toHaveBeenCalled(); - expect(state.out).not.toHaveBeenCalled(); - expect(state.pending).not.toHaveBeenCalled(); - expect(logger.warn).toHaveBeenCalledWith("Unknown consent value: foo"); - }); - it("suspends", () => { - subject.suspend(); - expect(state.in).not.toHaveBeenCalled(); - expect(state.out).not.toHaveBeenCalled(); - expect(state.pending).toHaveBeenCalled(); - expect(logger.warn).not.toHaveBeenCalled(); - }); - it("calls await consent", () => { - state.awaitConsent.and.returnValue("mypromise"); - expect(subject.awaitConsent()).toEqual("mypromise"); - }); - it("calls with consent", () => { - state.withConsent.and.returnValue("mypromise"); - expect(subject.withConsent()).toEqual("mypromise"); - }); -}); diff --git a/test/unit/specs/core/consent/createConsentStateMachine.spec.js b/test/unit/specs/core/consent/createConsentStateMachine.spec.js deleted file mode 100644 index 3b7b02b04..000000000 --- a/test/unit/specs/core/consent/createConsentStateMachine.spec.js +++ /dev/null @@ -1,192 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createConsentStateMachine from "../../../../../src/core/consent/createConsentStateMachine.js"; -import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; - -const DECLINED_CONSENT_ERROR_CODE = "declinedConsent"; - -describe("createConsentStateMachine", () => { - let logger; - let subject; - - beforeEach(() => { - logger = jasmine.createSpyObj("logger", ["info", "warn"]); - subject = createConsentStateMachine({ logger }); - }); - - it("does not resolve promise if consent is pending", () => { - subject.pending(); - const onFulfilled = jasmine.createSpy("onFulfilled"); - subject.awaitConsent().then(onFulfilled); - - return flushPromiseChains().then(() => { - expect(onFulfilled).not.toHaveBeenCalled(); - }); - }); - - it("resolves promise if user consented to all purposes", () => { - subject.in(); - const onFulfilled = jasmine.createSpy("onFulfilled"); - subject.awaitConsent().then(onFulfilled); - - return flushPromiseChains().then(() => { - expect(onFulfilled).toHaveBeenCalled(); - }); - }); - - [ - ["default", "No consent preferences have been set."], - ["initial", "The user declined consent."], - ["new", "The user declined consent."], - ].forEach(([source, expectedMessage]) => { - it("rejects promise if user consented to no purposes", () => { - subject.out(source); - const onRejected = jasmine.createSpy("onRejected"); - subject.awaitConsent().catch(onRejected); - - return flushPromiseChains().then(() => { - const error = onRejected.calls.argsFor(0)[0]; - expect(error.code).toBe(DECLINED_CONSENT_ERROR_CODE); - expect(error.message).toBe(expectedMessage); - }); - }); - }); - - it("resolves queued promises when consent set to in", () => { - subject.pending(); - const onFulfilled = jasmine.createSpy("onFulfilled"); - subject.awaitConsent().then(onFulfilled); - - return flushPromiseChains() - .then(() => { - expect(onFulfilled).not.toHaveBeenCalled(); - subject.in(); - return flushPromiseChains(); - }) - .then(() => { - expect(onFulfilled).toHaveBeenCalled(); - }); - }); - - it("rejects queued promises when consent set to out", () => { - subject.pending(); - const onRejected = jasmine.createSpy("onRejected"); - subject.awaitConsent().catch(onRejected); - - return flushPromiseChains() - .then(() => { - expect(onRejected).not.toHaveBeenCalled(); - subject.out(); - return flushPromiseChains(); - }) - .then(() => { - const error = onRejected.calls.argsFor(0)[0]; - expect(error.code).toBe(DECLINED_CONSENT_ERROR_CODE); - expect(error.message).toBe("The user declined consent."); - }); - }); - - // This is what would happen when the consent component is not included - it("resolves promises when it is not initialized", () => { - const onFulfilled = jasmine.createSpy("onFulfilled"); - subject.awaitConsent().then(onFulfilled); - - return flushPromiseChains().then(() => { - expect(onFulfilled).toHaveBeenCalled(); - }); - }); - - [ - ["in", "default"], - [ - "in", - "initial", - "Loaded user consent preferences. The user previously consented.", - "info", - ], - ["in", "new", "User consented.", "info"], - [ - "out", - "default", - "User consent preferences not found. Default consent of out will be used.", - ], - [ - "out", - "initial", - "Loaded user consent preferences. The user previously declined consent.", - ], - ["out", "new", "User declined consent."], - [ - "pending", - "default", - "User consent preferences not found. Default consent of pending will be used. Some commands may be delayed.", - "info", - ], - ["pending", "initial"], - ["pending", "new"], - ].forEach(([action, source, expectedMessage, logLevel = "warn"]) => { - it(`logs the correct messages when ${action} is called with source ${source}`, () => { - subject[action](source); - if (expectedMessage) { - expect(logger[logLevel]).toHaveBeenCalledWith(expectedMessage); - } else { - expect(logger.warn).not.toHaveBeenCalled(); - } - }); - }); - - [ - ["in", "User consented.", "info"], - ["out", "User declined consent.", "warn"], - ].forEach(([action, expectedMessage, logLevel]) => { - ["in", "out", "pending"].forEach((defaultConsent) => { - it(`logs a message when first setting consent (${defaultConsent} => ${action}) using setConsent`, () => { - subject[defaultConsent]("default"); - subject.pending(); - subject[action]("new"); - expect(logger[logLevel]).toHaveBeenCalledWith(expectedMessage); - }); - it(`logs a message when first setting consent (${defaultConsent} => ${action}) using sendEvent`, () => { - subject[defaultConsent]("default"); - subject[action]("new"); - expect(logger[logLevel]).toHaveBeenCalledWith(expectedMessage); - }); - }); - it(`doesn't log a message when a request returns or fails. (${action})`, () => { - subject[action]("initial"); - logger.info.calls.reset(); - logger.warn.calls.reset(); - subject[action]("new"); - subject[action]("new"); - expect(logger.info).not.toHaveBeenCalled(); - expect(logger.warn).not.toHaveBeenCalled(); - }); - }); - - describe("withConsent", () => { - ["default", "initial", "new"].forEach((source) => { - it(`returns immediately when ${source} consent is set to "in"`, () => { - subject.in(source); - return expectAsync(subject.withConsent()).toBeResolvedTo(); - }); - it(`rejects when ${source} consent is set to "out"`, () => { - subject.out(source); - return expectAsync(subject.withConsent()).toBeRejected(); - }); - it(`rejects when ${source} consent is set to "pending"`, () => { - subject.pending(source); - return expectAsync(subject.withConsent()).toBeRejected(); - }); - }); - }); -}); diff --git a/test/unit/specs/core/createComponentRegistry.spec.js b/test/unit/specs/core/createComponentRegistry.spec.js deleted file mode 100644 index 9a1747c5a..000000000 --- a/test/unit/specs/core/createComponentRegistry.spec.js +++ /dev/null @@ -1,220 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createComponentRegistry from "../../../../src/core/createComponentRegistry.js"; - -const commandErrorRegex = - /\[CompOne\] An error occurred while executing the perform command./; -const lifecycleErrorRegex = - /\[CompOne\] An error occurred while executing the onBeforeEvent lifecycle hook./; - -describe("createComponentRegistry", () => { - describe("register", () => { - it("should not register components with existing commands", () => { - expect(() => { - const registry = createComponentRegistry(); - registry.register("CompOne", { - commands: { - command1() {}, - command2() {}, - command3() {}, - }, - }); - registry.register("CompTwo", { - commands: { - command2() {}, - command3() {}, - command4() {}, - }, - }); - }).toThrowError( - "[ComponentRegistry] Could not register CompTwo because it has existing command(s): command2,command3", - ); - }); - }); - - describe("getCommand", () => { - it("handles a command that returns a non-promise", () => { - const registry = createComponentRegistry(); - const component = { - commands: { - perform: jasmine.createSpy().and.returnValue("nonPromiseValue"), - }, - }; - registry.register("CompOne", component); - const command = registry.getCommand("perform"); - const result = command("arg1", "arg2"); - expect(component.commands.perform).toHaveBeenCalledWith("arg1", "arg2"); - expect(result).toBe("nonPromiseValue"); - }); - - it("handles a command that returns a promise that gets resolved", () => { - const registry = createComponentRegistry(); - const component = { - commands: { - perform: jasmine - .createSpy() - .and.returnValue(Promise.resolve("resolvedPromiseValue")), - }, - }; - registry.register("CompOne", component); - const command = registry.getCommand("perform"); - const result = command("arg1", "arg2"); - expect(component.commands.perform).toHaveBeenCalledWith("arg1", "arg2"); - return result.then((value) => { - expect(value).toBe("resolvedPromiseValue"); - }); - }); - - it("handles a command that throws an error", () => { - const registry = createComponentRegistry(); - const runSpy = jasmine.createSpy().and.throwError("thrownError"); - const component = { - commands: { - perform: { - run: runSpy, - }, - }, - }; - registry.register("CompOne", component); - const command = registry.getCommand("perform"); - expect(() => { - command.run("arg1", "arg2"); - }).toThrowError(commandErrorRegex); - expect(runSpy).toHaveBeenCalledWith("arg1", "arg2"); - }); - - it("handles a command that returns a promise that gets rejected", () => { - const registry = createComponentRegistry(); - const runSpy = jasmine - .createSpy() - .and.returnValue(Promise.reject(new Error("rejectedPromiseError"))); - const component = { - commands: { - perform: { - run: runSpy, - }, - }, - }; - registry.register("CompOne", component); - const command = registry.getCommand("perform"); - const result = command.run("arg1", "arg2"); - expect(runSpy).toHaveBeenCalledWith("arg1", "arg2"); - return result.then(fail).catch((error) => { - expect(error).toEqual(jasmine.any(Error)); - expect(error.message).toMatch(commandErrorRegex); - }); - }); - - it("should return undefined if command does not exist", () => { - const registry = createComponentRegistry(); - const command = registry.getCommand("bogus"); - expect(command).toBeUndefined(); - }); - }); - - describe("getLifecycleCallbacks", () => { - it("handles a callback that returns a non-promise", () => { - const registry = createComponentRegistry(); - const component = { - lifecycle: { - onBeforeEvent: jasmine.createSpy().and.returnValue("nonPromiseValue"), - }, - }; - registry.register("CompOne", component); - const callback = registry.getLifecycleCallbacks("onBeforeEvent")[0]; - const result = callback("arg1", "arg2"); - expect(component.lifecycle.onBeforeEvent).toHaveBeenCalledWith( - "arg1", - "arg2", - ); - expect(result).toBe("nonPromiseValue"); - }); - - it("handles a callback that returns a promise that gets resolved", () => { - const registry = createComponentRegistry(); - const component = { - lifecycle: { - onBeforeEvent: jasmine - .createSpy() - .and.returnValue(Promise.resolve("resolvedPromiseValue")), - }, - }; - registry.register("CompOne", component); - const callback = registry.getLifecycleCallbacks("onBeforeEvent")[0]; - const result = callback("arg1", "arg2"); - expect(component.lifecycle.onBeforeEvent).toHaveBeenCalledWith( - "arg1", - "arg2", - ); - return result.then((value) => { - expect(value).toBe("resolvedPromiseValue"); - }); - }); - - it("handles a callback that throws an error", () => { - const registry = createComponentRegistry(); - const component = { - lifecycle: { - onBeforeEvent: jasmine.createSpy().and.throwError("thrownError"), - }, - }; - registry.register("CompOne", component); - const callback = registry.getLifecycleCallbacks("onBeforeEvent")[0]; - expect(() => { - callback("arg1", "arg2"); - }).toThrowError(lifecycleErrorRegex); - expect(component.lifecycle.onBeforeEvent).toHaveBeenCalledWith( - "arg1", - "arg2", - ); - }); - - it("handles a callback that returns a promise that gets rejected", () => { - const registry = createComponentRegistry(); - const component = { - lifecycle: { - onBeforeEvent: jasmine - .createSpy() - .and.returnValue(Promise.reject(new Error("rejectedPromiseError"))), - }, - }; - registry.register("CompOne", component); - const callback = registry.getLifecycleCallbacks("onBeforeEvent")[0]; - const result = callback("arg1", "arg2"); - expect(component.lifecycle.onBeforeEvent).toHaveBeenCalledWith( - "arg1", - "arg2", - ); - return result.then(fail).catch((error) => { - expect(error).toEqual(jasmine.any(Error)); - expect(error.message).toMatch(lifecycleErrorRegex); - }); - }); - - it("should return all registered lifecycle callbacks", () => { - const registry = createComponentRegistry(); - registry.register("CompOne", { - lifecycle: { - onBeforeEvent() {}, - }, - }); - registry.register("CompTwo", { - lifecycle: { - onBeforeEvent() {}, - }, - }); - const callbacks = registry.getLifecycleCallbacks("onBeforeEvent"); - expect(callbacks.length).toBe(2); - }); - }); -}); diff --git a/test/unit/specs/core/createCookieTransfer.spec.js b/test/unit/specs/core/createCookieTransfer.spec.js deleted file mode 100644 index 1c3285eb3..000000000 --- a/test/unit/specs/core/createCookieTransfer.spec.js +++ /dev/null @@ -1,186 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createCookieTransfer from "../../../../src/core/createCookieTransfer.js"; - -describe("createCookieTransfer", () => { - let apexDomain; - const endpointDomain = "thirdparty.com"; - let shouldTransferCookie; - let payload; - let cookieJar; - let cookieTransfer; - const date = new Date(); - const dateProvider = () => date; - - beforeEach(() => { - apexDomain = "example.com"; - shouldTransferCookie = jasmine.createSpy("shouldTransferCookie"); - shouldTransferCookie.and.returnValue(false); - payload = jasmine.createSpyObj("payload", ["mergeState"]); - cookieJar = jasmine.createSpyObj("cookieJar", ["get", "set"]); - }); - - const build = () => { - cookieTransfer = createCookieTransfer({ - cookieJar, - shouldTransferCookie, - apexDomain, - dateProvider, - }); - }; - - describe("cookiesToPayload", () => { - it("does not transfer cookies to payload if endpoint is first-party", () => { - build(); - cookieTransfer.cookiesToPayload(payload, "edge.example.com"); - expect(payload.mergeState).toHaveBeenCalledWith({ - domain: apexDomain, - cookiesEnabled: true, - }); - }); - - it("does not set state.entries if there are no qualifying cookies", () => { - cookieJar.get.and.returnValue({}); - build(); - cookieTransfer.cookiesToPayload(payload, endpointDomain); - expect(payload.mergeState).toHaveBeenCalledWith({ - domain: apexDomain, - cookiesEnabled: true, - }); - }); - - ["example.com", ""].forEach((domain) => { - it(`transfers eligible cookies to payload with domain ${domain}`, () => { - apexDomain = domain; - build(); - cookieJar.get.and.returnValue({ - kndctr_ABC_CustomOrg_identity: "XYZ@CustomOrg", - ineligible_cookie: "foo", - kndctr_ABC_CustomOrg_optIn: "all", - at_qa_mode: - '{"token":"QATokenString","listedActivitiesOnly":true,"evaluateAsTrueAudienceIds":["2480042"],"previewIndexes":[{"activityIndex":1,"experienceIndex":1}]}', - }); - shouldTransferCookie.and.returnValues(true, false, true, true); - cookieTransfer.cookiesToPayload(payload, endpointDomain); - expect(payload.mergeState).toHaveBeenCalledWith({ - domain: apexDomain, - cookiesEnabled: true, - entries: [ - { - key: "kndctr_ABC_CustomOrg_identity", - value: "XYZ@CustomOrg", - }, - { - key: "kndctr_ABC_CustomOrg_optIn", - value: "all", - }, - { - key: "at_qa_mode", - value: - '{"token":"QATokenString","listedActivitiesOnly":true,"evaluateAsTrueAudienceIds":["2480042"],"previewIndexes":[{"activityIndex":1,"experienceIndex":1}]}', - }, - ], - }); - }); - }); - }); - - describe("responseToCookies", () => { - let response; - beforeEach(() => { - response = jasmine.createSpyObj("response", ["getPayloadsByType"]); - }); - - it("adds a cookie with the correct domain", () => { - build(); - response.getPayloadsByType.and.returnValue([ - { - key: "mykey", - value: "myvalue", - }, - ]); - cookieTransfer.responseToCookies(response); - expect(cookieJar.set).toHaveBeenCalledOnceWith("mykey", "myvalue", { - domain: "example.com", - }); - }); - - it("adds multiple cookies", () => { - build(); - response.getPayloadsByType.and.returnValue([ - { - key: "mykey1", - value: "myvalue1", - }, - { - key: "mykey2", - value: "myvalue2", - }, - ]); - cookieTransfer.responseToCookies(response); - expect(cookieJar.set).toHaveBeenCalledWith( - "mykey1", - "myvalue1", - jasmine.any(Object), - ); - expect(cookieJar.set).toHaveBeenCalledWith( - "mykey2", - "myvalue2", - jasmine.any(Object), - ); - }); - - it("sets the expires attribute", () => { - build(); - response.getPayloadsByType.and.returnValue([ - { - key: "mykey", - value: "myvalue", - maxAge: 172800, // 24 * 60 * 60 * 2 - }, - ]); - cookieTransfer.responseToCookies(response); - expect(cookieJar.set.calls.argsFor(0)[2].expires.getTime()).toEqual( - date.getTime() + 172800 * 1000, - ); - }); - - it("adds a sameSite=none cookie with secure attribute", () => { - build(); - response.getPayloadsByType.and.returnValue([ - { - key: "mykey", - value: "myvalue", - attrs: { SameSite: "None" }, - }, - ]); - cookieTransfer.responseToCookies(response); - expect(cookieJar.set.calls.argsFor(0)[2].sameSite).toEqual("none"); - expect(cookieJar.set.calls.argsFor(0)[2].secure).toEqual(true); - }); - - it("adds a sameSite=strict cookie", () => { - build(); - response.getPayloadsByType.and.returnValue([ - { - key: "mykey", - value: "myvalue", - attrs: { SameSite: "Strict" }, - }, - ]); - cookieTransfer.responseToCookies(response); - expect(cookieJar.set.calls.argsFor(0)[2].sameSite).toEqual("strict"); - expect(cookieJar.set.calls.argsFor(0)[2].secure).toBeUndefined(); - }); - }); -}); diff --git a/test/unit/specs/core/createEvent.spec.js b/test/unit/specs/core/createEvent.spec.js deleted file mode 100644 index 49dd20ba6..000000000 --- a/test/unit/specs/core/createEvent.spec.js +++ /dev/null @@ -1,402 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createEvent from "../../../../src/core/createEvent.js"; - -describe("createEvent", () => { - let event; - - beforeEach(() => { - event = createEvent(); - }); - - it("deeply merges XDM with user-provided XDM merged last", () => { - event.setUserXdm({ - fruit: { - type: "apple", - }, - veggie: { - type: "carrot", - }, - }); - event.mergeXdm({ - fruit: { - type: "strawberry", - }, - sport: { - type: "basketball", - }, - }); - event.mergeXdm(); - event.mergeXdm(null); - event.mergeXdm({ - sport: { - type: "football", - }, - game: { - type: "clue", - }, - }); - event.finalize(); - expect(event.toJSON()).toEqual({ - xdm: { - fruit: { - type: "apple", - }, - veggie: { - type: "carrot", - }, - sport: { - type: "football", - }, - game: { - type: "clue", - }, - }, - }); - }); - - it("does not modify the original user XDM object", () => { - const dataLayer = { - fruit: { - type: "apple", - }, - veggie: { - type: "carrot", - }, - }; - event.setUserXdm(dataLayer); - event.mergeXdm({ - fruit: { - type: "strawberry", - }, - sport: { - type: "basketball", - }, - }); - expect(dataLayer).toEqual({ - fruit: { - type: "apple", - }, - veggie: { - type: "carrot", - }, - }); - }); - - it("handles undefined user XDM", () => { - event.setUserXdm(undefined); - event.mergeXdm({ - fruit: "apple", - }); - event.finalize(); - expect(event.toJSON()).toEqual({ - xdm: { - fruit: "apple", - }, - }); - }); - - it("sets user data", () => { - event.setUserData({ fruit: "apple" }); - event.setUserData({ veggie: "carrot" }); - event.finalize(); - expect(event.toJSON()).toEqual({ - data: { - veggie: "carrot", - }, - }); - }); - - it("handles undefined user data", () => { - event.setUserData(undefined); - event.finalize(); - expect(event.toJSON()).toEqual({}); - }); - - it("deeply merges meta", () => { - event.mergeMeta({ - fruit: { - type: "strawberry", - }, - sport: { - type: "basketball", - }, - }); - event.mergeMeta({ - sport: { - type: "football", - }, - game: { - type: "clue", - }, - }); - event.mergeMeta(); - event.mergeMeta(null); - event.finalize(); - expect(event.toJSON()).toEqual({ - meta: { - fruit: { - type: "strawberry", - }, - sport: { - type: "football", - }, - game: { - type: "clue", - }, - }, - }); - }); - - it("deeply merges query", () => { - event.mergeQuery({ - fruit: { - type: "strawberry", - }, - sport: { - type: "basketball", - }, - }); - event.mergeQuery({ - sport: { - type: "football", - }, - game: { - type: "clue", - }, - }); - event.mergeQuery(); - event.mergeQuery(null); - event.finalize(); - expect(event.toJSON()).toEqual({ - query: { - fruit: { - type: "strawberry", - }, - sport: { - type: "football", - }, - game: { - type: "clue", - }, - }, - }); - }); - - it("sets documentUnloading", () => { - expect(event.getDocumentMayUnload()).toBeFalse(); - event.documentMayUnload(); - expect(event.getDocumentMayUnload()).toBeTrue(); - }); - - it("throws error when mergeXdm called after finalize", () => { - event.setUserXdm({ web: {} }); - event.finalize(); - expect(() => event.mergeXdm({ a: "b" })).toThrowError( - "mergeXdm cannot be called after event is finalized.", - ); - }); - - it("throws error when toJSON called before finalize", () => { - event.setUserXdm({ web: {} }); - expect(() => event.toJSON()).toThrowError("toJSON called before finalize"); - }); - - it("reports whether the event is empty", () => { - expect(event.isEmpty()).toBeTrue(); - event.setUserData({ foo: "bar" }); - expect(event.isEmpty()).toBeFalse(); - }); - it("returns undefined when no viewName exists", () => { - expect(event.getViewName()).toBe(undefined); - event.setUserXdm({ web: {} }); - expect(event.getViewName()).toBe(undefined); - event.setUserXdm({ web: { webPageDetails: {} } }); - expect(event.getViewName()).toBe(undefined); - }); - it("returns viewName when viewName exists", () => { - event.setUserXdm({ web: { webPageDetails: { viewName: "cart" } } }); - expect(event.getViewName()).toBe("cart"); - }); - describe("applyCallback", () => { - it("can add fields to empty xdm", () => { - const callback = ({ xdm, data }) => { - xdm.a = "1"; - data.b = "2"; - }; - const subject = createEvent(); - subject.finalize(callback); - expect(subject.toJSON()).toEqual({ xdm: { a: "1" }, data: { b: "2" } }); - }); - - it("can add fields to an existing xdm", () => { - const callback = ({ xdm, data }) => { - xdm.b = "2"; - data.b = "2"; - }; - const subject = createEvent(); - subject.setUserData({ a: "1" }); - subject.setUserXdm({ a: "1" }); - subject.finalize(callback); - expect(subject.toJSON()).toEqual({ - xdm: { a: "1", b: "2" }, - data: { a: "1", b: "2" }, - }); - }); - - it("can remove fields", () => { - const callback = ({ xdm, data }) => { - delete xdm.a; - delete data.a; - }; - const subject = createEvent(); - subject.setUserXdm({ a: "1", b: "2" }); - subject.setUserData({ a: "1", b: "2" }); - subject.finalize(callback); - expect(subject.toJSON()).toEqual({ xdm: { b: "2" }, data: { b: "2" } }); - }); - - it("can set xdm or data to empty objects", () => { - const callback = (content) => { - content.xdm = {}; - content.data = {}; - }; - const subject = createEvent(); - subject.setUserXdm({ a: "1", b: "2" }); - subject.setUserData({ a: "1", b: "2" }); - subject.finalize(callback); - expect(subject.toJSON()).toEqual({}); - }); - - it("can delete xdm or data objects", () => { - const callback = (content) => { - delete content.xdm; - delete content.data; - }; - const subject = createEvent(); - subject.setUserXdm({ a: "1", b: "2" }); - subject.setUserData({ a: "1", b: "2" }); - subject.finalize(callback); - expect(subject.toJSON()).toEqual({}); - }); - - it("event merges when there is an error", () => { - const callback = ({ xdm, data }) => { - delete xdm.a; - xdm.c = "3"; - delete data.a; - data.c = "3"; - throw new Error("Expected Error"); - }; - const subject = createEvent(); - subject.setUserXdm({ a: "1", b: "2" }); - subject.setUserData({ a: "1", b: "2" }); - expect(() => subject.finalize(callback)).toThrowError("Expected Error"); - expect(subject.toJSON()).toEqual({ - xdm: { b: "2", c: "3" }, - data: { b: "2", c: "3" }, - }); - }); - - it("event shouldSend should be true when callback returns undefined", () => { - const callback = () => { - return undefined; - }; - const subject = createEvent(); - subject.finalize(callback); - expect(subject.shouldSend()).toBeTrue(); - }); - - it("event shouldSend should be true when callback returns true", () => { - const callback = () => { - return true; - }; - const subject = createEvent(); - subject.finalize(callback); - expect(subject.shouldSend()).toBeTrue(); - }); - - it("event shouldSend should be false when callback throws error", () => { - const callback = () => { - throw new Error("Expected Error"); - }; - const subject = createEvent(); - expect(() => subject.finalize(callback)).toThrowError("Expected Error"); - expect(subject.shouldSend()).toBeFalse(); - }); - - it("event shouldSend should be false when callback returns false", () => { - const callback = () => { - return false; - }; - const subject = createEvent(); - subject.finalize(callback); - expect(subject.shouldSend()).toBeFalse(); - }); - - it("can replace xdm or data", () => { - const callback = (content) => { - content.xdm = { a: "1" }; - content.data = { b: "2" }; - }; - - const subject = createEvent(); - subject.setUserXdm({ c: "3" }); - subject.setUserData({ d: "4" }); - subject.finalize(callback); - expect(subject.toJSON()).toEqual({ - xdm: { a: "1" }, - data: { b: "2" }, - }); - }); - }); - - it("deduplicates propositions by id", () => { - const subject = createEvent(); - subject.mergeXdm({ - _experience: { - decisioning: { - propositions: [ - { id: "1", scope: "a" }, - { id: "2", scope: "a" }, - ], - }, - }, - }); - subject.setUserXdm({ - _experience: { - decisioning: { - propositions: [ - { id: "2", scope: "a" }, - { id: "3", scope: "a" }, - { id: "3", scope: "a" }, - ], - }, - }, - }); - subject.finalize(); - expect(subject.toJSON()).toEqual({ - xdm: { - _experience: { - decisioning: { - propositions: [ - { id: "2", scope: "a" }, - { id: "3", scope: "a" }, - { id: "1", scope: "a" }, - ], - }, - }, - }, - }); - }); -}); diff --git a/test/unit/specs/core/createEventManager.spec.js b/test/unit/specs/core/createEventManager.spec.js deleted file mode 100644 index d609d9389..000000000 --- a/test/unit/specs/core/createEventManager.spec.js +++ /dev/null @@ -1,393 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createEventManager from "../../../../src/core/createEventManager.js"; -import createConfig from "../../../../src/core/config/createConfig.js"; -import { defer } from "../../../../src/utils/index.js"; -import flushPromiseChains from "../../helpers/flushPromiseChains.js"; - -const CANCELLATION_MESSAGE_REGEX = /Event was canceled/; - -describe("createEventManager", () => { - let config; - let logger; - let lifecycle; - let consent; - let event; - let requestPayload; - let request; - let createDataCollectionRequest; - let sendEdgeNetworkRequest; - let applyResponse; - let onRequestFailureForOnBeforeEvent; - let fakeOnRequestFailure; - let eventManager; - beforeEach(() => { - config = createConfig({ - orgId: "ABC123", - onBeforeEventSend: jasmine.createSpy(), - debugEnabled: true, - edgeConfigOverrides: {}, - }); - logger = jasmine.createSpyObj("logger", ["info"]); - lifecycle = jasmine.createSpyObj("lifecycle", { - onBeforeEvent: Promise.resolve(), - onBeforeDataCollectionRequest: Promise.resolve(), - onRequestFailure: Promise.resolve(), - }); - consent = jasmine.createSpyObj("consent", { - awaitConsent: Promise.resolve(), - }); - event = jasmine.createSpyObj("event", { - finalize: undefined, - shouldSend: true, - }); - onRequestFailureForOnBeforeEvent = jasmine.createSpy( - "onRequestFailureForOnBeforeEvent", - ); - fakeOnRequestFailure = ({ onRequestFailure }) => { - onRequestFailure(onRequestFailureForOnBeforeEvent); - return Promise.resolve(); - }; - const createEvent = () => { - return event; - }; - requestPayload = jasmine.createSpyObj("requestPayload", [ - "addEvent", - "mergeConfigOverride", - ]); - const createDataCollectionRequestPayload = () => { - return requestPayload; - }; - request = { - getPayload() { - return requestPayload; - }, - }; - createDataCollectionRequest = jasmine - .createSpy("createDataCollectionRequest") - .and.returnValue(request); - sendEdgeNetworkRequest = jasmine - .createSpy("sendEdgeNetworkRequest") - .and.returnValue(Promise.resolve()); - applyResponse = jasmine - .createSpy("applyResponse") - .and.returnValue(Promise.resolve()); - eventManager = createEventManager({ - config, - logger, - lifecycle, - consent, - createEvent, - createDataCollectionRequestPayload, - createDataCollectionRequest, - sendEdgeNetworkRequest, - applyResponse, - }); - }); - - describe("createEvent", () => { - it("creates an event object", () => { - expect(eventManager.createEvent()).toBe(event); - }); - }); - - describe("sendEvent", () => { - it("creates the payload and adds event and meta", () => { - return eventManager.sendEvent(event).then(() => { - expect(requestPayload.addEvent).toHaveBeenCalledWith(event); - }); - }); - - it("allows other components to access event and pause the lifecycle", () => { - const deferred = defer(); - const options = { - renderDecisions: true, - }; - lifecycle.onBeforeEvent.and.returnValue(deferred.promise); - eventManager.sendEvent(event, options); - return flushPromiseChains() - .then(() => { - expect(lifecycle.onBeforeEvent).toHaveBeenCalledWith({ - event, - renderDecisions: true, - onResponse: jasmine.any(Function), - onRequestFailure: jasmine.any(Function), - }); - expect(consent.awaitConsent).not.toHaveBeenCalled(); - deferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(sendEdgeNetworkRequest).toHaveBeenCalled(); - }); - }); - - it("events call finalize with onBeforeEventSend callback", () => { - return eventManager.sendEvent(event).then(() => { - expect(event.finalize).toHaveBeenCalledWith(config.onBeforeEventSend); - }); - }); - - it("does not send event when event.shouldSend returns false", () => { - lifecycle.onBeforeEvent.and.callFake(fakeOnRequestFailure); - event.shouldSend.and.returnValue(false); - return eventManager.sendEvent(event).then((result) => { - expect(result).toBeUndefined(); - expect(onRequestFailureForOnBeforeEvent).toHaveBeenCalled(); - expect( - onRequestFailureForOnBeforeEvent.calls.mostRecent().args[0].error - .message, - ).toMatch(CANCELLATION_MESSAGE_REGEX); - expect(logger.info).toHaveBeenCalledWith( - jasmine.stringMatching(CANCELLATION_MESSAGE_REGEX), - ); - expect(sendEdgeNetworkRequest).not.toHaveBeenCalled(); - }); - }); - - it("sends event when event.shouldSend returns true", () => { - lifecycle.onBeforeEvent.and.callFake(fakeOnRequestFailure); - return eventManager.sendEvent(event).then((result) => { - expect(result).toBeUndefined(); - expect(onRequestFailureForOnBeforeEvent).not.toHaveBeenCalled(); - expect(sendEdgeNetworkRequest).toHaveBeenCalled(); - }); - }); - - it("throws an error on event finalize and event should not be sent", () => { - lifecycle.onBeforeEvent.and.callFake(fakeOnRequestFailure); - const errorMsg = "Expected Error"; - event.finalize.and.throwError(errorMsg); - return eventManager - .sendEvent(event) - .then(() => { - throw new Error("Should not have resolved."); - }) - .catch((error) => { - expect(error.message).toEqual(errorMsg); - expect(onRequestFailureForOnBeforeEvent).toHaveBeenCalledWith({ - error, - }); - expect(sendEdgeNetworkRequest).not.toHaveBeenCalled(); - }); - }); - - it("allows components and consent to pause the lifecycle", () => { - const onBeforeEventDeferred = defer(); - const consentDeferred = defer(); - lifecycle.onBeforeEvent.and.returnValue(onBeforeEventDeferred.promise); - consent.awaitConsent.and.returnValue(consentDeferred.promise); - eventManager.sendEvent(event); - expect(lifecycle.onBeforeEvent).toHaveBeenCalled(); - return flushPromiseChains() - .then(() => { - expect(consent.awaitConsent).not.toHaveBeenCalled(); - expect(sendEdgeNetworkRequest).not.toHaveBeenCalled(); - onBeforeEventDeferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(consent.awaitConsent).toHaveBeenCalled(); - expect(sendEdgeNetworkRequest).not.toHaveBeenCalled(); - consentDeferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(sendEdgeNetworkRequest).toHaveBeenCalled(); - }); - }); - - it("calls onResponse callbacks on response", () => { - const onResponseForOnBeforeEvent = jasmine.createSpy( - "onResponseForOnBeforeEvent", - ); - lifecycle.onBeforeEvent.and.callFake(({ onResponse }) => { - onResponse(onResponseForOnBeforeEvent); - return Promise.resolve(); - }); - const response = { type: "response" }; - sendEdgeNetworkRequest.and.callFake(({ runOnResponseCallbacks }) => { - runOnResponseCallbacks({ response }); - return Promise.resolve(); - }); - return eventManager.sendEvent(event).then(() => { - expect(onResponseForOnBeforeEvent).toHaveBeenCalledWith({ response }); - }); - }); - - it("calls onRequestFailure callbacks on request failure", () => { - lifecycle.onBeforeEvent.and.callFake(fakeOnRequestFailure); - sendEdgeNetworkRequest.and.callFake( - ({ runOnRequestFailureCallbacks }) => { - const error = new Error(); - runOnRequestFailureCallbacks({ error }); - throw error; - }, - ); - return eventManager - .sendEvent(event) - .then(fail) - .catch((error) => { - expect(onRequestFailureForOnBeforeEvent).toHaveBeenCalledWith({ - error, - }); - }); - }); - - it("sends network request", () => { - return eventManager.sendEvent(event).then(() => { - expect(sendEdgeNetworkRequest).toHaveBeenCalledWith({ - request, - runOnResponseCallbacks: jasmine.any(Function), - runOnRequestFailureCallbacks: jasmine.any(Function), - }); - }); - }); - - it("fails returned promise if request fails", () => { - sendEdgeNetworkRequest.and.returnValue( - Promise.reject(new Error("no connection")), - ); - return expectAsync(eventManager.sendEvent(event)).toBeRejectedWithError( - "no connection", - ); - }); - }); - - describe("applyResponse", () => { - const responseHeaders = { - "x-request-id": "474ec8af-6326-4cb5-952a-4b7dc6be5749", - }; - const responseBody = { - requestId: "474ec8af-6326-4cb5-952a-4b7dc6be5749", - handle: [], - }; - - const options = { - renderDecisions: false, - responseHeaders, - responseBody, - }; - - it("creates the payload and adds event and meta", () => { - return eventManager.applyResponse(event, options).then(() => { - expect(requestPayload.addEvent).toHaveBeenCalledWith(event); - }); - }); - - it("events no not call finalize with onBeforeEventSend callback", () => { - return eventManager.applyResponse(event, options).then(() => { - expect(event.finalize).not.toHaveBeenCalled(); - }); - }); - - it("calls onResponse callbacks", () => { - const onResponseForOnBeforeEvent = jasmine.createSpy( - "onResponseForOnBeforeEvent", - ); - lifecycle.onBeforeEvent.and.callFake(({ onResponse }) => { - onResponse(onResponseForOnBeforeEvent); - return Promise.resolve(); - }); - const response = { type: "response" }; - - applyResponse.and.callFake(({ runOnResponseCallbacks }) => { - runOnResponseCallbacks({ response }); - return Promise.resolve(); - }); - - return eventManager.applyResponse(event, options).then(() => { - expect(onResponseForOnBeforeEvent).toHaveBeenCalledWith({ response }); - }); - }); - - it("applies AEP edge response headers and body and returns result", () => { - const mockResult = { response: "yep" }; - applyResponse.and.returnValue(mockResult); - - return eventManager.applyResponse(event, options).then((result) => { - expect(sendEdgeNetworkRequest).not.toHaveBeenCalled(); - expect(applyResponse).toHaveBeenCalledWith({ - request, - responseHeaders, - responseBody, - runOnResponseCallbacks: jasmine.any(Function), - }); - expect(result).toEqual(mockResult); - }); - }); - - it("includes override configuration, if provided", (done) => { - eventManager - .sendEvent(event, { - edgeConfigOverrides: { - com_adobe_experience_platform: { - event: { - datasetId: "456", - }, - }, - com_adobe_identity: { - idSyncContainerId: "123", - }, - }, - }) - .then(() => { - expect(requestPayload.mergeConfigOverride).toHaveBeenCalledWith({ - com_adobe_identity: { - idSyncContainerId: "123", - }, - com_adobe_experience_platform: { - event: { - datasetId: "456", - }, - }, - }); - done(); - }); - }); - - it("includes global override configuration, if provided", (done) => { - config.edgeConfigOverrides.com_adobe_identity = { - idSyncContainerId: "123", - }; - - eventManager - .sendEvent(event, { - edgeConfigOverrides: {}, - }) - .then(() => { - expect(requestPayload.mergeConfigOverride).toHaveBeenCalledWith({ - com_adobe_identity: { - idSyncContainerId: "123", - }, - }); - done(); - }); - }); - it("includes the datastreamId override, if provided", (done) => { - eventManager - .sendEvent(event, { - edgeConfigOverrides: { - datastreamId: "456", - }, - }) - .then(() => { - expect(createDataCollectionRequest).toHaveBeenCalledWith({ - payload: jasmine.any(Object), - datastreamIdOverride: "456", - }); - done(); - }); - }); - }); -}); diff --git a/test/unit/specs/core/createInstanceFunction.spec.js b/test/unit/specs/core/createInstanceFunction.spec.js deleted file mode 100644 index b92a2d865..000000000 --- a/test/unit/specs/core/createInstanceFunction.spec.js +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createInstanceFunction from "../../../../src/core/createInstanceFunction.js"; -import flushPromiseChains from "../../helpers/flushPromiseChains.js"; - -describe("createInstance", () => { - it("successfully executes command", () => { - const executeCommand = jasmine - .createSpy() - .and.returnValue(Promise.resolve("commandresult")); - const resolve = jasmine.createSpy(); - const reject = jasmine.createSpy(); - const instance = createInstanceFunction(executeCommand); - - instance([resolve, reject, ["event", { foo: "bar" }]]); - - expect(executeCommand).toHaveBeenCalledWith("event", { foo: "bar" }); - - return flushPromiseChains().then(() => { - expect(resolve).toHaveBeenCalledWith("commandresult"); - expect(reject).not.toHaveBeenCalled(); - }); - }); - - it("unsuccessfully execute command", () => { - const executeCommand = jasmine - .createSpy() - .and.returnValue(Promise.reject(new Error("error occurred"))); - const resolve = jasmine.createSpy(); - const reject = jasmine.createSpy(); - const instance = createInstanceFunction(executeCommand); - - instance([resolve, reject, ["event", { foo: "bar" }]]); - - expect(executeCommand).toHaveBeenCalledWith("event", { foo: "bar" }); - - return flushPromiseChains().then(() => { - expect(resolve).not.toHaveBeenCalled(); - expect(reject).toHaveBeenCalledWith(jasmine.any(Error)); - }); - }); -}); diff --git a/test/unit/specs/core/createLifecycle.spec.js b/test/unit/specs/core/createLifecycle.spec.js deleted file mode 100644 index 209ce2eed..000000000 --- a/test/unit/specs/core/createLifecycle.spec.js +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createLifecycle from "../../../../src/core/createLifecycle.js"; - -describe("createLifecycle", () => { - it("exposes all lifecycle methods and they return promises", () => { - const componentRegistry = { - getLifecycleCallbacks() { - return []; - }, - }; - const lifecycle = createLifecycle(componentRegistry); - [ - "onComponentsRegistered", - "onBeforeEvent", - "onBeforeRequest", - "onResponse", - "onRequestFailure", - "onClick", - ].forEach((methodName) => { - expect(lifecycle[methodName]()).toEqual(jasmine.any(Promise)); - }); - }); - - it("calls all callbacks for a given lifecycle method", () => { - const callbacks = [ - jasmine - .createSpy() - .and.returnValue({ returnValue1: "valueFromCallback1" }), - jasmine - .createSpy() - .and.returnValue( - Promise.resolve({ returnValue2: "valueFromCallback2" }), - ), - ]; - const componentRegistry = { - getLifecycleCallbacks(hookName) { - return hookName === "onBeforeEvent" ? callbacks : []; - }, - }; - const lifecycle = createLifecycle(componentRegistry); - return lifecycle.onBeforeEvent("arg1", "arg2").then((result) => { - callbacks.forEach((callback) => { - expect(callback).toHaveBeenCalledWith("arg1", "arg2"); - }); - expect(result).toBeInstanceOf(Array); - expect(result[0].returnValue1).toEqual("valueFromCallback1"); - expect(result[1].returnValue2).toEqual("valueFromCallback2"); - }); - }); - - it("ensures all callbacks for one method are called before any callbacks from a different method", () => { - let lifecycle; - const callbacksByHookName = { - onComponentsRegistered: [ - jasmine.createSpy().and.callFake(() => { - lifecycle.onBeforeEvent(); - }), - jasmine.createSpy(), - ], - onBeforeEvent: [jasmine.createSpy()], - }; - const componentRegistry = { - getLifecycleCallbacks(hookName) { - return callbacksByHookName[hookName] || []; - }, - }; - lifecycle = createLifecycle(componentRegistry); - return lifecycle.onComponentsRegistered().then(() => { - expect( - callbacksByHookName.onComponentsRegistered[0], - ).toHaveBeenCalledBefore(callbacksByHookName.onComponentsRegistered[1]); - expect( - callbacksByHookName.onComponentsRegistered[1], - ).toHaveBeenCalledBefore(callbacksByHookName.onBeforeEvent[0]); - }); - }); -}); diff --git a/test/unit/specs/core/createLogController.spec.js b/test/unit/specs/core/createLogController.spec.js deleted file mode 100644 index e0bed5d18..000000000 --- a/test/unit/specs/core/createLogController.spec.js +++ /dev/null @@ -1,239 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createLogController from "../../../../src/core/createLogController.js"; - -const instanceName = "alloy123"; - -describe("createLogController", () => { - let console; - let locationSearch; - let logger; - let createLogger; - let getDebugEnabled; - let sessionStorage; - let createNamespacedStorage; - let getMonitors; - - beforeEach(() => { - console = { log() {} }; - locationSearch = ""; - logger = { log() {} }; - createLogger = jasmine - .createSpy() - .and.callFake(({ getDebugEnabled: _getDebugEnabled }) => { - getDebugEnabled = _getDebugEnabled; - return logger; - }); - sessionStorage = { - getItem: jasmine.createSpy().and.returnValue(null), - setItem: jasmine.createSpy(), - }; - createNamespacedStorage = jasmine.createSpy().and.returnValue({ - session: sessionStorage, - }); - getMonitors = () => []; - }); - - it("creates a namespaced storage", () => { - createLogController({ - console, - locationSearch, - createLogger, - instanceName, - createNamespacedStorage, - getMonitors, - }); - expect(createNamespacedStorage).toHaveBeenCalledWith("instance.alloy123."); - }); - - it("returns false for getDebugEnabled if storage item is not found", () => { - createLogController({ - console, - locationSearch, - createLogger, - instanceName, - createNamespacedStorage, - getMonitors, - }); - expect(getDebugEnabled()).toBe(false); - }); - - it("returns false for getDebugEnabled if storage item is false", () => { - sessionStorage.getItem = () => "false"; - createLogController({ - console, - locationSearch, - createLogger, - instanceName, - createNamespacedStorage, - getMonitors, - }); - expect(getDebugEnabled()).toBe(false); - }); - - it("returns true for getDebugEnabled if storage item is true", () => { - sessionStorage.getItem = () => "true"; - createLogController({ - console, - locationSearch, - createLogger, - instanceName, - createNamespacedStorage, - getMonitors, - }); - expect(getDebugEnabled()).toBe(true); - }); - - it("persists changes to debugEnabled if not set from config", () => { - const logController = createLogController({ - console, - locationSearch, - createLogger, - instanceName, - createNamespacedStorage, - getMonitors, - }); - - logController.setDebugEnabled(true, { fromConfig: false }); - expect(sessionStorage.setItem).toHaveBeenCalledWith("debug", "true"); - expect(getDebugEnabled()).toBe(true); - }); - - it("does not persist changes to debugEnabled if set from config", () => { - const logController = createLogController({ - console, - locationSearch, - createLogger, - instanceName, - createNamespacedStorage, - getMonitors, - }); - - logController.setDebugEnabled(true, { fromConfig: true }); - expect(sessionStorage.setItem).not.toHaveBeenCalled(); - expect(getDebugEnabled()).toBe(true); - }); - - it("does not change debugEnabled from config if previously changed from something other than config on same page load", () => { - const logController = createLogController({ - console, - locationSearch, - createLogger, - instanceName, - createNamespacedStorage, - getMonitors, - }); - - logController.setDebugEnabled(true, { fromConfig: false }); - logController.setDebugEnabled(false, { fromConfig: true }); - expect(sessionStorage.setItem).toHaveBeenCalledWith("debug", "true"); - expect(sessionStorage.setItem).not.toHaveBeenCalledWith("debug", "false"); - expect(getDebugEnabled()).toBe(true); - }); - - it("does not change debugEnabled from config if previously changed from something other than config on previous page load", () => { - sessionStorage.getItem = () => "true"; - const logController = createLogController({ - console, - locationSearch, - createLogger, - instanceName, - createNamespacedStorage, - getMonitors, - }); - - logController.setDebugEnabled(false, { fromConfig: true }); - expect(sessionStorage.setItem).not.toHaveBeenCalled(); - expect(getDebugEnabled()).toBe(true); - }); - - it("sets debugEnabled to true if query string parameter set to true", () => { - locationSearch = "?alloy_debug=true"; - const logController = createLogController({ - console, - locationSearch, - createLogger, - instanceName, - createNamespacedStorage, - getMonitors, - }); - - // Make sure setting debugEnabled from config can't override it. - logController.setDebugEnabled(false, { fromConfig: true }); - expect(sessionStorage.setItem).toHaveBeenCalledWith("debug", "true"); - expect(sessionStorage.setItem.calls.count()).toBe(1); - expect(getDebugEnabled()).toBe(true); - }); - - it("sets debugEnabled to false if query string parameter set to false", () => { - locationSearch = "?alloy_debug=false"; - const logController = createLogController({ - console, - locationSearch, - createLogger, - instanceName, - createNamespacedStorage, - getMonitors, - }); - - // Make sure setting debugEnabled from config can't override it. - logController.setDebugEnabled(true, { fromConfig: true }); - expect(sessionStorage.setItem).toHaveBeenCalledWith("debug", "false"); - expect(sessionStorage.setItem.calls.count()).toBe(1); - expect(getDebugEnabled()).toBe(false); - }); - - it("creates a logger", () => { - const logController = createLogController({ - console, - locationSearch, - createLogger, - instanceName, - createNamespacedStorage, - getMonitors, - }); - - expect(createLogger).toHaveBeenCalledWith({ - getDebugEnabled, - console, - getMonitors, - context: { instanceName: "alloy123" }, - }); - expect(logController.logger).toBe(logger); - }); - - it("creates a component logger", () => { - const logController = createLogController({ - console, - locationSearch, - createLogger, - instanceName, - createNamespacedStorage, - getMonitors, - }); - const componentLogger = {}; - createLogger.and.returnValue(componentLogger); - const result = logController.createComponentLogger("Personalization"); - - expect(createLogger).toHaveBeenCalledWith({ - getDebugEnabled, - console, - getMonitors, - context: { - instanceName: "alloy123", - componentName: "Personalization", - }, - }); - expect(result).toBe(componentLogger); - }); -}); diff --git a/test/unit/specs/core/createLogger.spec.js b/test/unit/specs/core/createLogger.spec.js deleted file mode 100644 index ec9584ad2..000000000 --- a/test/unit/specs/core/createLogger.spec.js +++ /dev/null @@ -1,253 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createLogger from "../../../../src/core/createLogger.js"; - -const logMethods = ["info", "warn", "error"]; -const monitorMethods = [ - "onInstanceCreated", - "onInstanceConfigured", - "onBeforeCommand", - "onCommandResolved", - "onCommandRejected", - "onBeforeNetworkRequest", - "onNetworkResponse", - "onNetworkError", -]; - -const message = "test message"; - -describe("createLogger", () => { - let logEnabled; - let logger; - - let console; - let getDebugEnabled; - let context; - let getMonitors; - - beforeEach(() => { - console = jasmine.createSpyObj("console", logMethods); - getDebugEnabled = () => logEnabled; - context = { instanceName: "myinstance" }; - getMonitors = () => []; - }); - - const build = () => { - logger = createLogger({ - console, - getDebugEnabled, - context, - getMonitors, - }); - }; - - logMethods.forEach((logMethod) => { - it(`logs message if debugging is enabled and ${logMethod} is called`, () => { - logEnabled = true; - build(); - logger[logMethod](message); - expect(console[logMethod]).toHaveBeenCalledWith("[myinstance]", message); - }); - - it(`does not log a message if debugging is disabled and ${logMethod} is called`, () => { - logEnabled = false; - build(); - logger[logMethod](message); - expect(console[logMethod]).not.toHaveBeenCalled(); - }); - }); - - it("is enabled if logEnabled", () => { - logEnabled = true; - getMonitors = () => []; - build(); - expect(logger.enabled).toBeTrue(); - }); - - it("is enabled if there are monitors", () => { - logEnabled = false; - getMonitors = () => [{}]; - build(); - expect(logger.enabled).toBeTrue(); - }); - - it("is not enabled if no logEnabled and no monitors", () => { - logEnabled = false; - getMonitors = () => []; - build(); - expect(logger.enabled).toBeFalse(); - }); - - it("is ok for a monitor to only implement some of the methods", () => { - getMonitors = () => [{}]; - build(); - logger.logOnInstanceCreated({ b: "2" }); - }); - - monitorMethods.forEach((monitorMethod) => { - it(`calls the monitor method ${monitorMethod}`, () => { - const loggerMethod = `log${monitorMethod - .charAt(0) - .toUpperCase()}${monitorMethod.slice(1)}`; - context = { a: "1" }; - const monitor1 = jasmine.createSpyObj("monitor1", [ - monitorMethod, - "onBeforeLog", - ]); - const monitor2 = jasmine.createSpyObj("monitor2", [monitorMethod]); - getMonitors = () => [monitor1, monitor2]; - build(); - logger[loggerMethod]({ b: "2" }); - expect(monitor1[monitorMethod]).toHaveBeenCalledWith({ a: "1", b: "2" }); - expect(monitor1.onBeforeLog).toHaveBeenCalled(); - expect(monitor2[monitorMethod]).toHaveBeenCalledWith({ a: "1", b: "2" }); - }); - }); - - it("logs onInstanceCreated", () => { - logEnabled = true; - build(); - logger.logOnInstanceCreated({}); - expect(console.info).toHaveBeenCalledWith( - "[myinstance]", - "Instance initialized.", - ); - }); - - it("logs onInstanceConfigured", () => { - logEnabled = true; - build(); - logger.logOnInstanceConfigured({ config: { a: "1" } }); - expect(console.info).toHaveBeenCalledWith( - "[myinstance]", - "Instance configured. Computed configuration:", - { a: "1" }, - ); - }); - - it("logs onBeforeCommand", () => { - logEnabled = true; - build(); - logger.logOnBeforeCommand({ - commandName: "mycommand", - options: { a: "1" }, - }); - expect(console.info).toHaveBeenCalledWith( - "[myinstance]", - "Executing mycommand command. Options:", - { a: "1" }, - ); - }); - - it("logs onCommandResolved", () => { - logEnabled = true; - build(); - logger.logOnCommandResolved({ - commandName: "mycommand", - options: { a: "1" }, - result: { b: "2" }, - }); - expect(console.info).toHaveBeenCalledWith( - "[myinstance]", - "mycommand command resolved. Result:", - { b: "2" }, - ); - }); - - it("logs onCommandRejected", () => { - logEnabled = true; - build(); - const error = Error("myerror"); - logger.logOnCommandRejected({ - commandName: "mycommand", - options: { a: "1" }, - error, - }); - expect(console.error).toHaveBeenCalledWith( - "[myinstance]", - "mycommand command was rejected. Error:", - error, - ); - }); - - it("logs onBeforeNetworkRequest", () => { - logEnabled = true; - build(); - logger.logOnBeforeNetworkRequest({ - requestId: "abc123", - payload: { a: "1" }, - }); - expect(console.info).toHaveBeenCalledWith( - "[myinstance]", - "Request abc123: Sending request.", - { a: "1" }, - ); - }); - - it("logs onNetworkResponse with parsedBody", () => { - logEnabled = true; - build(); - logger.logOnNetworkResponse({ - parsedBody: { a: "1" }, - body: "thebody", - requestId: "abc123", - statusCode: 200, - }); - expect(console.info).toHaveBeenCalledWith( - "[myinstance]", - "Request abc123: Received response with status code 200 and response body:", - { a: "1" }, - ); - }); - - it("logs onNetworkResponse with no parsedBody", () => { - logEnabled = true; - build(); - logger.logOnNetworkResponse({ - body: "thebody", - requestId: "abc123", - statusCode: 200, - }); - expect(console.info).toHaveBeenCalledWith( - "[myinstance]", - "Request abc123: Received response with status code 200 and response body:", - "thebody", - ); - }); - - it("logs onNetworkResponse with no response body", () => { - logEnabled = true; - build(); - logger.logOnNetworkResponse({ - body: "", - requestId: "abc123", - statusCode: 200, - }); - expect(console.info).toHaveBeenCalledWith( - "[myinstance]", - "Request abc123: Received response with status code 200 and no response body.", - "", - ); - }); - - it("logs onNetworkError", () => { - logEnabled = true; - build(); - logger.logOnNetworkError({ requestId: "abc123", error: "myerror" }); - expect(console.error).toHaveBeenCalledWith( - "[myinstance]", - "Request abc123: Network request failed.", - "myerror", - ); - }); -}); diff --git a/test/unit/specs/core/edgeNetwork/handleRequestFailure.spec.js b/test/unit/specs/core/edgeNetwork/handleRequestFailure.spec.js deleted file mode 100644 index 569a289d3..000000000 --- a/test/unit/specs/core/edgeNetwork/handleRequestFailure.spec.js +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import handleRequestFailure from "../../../../../src/core/edgeNetwork/handleRequestFailure.js"; - -describe("handleRequestFailure", () => { - it("works", () => { - const onRequestFailureCallbackAggregator = jasmine.createSpyObj( - "onRequestFailureCallbackAggregator", - ["add", "call"], - ); - - onRequestFailureCallbackAggregator.call.and.returnValue(Promise.resolve()); - - const error = new Error("woopsie"); - - handleRequestFailure(onRequestFailureCallbackAggregator)(error).catch( - (err) => { - expect(onRequestFailureCallbackAggregator.call).toHaveBeenCalledWith({ - error, - }); - expect(err).toEqual(error); - }, - ); - }); -}); diff --git a/test/unit/specs/core/edgeNetwork/injectApplyResponse.spec.js b/test/unit/specs/core/edgeNetwork/injectApplyResponse.spec.js deleted file mode 100644 index 2065b801e..000000000 --- a/test/unit/specs/core/edgeNetwork/injectApplyResponse.spec.js +++ /dev/null @@ -1,250 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import injectApplyResponse from "../../../../../src/core/edgeNetwork/injectApplyResponse.js"; -import assertFunctionCallOrder from "../../../helpers/assertFunctionCallOrder.js"; -import { defer } from "../../../../../src/utils/index.js"; -import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; - -describe("injectApplyResponse", () => { - let lifecycle; - let cookieTransfer; - let processWarningsAndErrors; - let createResponse; - let applyResponse; - let request; - let response; - let responseHeaders; - let responseBody; - - const testApplyResponseSuccess = ({ - runOnResponseCallbacks, - assertLifecycleCall, - }) => { - const successHandler = jasmine.createSpy("successHandler"); - applyResponse({ - request, - responseHeaders, - responseBody, - runOnResponseCallbacks, - }).then(successHandler); - return flushPromiseChains() - .then(() => { - expect(successHandler).not.toHaveBeenCalled(); - assertLifecycleCall(); - expect(lifecycle.onResponse).toHaveBeenCalledWith({ response }); - return flushPromiseChains(); - }) - .then(() => { - expect(successHandler).toHaveBeenCalled(); - }); - }; - - beforeEach(() => { - lifecycle = jasmine.createSpyObj("lifecycle", { - onBeforeRequest: Promise.resolve(), - onRequestFailure: Promise.resolve(), - onResponse: Promise.resolve(), - }); - cookieTransfer = jasmine.createSpyObj("cookieTransfer", [ - "cookiesToPayload", - "responseToCookies", - ]); - - processWarningsAndErrors = jasmine.createSpy("processWarningsAndErrors"); - - request = jasmine.createSpyObj("request", { - getId: "RID123", - getAction: "test-action", - getPayload: { - type: "payload", - }, - getUseIdThirdPartyDomain: false, - getUseSendBeacon: false, - }); - - responseHeaders = { "x-hello": "yep" }; - responseBody = { handle: [] }; - - response = { type: "response" }; - - createResponse = jasmine - .createSpy("createResponse") - .and.returnValue(response); - - applyResponse = injectApplyResponse({ - cookieTransfer, - lifecycle, - createResponse, - processWarningsAndErrors, - }); - }); - - it("calls lifecycle.onResponse, waits for it to complete, then resolves promise", () => { - const deferred = defer(); - lifecycle.onResponse.and.returnValue(deferred.promise); - return testApplyResponseSuccess({ - assertLifecycleCall() { - expect(lifecycle.onResponse).toHaveBeenCalledWith({ response }); - deferred.resolve(); - }, - }); - }); - - it("calls lifecycle.onBeforeRequest's responseCallback callback, waits for it to complete, then resolves promise", () => { - const deferred = defer(); - const responseCallback = jasmine - .createSpy("responseCallback") - .and.returnValue(deferred.promise); - lifecycle.onBeforeRequest.and.callFake(({ onResponse }) => { - onResponse(responseCallback); - return Promise.resolve(); - }); - return testApplyResponseSuccess({ - assertLifecycleCall() { - expect(responseCallback).toHaveBeenCalledWith({ response }); - deferred.resolve(); - }, - }); - }); - - it("calls runOnResponseCallbacks, waits for it to complete, then resolves promise", () => { - const deferred = defer(); - const runOnResponseCallbacks = jasmine - .createSpy("runOnResponseCallbacks") - .and.returnValue(deferred.promise); - return testApplyResponseSuccess({ - runOnResponseCallbacks, - assertLifecycleCall() { - expect(runOnResponseCallbacks).toHaveBeenCalledWith({ response }); - deferred.resolve(); - }, - }); - }); - - it("transfers cookies from response before lifecycle.onResponse", () => { - return applyResponse({ - request, - responseHeaders, - responseBody, - }).then(() => { - expect(cookieTransfer.responseToCookies).toHaveBeenCalledWith(response); - assertFunctionCallOrder([ - cookieTransfer.responseToCookies, - lifecycle.onResponse, - ]); - }); - }); - - it("returns the merged object from lifecycle::onResponse and runOnResponseCallbacks", () => { - const runOnResponseCallbacks = jasmine - .createSpy("runOnResponseCallbacks") - .and.returnValue(Promise.resolve([{ c: 2 }, { h: 9 }, undefined])); - - lifecycle.onResponse.and.returnValue( - Promise.resolve([{ a: 2 }, { b: 8 }, undefined]), - ); - - return expectAsync( - applyResponse({ - request, - responseHeaders, - responseBody, - runOnResponseCallbacks, - }), - ).toBeResolvedTo({ c: 2, h: 9, a: 2, b: 8 }); - }); - - it("returns the merged object from lifecycle::onBeforeRequest & lifecycle::onResponse", () => { - lifecycle.onBeforeRequest.and.callFake(({ onResponse }) => { - onResponse(() => ({ a: 1 })); - onResponse(() => ({ b: 1 })); - onResponse(() => undefined); - return Promise.resolve(); - }); - lifecycle.onResponse.and.returnValue(Promise.resolve([{ c: 2 }])); - return expectAsync( - applyResponse({ request, responseHeaders, responseBody }), - ).toBeResolvedTo({ - a: 1, - b: 1, - c: 2, - }); - }); - - it("creates the response with the correct parameters", () => { - return applyResponse({ - request, - responseHeaders, - responseBody, - }).then(() => { - expect(createResponse).toHaveBeenCalledWith({ - content: responseBody, - getHeader: jasmine.any(Function), - }); - }); - }); - - it("catches when warnings and errors in response", () => { - const error = new Error("whoopsie"); - processWarningsAndErrors = jasmine - .createSpy("processWarningsAndErrors") - .and.throwError(error); - - applyResponse = injectApplyResponse({ - cookieTransfer, - lifecycle, - createResponse, - processWarningsAndErrors, - }); - - const runOnResponseCallbacks = jasmine.createSpy("runOnResponseCallbacks"); - const runOnRequestFailureCallbacks = jasmine.createSpy( - "runOnRequestFailureCallbacks", - ); - - return applyResponse({ - request, - responseHeaders, - responseBody, - runOnResponseCallbacks, - runOnRequestFailureCallbacks, - }) - .catch((err) => { - expect(runOnRequestFailureCallbacks).toHaveBeenCalledWith({ - error, - }); - expect(err).toEqual(error); - }) - .then(() => {}); - }); - - it("returns combined result", () => { - const runOnResponseCallbacks = jasmine - .createSpy("runOnResponseCallbacks") - .and.returnValue(Promise.resolve([{ c: 2 }, { h: 9 }, undefined])); - - return applyResponse({ - request, - responseHeaders, - responseBody, - runOnResponseCallbacks, - }).then((result) => { - expect(runOnResponseCallbacks).toHaveBeenCalledWith({ response }); - assertFunctionCallOrder([ - cookieTransfer.responseToCookies, - lifecycle.onResponse, - ]); - expect(result).not.toBeNull(); - expect(result).toEqual({ c: 2, h: 9 }); - }); - }); -}); diff --git a/test/unit/specs/core/edgeNetwork/injectExtractEdgeInfo.spec.js b/test/unit/specs/core/edgeNetwork/injectExtractEdgeInfo.spec.js deleted file mode 100644 index 8d216abe8..000000000 --- a/test/unit/specs/core/edgeNetwork/injectExtractEdgeInfo.spec.js +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2021 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectExtractEdgeInfo from "../../../../../src/core/edgeNetwork/injectExtractEdgeInfo.js"; - -describe("extractEdgeInfo", () => { - let logger; - let extractEdgeInfo; - beforeEach(() => { - logger = jasmine.createSpyObj("logger", ["warn"]); - extractEdgeInfo = injectExtractEdgeInfo({ logger }); - }); - - [undefined, ""].forEach((input) => { - it(`doesn't log for missing header "${input}"`, () => { - expect(extractEdgeInfo(input)).toEqual({}); - expect(logger.warn).not.toHaveBeenCalled(); - }); - }); - - ["OR2", "VA6;", "VA6;bad"].forEach((input) => { - it(`handles invalid header "${input}"`, () => { - expect(extractEdgeInfo(input)).toEqual({}); - expect(logger.warn).toHaveBeenCalled(); - }); - }); - - [ - ["OR2;9", { regionId: 9 }], - ["OR2;9;other info", { regionId: 9 }], - ["OR2;011", { regionId: 11 }], - ["VA7;-1", { regionId: -1 }], - ].forEach(([input, expectedOutput]) => { - it(`parses "${input}" correctly`, () => { - expect(extractEdgeInfo(input)).toEqual(expectedOutput); - }); - }); -}); diff --git a/test/unit/specs/core/edgeNetwork/injectGetLocationHint.spec.js b/test/unit/specs/core/edgeNetwork/injectGetLocationHint.spec.js deleted file mode 100644 index f08bf6814..000000000 --- a/test/unit/specs/core/edgeNetwork/injectGetLocationHint.spec.js +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import injectGetLocationHint from "../../../../../src/core/edgeNetwork/injectGetLocationHint.js"; - -describe("injectGetLocationHint", () => { - let cookieJar; - let orgId; - let getLocationHint; - - beforeEach(() => { - cookieJar = jasmine.createSpyObj("cookieJar", ["get"]); - orgId = "myorg@AdobeOrg"; - getLocationHint = injectGetLocationHint({ orgId, cookieJar }); - }); - - it("returns the cluster cookie", () => { - cookieJar.get.and.returnValue("mycluster"); - expect(getLocationHint()).toEqual("mycluster"); - }); - - it("generates the correct cookie name", () => { - cookieJar.get.and.returnValue("mycluster"); - getLocationHint(); - expect(cookieJar.get).toHaveBeenCalledOnceWith( - "kndctr_myorg_AdobeOrg_cluster", - ); - }); - - it("doesn't cache the result", () => { - cookieJar.get.and.returnValues("cluster1", "cluster2"); - expect(getLocationHint()).toEqual("cluster1"); - expect(getLocationHint()).toEqual("cluster2"); - }); - - it("returns mbox edge cluster cookie", () => { - cookieJar.get.and.returnValues(undefined, "35"); - expect(getLocationHint()).toEqual("t35"); - }); - - it("returns undefined", () => { - expect(getLocationHint()).toBeUndefined(); - }); -}); diff --git a/test/unit/specs/core/edgeNetwork/injectProcessWarningsAndErrors.spec.js b/test/unit/specs/core/edgeNetwork/injectProcessWarningsAndErrors.spec.js deleted file mode 100644 index 15fdb1a44..000000000 --- a/test/unit/specs/core/edgeNetwork/injectProcessWarningsAndErrors.spec.js +++ /dev/null @@ -1,125 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectProcessWarningsAndErrors from "../../../../../src/core/edgeNetwork/injectProcessWarningsAndErrors.js"; - -describe("processWarningsAndErrors", () => { - let logger; - let processWarningsAndErrors; - - beforeEach(() => { - logger = jasmine.createSpyObj("logger", ["warn", "error"]); - processWarningsAndErrors = injectProcessWarningsAndErrors({ logger }); - }); - - it("throws error if status code is below 2xx", () => { - expect(() => { - processWarningsAndErrors({ - statusCode: 199, - }); - }).toThrowError( - "The server responded with a status code 199 and no response body.", - ); - }); - - it("throws error if status code is above 2xx", () => { - expect(() => { - processWarningsAndErrors({ - statusCode: 300, - }); - }).toThrowError( - "The server responded with a status code 300 and no response body.", - ); - }); - - it("throws error if no parsed body and HTTP status code is not 204", () => { - expect(() => { - processWarningsAndErrors({ - statusCode: 200, - }); - }).toThrowError( - "The server responded with a status code 200 and no response body.", - ); - }); - - it("throws an error if parsed body does not have handle array", () => { - expect(() => { - processWarningsAndErrors({ - statusCode: 200, - body: '{"foo":"bar"}', - parsedBody: { foo: "bar" }, - }); - }).toThrowError( - 'The server responded with a status code 200 and response body:\n{\n "foo": "bar"\n}', - ); - }); - - it("logs warnings", () => { - const warnings = [ - { - title: "General warning", - detail: "General warning detail", - }, - { - title: "Personalization warning", - detail: "Personalization warning detail", - }, - ]; - - processWarningsAndErrors({ - statusCode: 200, - parsedBody: { - handle: [], - warnings, - }, - }); - - expect(logger.warn).toHaveBeenCalledWith( - "The server responded with a warning:", - warnings[0], - ); - expect(logger.warn).toHaveBeenCalledWith( - "The server responded with a warning:", - warnings[1], - ); - }); - - it("logs non-fatal errors", () => { - const errors = [ - { - title: "General warning", - detail: "General warning detail", - }, - { - title: "Personalization warning", - detail: "Personalization warning detail", - }, - ]; - - processWarningsAndErrors({ - statusCode: 207, - parsedBody: { - handle: [], - errors, - }, - }); - - expect(logger.error).toHaveBeenCalledWith( - "The server responded with a non-fatal error:", - errors[0], - ); - expect(logger.error).toHaveBeenCalledWith( - "The server responded with a non-fatal error:", - errors[1], - ); - }); -}); diff --git a/test/unit/specs/core/edgeNetwork/injectSendEdgeNetworkRequest.spec.js b/test/unit/specs/core/edgeNetwork/injectSendEdgeNetworkRequest.spec.js deleted file mode 100644 index 3581c221a..000000000 --- a/test/unit/specs/core/edgeNetwork/injectSendEdgeNetworkRequest.spec.js +++ /dev/null @@ -1,492 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectSendEdgeNetworkRequest from "../../../../../src/core/edgeNetwork/injectSendEdgeNetworkRequest.js"; -import createConfig from "../../../../../src/core/config/createConfig.js"; -import { defer } from "../../../../../src/utils/index.js"; -import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; -import assertFunctionCallOrder from "../../../helpers/assertFunctionCallOrder.js"; - -describe("injectSendEdgeNetworkRequest", () => { - let config; - let logger; - let lifecycle; - let cookieTransfer; - let networkResult; - let sendNetworkRequest; - let response; - let createResponse; - let processWarningsAndErrors; - let getLocationHint; - let getAssuranceValidationTokenParams; - let sendEdgeNetworkRequest; - let payload; - let request; - - // Helper for testing handling of network request failures, particularly - // their interplay with lifecycle hooks. - const testRequestFailureHandling = ({ - runOnRequestFailureCallbacks, - assertLifecycleCall, - }) => { - const error = new Error("no connection"); - sendNetworkRequest.and.returnValue(Promise.reject(error)); - const errorHandler = jasmine.createSpy("errorHandler"); - sendEdgeNetworkRequest({ request, runOnRequestFailureCallbacks }) - .then(fail) - .catch(errorHandler); - return flushPromiseChains() - .then(() => { - expect(errorHandler).not.toHaveBeenCalled(); - assertLifecycleCall(error); - return flushPromiseChains(); - }) - .then(() => { - expect(errorHandler).toHaveBeenCalledWith(error); - }); - }; - - // Helper for testing handling of fatal error responses from the server, particularly - // their interplay with lifecycle hooks. - - const testResponseFailureHandling = ({ - runOnRequestFailureCallbacks, - assertLifecycleCall, - }) => { - const error = new Error("Unexpected response."); - processWarningsAndErrors.and.throwError(error); - const errorHandler = jasmine.createSpy("errorHandler"); - sendEdgeNetworkRequest({ request, runOnRequestFailureCallbacks }) - .then(fail) - .catch(errorHandler); - return flushPromiseChains() - .then(() => { - expect(errorHandler).not.toHaveBeenCalled(); - assertLifecycleCall(error); - return flushPromiseChains(); - }) - .then(() => { - expect(errorHandler).toHaveBeenCalledWith(error); - }); - }; - - // Helper for testing handling of successful network responses, particularly - // their interplay with lifecycle hooks. - const testResponseSuccessHandling = ({ - runOnResponseCallbacks, - assertLifecycleCall, - }) => { - const successHandler = jasmine.createSpy("successHandler"); - sendEdgeNetworkRequest({ request, runOnResponseCallbacks }).then( - successHandler, - ); - return flushPromiseChains() - .then(() => { - expect(successHandler).not.toHaveBeenCalled(); - assertLifecycleCall(); - expect(lifecycle.onResponse).toHaveBeenCalledWith({ response }); - return flushPromiseChains(); - }) - .then(() => { - expect(successHandler).toHaveBeenCalled(); - }); - }; - - beforeEach(() => { - config = createConfig({ - edgeDomain: "edge.example.com", - edgeBasePath: "ee", - datastreamId: "myconfigId", - }); - payload = jasmine.createSpyObj("payload", ["mergeMeta"], { - type: "payload", - }); - request = jasmine.createSpyObj("request", { - getId: "RID123", - getAction: "test-action", - getPayload: payload, - getUseIdThirdPartyDomain: false, - getUseSendBeacon: false, - getDatastreamIdOverride: "", - getEdgeSubPath: "", - }); - logger = jasmine.createSpyObj("logger", ["info"]); - lifecycle = jasmine.createSpyObj("lifecycle", { - onBeforeRequest: Promise.resolve(), - onRequestFailure: Promise.resolve(), - onResponse: Promise.resolve(), - }); - cookieTransfer = jasmine.createSpyObj("cookieTransfer", [ - "cookiesToPayload", - "responseToCookies", - ]); - networkResult = { - parsedBody: { my: "parsedBody" }, - getHeader: () => "myheader", - }; - sendNetworkRequest = jasmine - .createSpy("sendNetworkRequest") - .and.returnValue(Promise.resolve(networkResult)); - response = { type: "response" }; - createResponse = jasmine - .createSpy("createResponse") - .and.returnValue(response); - processWarningsAndErrors = jasmine.createSpy("processWarningsAndErrors"); - getLocationHint = jasmine.createSpy("getLocationHint"); - getAssuranceValidationTokenParams = jasmine - .createSpy("getAssuranceValidationTokenParams") - .and.returnValue(""); - sendEdgeNetworkRequest = injectSendEdgeNetworkRequest({ - config, - logger, - lifecycle, - cookieTransfer, - sendNetworkRequest, - createResponse, - processWarningsAndErrors, - getLocationHint, - getAssuranceValidationTokenParams, - }); - }); - - it("transfers cookies to payload when sending to first-party domain", () => { - return sendEdgeNetworkRequest({ request }).then(() => { - expect(cookieTransfer.cookiesToPayload).toHaveBeenCalledWith( - payload, - "edge.example.com", - ); - }); - }); - - it("transfers cookies to payload when sending to third-party domain", () => { - // Ensure that sendEdgeNetworkRequest waits until after - // lifecycle.onBeforeRequest to determine the endpoint domain. - lifecycle.onBeforeRequest.and.callFake(() => { - request.getUseIdThirdPartyDomain.and.returnValue(true); - return Promise.resolve(); - }); - return sendEdgeNetworkRequest({ request }).then(() => { - expect(cookieTransfer.cookiesToPayload).toHaveBeenCalledWith( - payload, - "adobedc.demdex.net", - ); - }); - }); - - it("sends request to first-party domain", () => { - return sendEdgeNetworkRequest({ request }).then(() => { - expect(sendNetworkRequest).toHaveBeenCalledWith({ - requestId: "RID123", - url: "https://edge.example.com/ee/v1/test-action?configId=myconfigId&requestId=RID123", - payload, - useSendBeacon: false, - }); - }); - }); - - it("sends request to third-party domain", () => { - // Ensure that sendEdgeNetworkRequest waits until after - // lifecycle.onBeforeRequest to determine the endpoint domain. - lifecycle.onBeforeRequest.and.callFake(() => { - request.getUseIdThirdPartyDomain.and.returnValue(true); - return Promise.resolve(); - }); - return sendEdgeNetworkRequest({ request }).then(() => { - expect(sendNetworkRequest).toHaveBeenCalledWith({ - requestId: "RID123", - url: "https://adobedc.demdex.net/ee/v1/test-action?configId=myconfigId&requestId=RID123", - payload, - useSendBeacon: false, - }); - }); - }); - - it("sends request using sendBeacon", () => { - // Ensure that sendEdgeNetworkRequest waits until after - // lifecycle.onBeforeRequest to determine whether to use sendBeacon. - lifecycle.onBeforeRequest.and.callFake(() => { - request.getUseSendBeacon.and.returnValue(true); - return Promise.resolve(); - }); - return sendEdgeNetworkRequest({ request }).then(() => { - expect(sendNetworkRequest).toHaveBeenCalledWith({ - requestId: "RID123", - url: "https://edge.example.com/ee/v1/test-action?configId=myconfigId&requestId=RID123", - payload, - useSendBeacon: true, - }); - }); - }); - - it("calls lifecycle.onBeforeRequest and waits for it to complete before sending request", () => { - const deferred = defer(); - lifecycle.onBeforeRequest.and.returnValue(deferred.promise); - const successHandler = jasmine.createSpy("successHandler"); - sendEdgeNetworkRequest({ request }).then(successHandler); - return flushPromiseChains() - .then(() => { - expect(lifecycle.onBeforeRequest).toHaveBeenCalledWith({ - request, - onResponse: jasmine.any(Function), - onRequestFailure: jasmine.any(Function), - }); - expect(sendNetworkRequest).not.toHaveBeenCalled(); - deferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(successHandler).toHaveBeenCalled(); - }); - }); - - it("when network request fails, calls lifecycle.onRequestFailure, waits for it to complete, then rejects promise", () => { - const deferred = defer(); - lifecycle.onRequestFailure.and.returnValue(deferred.promise); - return testRequestFailureHandling({ - assertLifecycleCall(error) { - expect(lifecycle.onRequestFailure).toHaveBeenCalledWith({ error }); - // We reject this deferred to simulate a component throwing an error - // during the lifecycle.onRequestFailure hook. This tests that the - // promise from sendEdgeNetworkRequest is still rejected with the - // network error rather than the error coming from a component. - deferred.reject(); - }, - }); - }); - - it("when network request fails, calls lifecycle.onBeforeRequest's onRequestFailure callback, waits for it to complete, then rejects promise", () => { - const deferred = defer(); - const requestFailureCallback = jasmine - .createSpy("requestFailureCallback") - .and.returnValue(deferred.promise); - lifecycle.onBeforeRequest.and.callFake(({ onRequestFailure }) => { - onRequestFailure(requestFailureCallback); - return Promise.resolve(); - }); - return testRequestFailureHandling({ - assertLifecycleCall(error) { - expect(requestFailureCallback).toHaveBeenCalledWith({ error }); - // We reject this deferred to simulate a component throwing an error - // during the lifecycle.onBeforeRequest's onRequestFailure callback. - // This tests that the promise from sendEdgeNetworkRequest is still - // rejected with the network error rather than the error coming from - // a component. - deferred.reject(); - }, - }); - }); - - it("when network request fails, calls onRequestFailureCallbacks, waits for it to complete, then rejects promise", () => { - const deferred = defer(); - const runOnRequestFailureCallbacks = jasmine - .createSpy("runOnRequestFailureCallbacks") - .and.returnValue(deferred.promise); - return testRequestFailureHandling({ - runOnRequestFailureCallbacks, - assertLifecycleCall(error) { - expect(runOnRequestFailureCallbacks).toHaveBeenCalledWith({ error }); - // We reject this deferred to simulate a component throwing an error - // during the runOnRequestFailureCallbacks call. This tests that the - // promise from sendEdgeNetworkRequest is still rejected with the - // network error rather than the error coming from a component. - deferred.reject(); - }, - }); - }); - - it("when network response is a failure, calls lifecycle.onRequestFailure, waits for it to complete, then rejects promise", () => { - const deferred = defer(); - lifecycle.onRequestFailure.and.returnValue(deferred.promise); - - return testResponseFailureHandling({ - assertLifecycleCall(error) { - expect(lifecycle.onRequestFailure).toHaveBeenCalledWith({ error }); - // We reject this deferred to simulate a component throwing an error - // during the lifecycle.onRequestFailure hook. This tests that the - // promise from sendEdgeNetworkRequest is still rejected with the - // network error rather than the error coming from a component. - deferred.reject(); - }, - }); - }); - - it("when network response is a failure, calls lifecycle.onBeforeRequest's onRequestFailure callback, waits for it to complete, then rejects promise", () => { - const deferred = defer(); - const requestFailureCallback = jasmine - .createSpy("requestFailureCallback") - .and.returnValue(deferred.promise); - lifecycle.onBeforeRequest.and.callFake(({ onRequestFailure }) => { - onRequestFailure(requestFailureCallback); - return Promise.resolve(); - }); - return testResponseFailureHandling({ - assertLifecycleCall(error) { - expect(requestFailureCallback).toHaveBeenCalledWith({ error }); - // We reject this deferred to simulate a component throwing an error - // during the lifecycle.onBeforeRequest's onRequestFailure callback. - // This tests that the promise from sendEdgeNetworkRequest is still - // rejected with the network error rather than the error coming from - // a component. - deferred.reject(); - }, - }); - }); - - it("when network response is a failure, calls runOnRequestFailureCallbacks, waits for it to complete, then rejects promise", () => { - const deferred = defer(); - const runOnRequestFailureCallbacks = jasmine - .createSpy("runOnRequestFailureCallbacks") - .and.returnValue(deferred.promise); - return testResponseFailureHandling({ - runOnRequestFailureCallbacks, - assertLifecycleCall(error) { - expect(runOnRequestFailureCallbacks).toHaveBeenCalledWith({ error }); - // We reject this deferred to simulate a component throwing an error - // during the runOnRequestFailureCallbacks call. This tests that the - // promise from sendEdgeNetworkRequest is still rejected with the - // network error rather than the error coming from a component. - deferred.reject(); - }, - }); - }); - - it("when network response is a success, calls lifecycle.onResponse, waits for it to complete, then resolves promise", () => { - const deferred = defer(); - lifecycle.onResponse.and.returnValue(deferred.promise); - return testResponseSuccessHandling({ - assertLifecycleCall() { - expect(lifecycle.onResponse).toHaveBeenCalledWith({ response }); - deferred.resolve(); - }, - }); - }); - - it("when network response is a success, calls lifecycle.onBeforeRequest's responseCallback callback, waits for it to complete, then resolves promise", () => { - const deferred = defer(); - const responseCallback = jasmine - .createSpy("responseCallback") - .and.returnValue(deferred.promise); - lifecycle.onBeforeRequest.and.callFake(({ onResponse }) => { - onResponse(responseCallback); - return Promise.resolve(); - }); - return testResponseSuccessHandling({ - assertLifecycleCall() { - expect(responseCallback).toHaveBeenCalledWith({ response }); - deferred.resolve(); - }, - }); - }); - - it("when network response is a success, calls runOnResponseCallbacks, waits for it to complete, then resolves promise", () => { - const deferred = defer(); - const runOnResponseCallbacks = jasmine - .createSpy("runOnResponseCallbacks") - .and.returnValue(deferred.promise); - return testResponseSuccessHandling({ - runOnResponseCallbacks, - assertLifecycleCall() { - expect(runOnResponseCallbacks).toHaveBeenCalledWith({ response }); - deferred.resolve(); - }, - }); - }); - - it("transfers cookies from response before lifecycle.onResponse", () => { - return sendEdgeNetworkRequest({ request }).then(() => { - expect(cookieTransfer.responseToCookies).toHaveBeenCalledWith(response); - assertFunctionCallOrder([ - cookieTransfer.responseToCookies, - lifecycle.onResponse, - ]); - }); - }); - - it("returns the merged object from lifecycle::onResponse and runOnResponseCallbacks", () => { - const runOnResponseCallbacks = jasmine - .createSpy("runOnResponseCallbacks") - .and.returnValue(Promise.resolve([{ c: 2 }, { h: 9 }, undefined])); - - lifecycle.onResponse.and.returnValue( - Promise.resolve([{ a: 2 }, { b: 8 }, undefined]), - ); - - return expectAsync( - sendEdgeNetworkRequest({ request, runOnResponseCallbacks }), - ).toBeResolvedTo({ c: 2, h: 9, a: 2, b: 8 }); - }); - - it("returns the merged object from lifecycle::onBeforeRequest & lifecycle::onResponse", () => { - lifecycle.onBeforeRequest.and.callFake(({ onResponse }) => { - onResponse(() => ({ a: 1 })); - onResponse(() => ({ b: 1 })); - onResponse(() => undefined); - return Promise.resolve(); - }); - lifecycle.onResponse.and.returnValue(Promise.resolve([{ c: 2 }])); - return expectAsync(sendEdgeNetworkRequest({ request })).toBeResolvedTo({ - a: 1, - b: 1, - c: 2, - }); - }); - - it("creates the response with the correct parameters", () => { - return sendEdgeNetworkRequest({ request }).then(() => { - expect(createResponse).toHaveBeenCalledWith({ - content: { my: "parsedBody" }, - getHeader: networkResult.getHeader, - }); - }); - }); - - it("uses the cluster cookie location hint", () => { - getLocationHint.and.returnValue("va6"); - return sendEdgeNetworkRequest({ request }).then(() => { - expect(sendNetworkRequest).toHaveBeenCalledWith({ - requestId: "RID123", - url: "https://edge.example.com/ee/va6/v1/test-action?configId=myconfigId&requestId=RID123", - payload, - useSendBeacon: false, - }); - }); - }); - - it("sets validation token params", () => { - getAssuranceValidationTokenParams.and.returnValue( - "&adobeAepValidationToken=abc-123", - ); - return sendEdgeNetworkRequest({ request }).then(() => { - expect(sendNetworkRequest).toHaveBeenCalledWith({ - requestId: "RID123", - url: "https://edge.example.com/ee/v1/test-action?configId=myconfigId&requestId=RID123&adobeAepValidationToken=abc-123", - payload, - useSendBeacon: false, - }); - }); - }); - - it("respects the datastreamIdOverride", () => { - request.getDatastreamIdOverride.and.returnValue("myconfigIdOverride"); - return sendEdgeNetworkRequest({ request }).then(() => { - expect(payload.mergeMeta).toHaveBeenCalledWith({ - sdkConfig: { datastream: { original: "myconfigId" } }, - }); - expect(sendNetworkRequest).toHaveBeenCalledWith({ - payload, - url: "https://edge.example.com/ee/v1/test-action?configId=myconfigIdOverride&requestId=RID123", - requestId: "RID123", - useSendBeacon: false, - }); - }); - }); -}); diff --git a/test/unit/specs/core/edgeNetwork/mergeLifecycleResponses.spec.js b/test/unit/specs/core/edgeNetwork/mergeLifecycleResponses.spec.js deleted file mode 100644 index 9e333fd84..000000000 --- a/test/unit/specs/core/edgeNetwork/mergeLifecycleResponses.spec.js +++ /dev/null @@ -1,115 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import mergeLifecycleResponses from "../../../../../src/core/edgeNetwork/mergeLifecycleResponses.js"; - -describe("mergeLifecycleResponses", () => { - it("works", () => { - expect( - mergeLifecycleResponses([ - [ - null, - { - destinations: [], - }, - null, - ], - [ - { - propositions: [], - }, - { - decisions: [ - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNTYzMTcwIiwiZXhwZXJpZW5jZUlkIjoiMCJ9", - scope: "superfluous", - items: [ - { - id: "780724", - schema: - "https://ns.adobe.com/personalization/html-content-item", - data: { - id: "1", - format: "text/html", - content: "
    hi
    ", - }, - }, - ], - }, - ], - propositions: [ - { - renderAttempted: true, - id: "AT:eyJhY3Rpdml0eUlkIjoiNTYzMTY5IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", - scope: "__view__", - items: [ - { - id: "0", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "insertAfter", - format: "application/vnd.adobe.target.dom-action", - content: "
    hai
    ", - selector: "HTML > BODY > H3:nth-of-type(1)", - prehidingSelector: "HTML > BODY > H3:nth-of-type(1)", - }, - }, - ], - }, - ], - }, - { - propositions: [], - }, - ], - ]), - ).toEqual({ - destinations: [], - propositions: [ - { - renderAttempted: true, - id: "AT:eyJhY3Rpdml0eUlkIjoiNTYzMTY5IiwiZXhwZXJpZW5jZUlkIjoiMCJ9", - scope: "__view__", - items: [ - { - id: "0", - schema: "https://ns.adobe.com/personalization/dom-action", - data: { - type: "insertAfter", - format: "application/vnd.adobe.target.dom-action", - content: "
    hai
    ", - selector: "HTML > BODY > H3:nth-of-type(1)", - prehidingSelector: "HTML > BODY > H3:nth-of-type(1)", - }, - }, - ], - }, - ], - decisions: [ - { - id: "AT:eyJhY3Rpdml0eUlkIjoiNTYzMTcwIiwiZXhwZXJpZW5jZUlkIjoiMCJ9", - scope: "superfluous", - items: [ - { - id: "780724", - schema: "https://ns.adobe.com/personalization/html-content-item", - data: { - id: "1", - format: "text/html", - content: "
    hi
    ", - }, - }, - ], - }, - ], - }); - }); -}); diff --git a/test/unit/specs/core/initializeComponents.spec.js b/test/unit/specs/core/initializeComponents.spec.js deleted file mode 100644 index 91f484ab9..000000000 --- a/test/unit/specs/core/initializeComponents.spec.js +++ /dev/null @@ -1,105 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import initializeComponents from "../../../../src/core/initializeComponents.js"; - -describe("initializeComponents", () => { - let lifecycle; - let componentRegistry; - let componentByNamespace; - let componentCreators; - let getImmediatelyAvailableTools; - - beforeEach(() => { - lifecycle = { - onComponentsRegistered: jasmine - .createSpy() - .and.returnValue(Promise.resolve()), - }; - componentRegistry = { - register: jasmine.createSpy(), - }; - componentByNamespace = { - Comp1: {}, - Comp2: {}, - }; - const componentCreator1 = jasmine - .createSpy() - .and.returnValue(componentByNamespace.Comp1); - componentCreator1.namespace = "Comp1"; - const componentCreator2 = jasmine - .createSpy() - .and.returnValue(componentByNamespace.Comp2); - componentCreator2.namespace = "Comp2"; - componentCreators = [componentCreator1, componentCreator2]; - - getImmediatelyAvailableTools = (componentName) => { - return { - tool1: { - name: "tool1", - componentName, - }, - tool2: { - name: "tool2", - componentName, - }, - }; - }; - }); - - it("creates and registers components", () => { - const initializeComponentsPromise = initializeComponents({ - componentCreators, - lifecycle, - componentRegistry, - getImmediatelyAvailableTools, - }); - - componentCreators.forEach((componentCreator) => { - const { namespace } = componentCreator; - expect(componentCreator).toHaveBeenCalledWith({ - tool1: { - name: "tool1", - componentName: componentCreator.namespace, - }, - tool2: { - name: "tool2", - componentName: componentCreator.namespace, - }, - }); - expect(componentRegistry.register).toHaveBeenCalledWith( - namespace, - componentByNamespace[namespace], - ); - }); - expect(lifecycle.onComponentsRegistered).toHaveBeenCalledWith({ - lifecycle, - }); - - return initializeComponentsPromise.then((result) => { - expect(result).toBe(componentRegistry); - }); - }); - - it("throws error if component throws error during creation", () => { - componentCreators[1].and.throwError("thrownError"); - - expect(() => { - initializeComponents({ - componentCreators, - lifecycle, - componentRegistry, - getImmediatelyAvailableTools, - }); - }).toThrowError(/\[Comp2\] An error occurred during component creation./); - }); -}); diff --git a/test/unit/specs/core/injectCreateResponse.spec.js b/test/unit/specs/core/injectCreateResponse.spec.js deleted file mode 100644 index 738ccdef6..000000000 --- a/test/unit/specs/core/injectCreateResponse.spec.js +++ /dev/null @@ -1,148 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectCreateResponse from "../../../../src/core/injectCreateResponse.js"; - -const responseContent = { - requestId: 123, - handle: [ - { - type: "type1", - payload: "payload1a", - }, - { - type: "type2", - payload: "payload2a", - }, - { - type: "type1", - payload: "payload1b", - }, - { - type: "type1", - payload: "payload1c", - }, - ], - errors: [ - { - code: "general:100", - message: "General error occurred.", - }, - { - code: "personalization:204", - message: "Personalization error occurred.", - }, - ], - warnings: [ - { - code: "general:101", - message: "General warning.", - }, - { - code: "personalization:205", - message: "Personalization warning.", - }, - ], -}; - -describe("createResponse", () => { - let extractEdgeInfo; - let getHeader; - let createResponse; - let response; - - beforeEach(() => { - extractEdgeInfo = jasmine.createSpy("extractEdgeInfo"); - getHeader = jasmine.createSpy("getHeader"); - createResponse = injectCreateResponse({ extractEdgeInfo }); - response = createResponse({ - content: responseContent, - getHeader, - }); - }); - - describe("getPayloadsByType", () => { - it("handles undefined content", () => { - const emptyResponse = createResponse({ content: undefined }); - expect(emptyResponse.getPayloadsByType("type1")).toEqual([]); - }); - - it("handles content without handle property", () => { - const emptyResponse = createResponse({ content: {} }); - expect(emptyResponse.getPayloadsByType("type1")).toEqual([]); - }); - - it("returns empty array when there are no matching payloads", () => { - expect(response.getPayloadsByType("type3")).toEqual([]); - }); - - it("returns one matching payload as an array", () => { - expect(response.getPayloadsByType("type2")).toEqual(["payload2a"]); - }); - - it("returns three matching payloads", () => { - expect(response.getPayloadsByType("type1")).toEqual([ - "payload1a", - "payload1b", - "payload1c", - ]); - }); - }); - - describe("getErrors", () => { - it("handles undefined content", () => { - const emptyResponse = createResponse({ content: undefined }); - expect(emptyResponse.getErrors()).toEqual([]); - }); - - it("handles content without errors property", () => { - const emptyResponse = createResponse({ content: {} }); - expect(emptyResponse.getErrors()).toEqual([]); - }); - - it("returns errors", () => { - expect(response.getErrors()).toBe(responseContent.errors); - }); - }); - - describe("getWarnings", () => { - it("handles undefined content", () => { - const emptyResponse = createResponse({ content: undefined }); - expect(emptyResponse.getWarnings()).toEqual([]); - }); - - it("handles content without warnings property", () => { - const emptyResponse = createResponse({ content: {} }); - expect(emptyResponse.getWarnings()).toEqual([]); - }); - - it("returns warnings", () => { - expect(response.getWarnings()).toBe(responseContent.warnings); - }); - }); - - describe("getEdge", () => { - it("calls extractEdgeInfo with x-adobe-edge header and returns the result", () => { - extractEdgeInfo.and.returnValue({ regionId: 42 }); - getHeader.and.returnValue("VA6;42"); - expect(response.getEdge()).toEqual({ regionId: 42 }); - expect(extractEdgeInfo).toHaveBeenCalledWith("VA6;42"); - expect(getHeader).toHaveBeenCalledWith("x-adobe-edge"); - }); - }); - - describe("toJSON", () => { - it("returns underlying content object", () => { - expect(response.toJSON()).toBe(responseContent); - }); - }); -}); diff --git a/test/unit/specs/core/injectExecuteCommand.spec.js b/test/unit/specs/core/injectExecuteCommand.spec.js deleted file mode 100644 index 2a60ffcbe..000000000 --- a/test/unit/specs/core/injectExecuteCommand.spec.js +++ /dev/null @@ -1,321 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectExecuteCommand from "../../../../src/core/injectExecuteCommand.js"; -import flushPromiseChains from "../../helpers/flushPromiseChains.js"; - -describe("injectExecuteCommand", () => { - let logger; - let handleError; - - beforeEach(() => { - logger = jasmine.createSpyObj("logger", [ - "info", - "warn", - "error", - "logOnBeforeCommand", - "logOnCommandResolved", - "logOnCommandRejected", - ]); - handleError = jasmine.createSpy().and.callFake((error) => { - throw error; - }); - }); - - it("rejects promise if configure is not the first command executed", () => { - const executeCommand = injectExecuteCommand({ - logger, - handleError, - }); - - return executeCommand("sendEvent") - .then(fail) - .catch((error) => { - expect(error.message).toContain("The library must be configured first"); - expect(handleError).toHaveBeenCalledWith(error, "sendEvent command"); - }); - }); - - it("rejects promise if configure command is executed twice", () => { - const configureCommand = () => Promise.resolve(); - const executeCommand = injectExecuteCommand({ - logger, - configureCommand, - handleError, - }); - - executeCommand("configure"); - return executeCommand("configure") - .then(fail) - .catch((error) => { - expect(error.message).toContain( - "The library has already been configured", - ); - expect(handleError).toHaveBeenCalledWith(error, "configure command"); - }); - }); - - it("rejects promise if command doesn't exist", () => { - const componentRegistry = { - getCommand() {}, - getCommandNames() { - return ["genuine"]; - }, - }; - const configureCommand = () => Promise.resolve(componentRegistry); - const executeCommand = injectExecuteCommand({ - logger, - configureCommand, - handleError, - }); - executeCommand("configure"); - return executeCommand("bogus") - .then(fail) - .catch((error) => { - expect(error.message).toBe( - "The bogus command does not exist. List of available commands: configure, setDebug, genuine.", - ); - expect(handleError).toHaveBeenCalledWith(error, "bogus command"); - }); - }); - - it("never resolves/rejects promise to any other command after configure fails", () => { - const configureError = new Error("Test configure command failed"); - const configureCommand = () => Promise.reject(configureError); - const executeCommand = injectExecuteCommand({ - logger, - configureCommand, - handleError, - }); - - const configureRejectedSpy = jasmine.createSpy("configureRejectedSpy"); - executeCommand("configure").then(fail).catch(configureRejectedSpy); - const sendEventResolvedSpy = jasmine.createSpy("sendEventResolvedSpy"); - const sendEventRejectedSpy = jasmine.createSpy("sendEventRejectedSpy"); - executeCommand("sendEvent") - .then(sendEventResolvedSpy) - .catch(sendEventRejectedSpy); - return flushPromiseChains().then(() => { - expect(configureRejectedSpy).toHaveBeenCalledWith(configureError); - expect(logger.warn).toHaveBeenCalledWith( - "An error during configuration is preventing the sendEvent command from executing.", - ); - expect(sendEventResolvedSpy).not.toHaveBeenCalled(); - expect(sendEventRejectedSpy).not.toHaveBeenCalled(); - }); - }); - - it("reject promise if component command throws error", () => { - const runCommandSpy = jasmine - .createSpy() - .and.throwError(new Error("Unexpected error")); - const testCommand = { - run: runCommandSpy, - }; - const componentRegistry = { - getCommand: () => testCommand, - getCommandNames() { - return ["test"]; - }, - }; - const configureCommand = () => Promise.resolve(componentRegistry); - const executeCommand = injectExecuteCommand({ - logger, - configureCommand, - handleError, - validateCommandOptions: (options) => options, - }); - executeCommand("configure"); - return executeCommand("test", {}).catch(() => { - expect(handleError).toHaveBeenCalledWith( - new Error("Unexpected error"), - "test command", - ); - }); - }); - - it("executes component commands", () => { - const validateCommandOptionsSpy = jasmine - .createSpy() - .and.returnValues( - "with-result-post-validation-options", - "without-result-post-validation-options", - ); - const testCommandWithResult = jasmine.createSpyObj( - "testCommandWithResult", - { - run: { foo: "bar" }, - }, - ); - const testCommandWithoutResult = jasmine.createSpyObj( - "testCommandWithoutResult", - { - run: undefined, - }, - ); - const componentRegistry = { - getCommand: jasmine - .createSpy("getCommand") - .and.returnValues(testCommandWithResult, testCommandWithoutResult), - getCommandNames() { - return ["testCommandWithResult", "testCommandWithoutResult"]; - }, - }; - const configureCommand = () => Promise.resolve(componentRegistry); - const executeCommand = injectExecuteCommand({ - logger, - configureCommand, - handleError, - validateCommandOptions: validateCommandOptionsSpy, - }); - executeCommand("configure"); - return Promise.all([ - executeCommand( - "testCommandWithResult", - "with-result-pre-validation-options", - ), - executeCommand( - "testCommandWithoutResult", - "without-result-pre-validation-options", - ), - ]).then((results) => { - expect(results[0]).toEqual({ foo: "bar" }); - expect(results[1]).toEqual({}); - expect(validateCommandOptionsSpy).toHaveBeenCalledWith({ - command: testCommandWithResult, - options: "with-result-pre-validation-options", - }); - expect(validateCommandOptionsSpy).toHaveBeenCalledWith({ - command: testCommandWithoutResult, - options: "without-result-pre-validation-options", - }); - expect(testCommandWithResult.run).toHaveBeenCalledWith( - "with-result-post-validation-options", - ); - expect(testCommandWithoutResult.run).toHaveBeenCalledWith( - "without-result-post-validation-options", - ); - }); - }); - - it("executes the core commands", () => { - const componentRegistry = { - getCommand() {}, - }; - const configureCommand = jasmine - .createSpy() - .and.returnValue(Promise.resolve(componentRegistry)); - const setDebugCommand = jasmine.createSpy(); - const validateCommandOptions = jasmine - .createSpy() - .and.returnValue({ enabled: true }); - - const executeCommand = injectExecuteCommand({ - logger, - configureCommand, - setDebugCommand, - handleError, - validateCommandOptions, - }); - - return Promise.all([ - executeCommand("configure", { foo: "bar" }), - executeCommand("setDebug", { baz: "qux" }), - ]).then(([configureResult, setDebugResult]) => { - expect(configureCommand).toHaveBeenCalledWith({ foo: "bar" }); - expect(setDebugCommand).toHaveBeenCalledWith({ enabled: true }); - expect(configureResult).toEqual({}); - expect(setDebugResult).toEqual({}); - }); - }); - - const buildWithTestCommand = (runCommand) => { - const testCommand = { - run: runCommand, - }; - const componentRegistry = { - getCommand: () => testCommand, - getCommandNames() { - return ["test"]; - }, - }; - const configureCommand = () => Promise.resolve(componentRegistry); - return injectExecuteCommand({ - logger, - configureCommand, - handleError, - validateCommandOptions: (options) => options, - }); - }; - - it("logs onBeforeCommand", () => { - const executeCommand = buildWithTestCommand(() => { - expect(logger.logOnBeforeCommand).toHaveBeenCalledWith({ - commandName: "test", - options: { my: "options" }, - }); - }); - executeCommand("configure"); - return executeCommand("test", { my: "options" }); - }); - - it("logs onCommandResolved", () => { - const executeCommand = buildWithTestCommand(() => { - expect(logger.logOnCommandResolved).not.toHaveBeenCalled(); - return { go: "bananas" }; - }); - executeCommand("configure"); - return executeCommand("test", { my: "options" }).then((result) => { - expect(result).toEqual({ go: "bananas" }); - expect(logger.logOnCommandResolved).toHaveBeenCalledWith({ - commandName: "test", - options: { my: "options" }, - result: { go: "bananas" }, - }); - }); - }); - - it("logs onCommandRejected", () => { - const myerror = Error("bananas"); - const executeCommand = buildWithTestCommand(() => { - expect(logger.logOnCommandRejected).not.toHaveBeenCalled(); - throw myerror; - }); - executeCommand("configure"); - return executeCommand("test", { my: "options" }).catch((error) => { - expect(error).toEqual(myerror); - expect(logger.logOnCommandRejected).toHaveBeenCalledWith({ - commandName: "test", - options: { my: "options" }, - error: myerror, - }); - }); - }); - - it("logs onCommandResolved when handleError swallows the error", () => { - const myerror = Error("bananas"); - handleError.and.returnValue({}); - const executeCommand = buildWithTestCommand(() => { - throw myerror; - }); - executeCommand("configure"); - return executeCommand("test", { my: "options" }).then((result) => { - expect(result).toEqual({}); - expect(logger.logOnCommandResolved).toHaveBeenCalledWith({ - commandName: "test", - options: { my: "options" }, - result: {}, - }); - expect(logger.logOnCommandRejected).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/test/unit/specs/core/injectHandleError.spec.js b/test/unit/specs/core/injectHandleError.spec.js deleted file mode 100644 index a39b685e5..000000000 --- a/test/unit/specs/core/injectHandleError.spec.js +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectHandleError from "../../../../src/core/injectHandleError.js"; - -const expectedMessage = "[testinstanceName] Bad thing happened."; - -describe("injectHandleError", () => { - it("converts non-error to error and throws", () => { - const handleError = injectHandleError({ - errorPrefix: "[testinstanceName]", - }); - - expect(() => { - handleError("Bad thing happened.", "myoperation"); - }).toThrowError(expectedMessage); - }); - - it("rethrows error with instanceName prepended", () => { - const handleError = injectHandleError({ - errorPrefix: "[testinstanceName]", - }); - - expect(() => { - handleError(new Error("Bad thing happened."), "myoperation"); - }).toThrowError(expectedMessage); - }); - - it("logs an error and returns empty object if error is due to declined consent", () => { - const logger = jasmine.createSpyObj("logger", ["warn"]); - const handleError = injectHandleError({ - errorPrefix: "[testinstanceName]", - logger, - }); - - const error = new Error("User declined consent."); - error.code = "declinedConsent"; - expect(handleError(error, "myoperation")).toEqual({}); - expect(logger.warn).toHaveBeenCalledWith( - "The myoperation could not fully complete. User declined consent.", - ); - }); -}); diff --git a/test/unit/specs/core/injectShouldTransferCookie.spec.js b/test/unit/specs/core/injectShouldTransferCookie.spec.js deleted file mode 100644 index fbbdb538e..000000000 --- a/test/unit/specs/core/injectShouldTransferCookie.spec.js +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectShouldTransferCookie from "../../../../src/core/injectShouldTransferCookie.js"; - -describe("shouldTransferCookie", () => { - let targetMigrationEnabled; - let orgId; - let shouldTransferCookie; - - beforeEach(() => { - targetMigrationEnabled = false; - orgId = "ABC@CustomOrg"; - shouldTransferCookie = null; - }); - const build = () => { - shouldTransferCookie = injectShouldTransferCookie({ - targetMigrationEnabled, - orgId, - }); - }; - - it("returns true if it's at_qa_mode cookie", () => { - build(); - expect(shouldTransferCookie("at_qa_mode")).toBeTrue(); - }); - - it("returns true if it's mbox cookie and targetMigrationEnabled=true", () => { - targetMigrationEnabled = true; - build(); - expect(shouldTransferCookie("mbox")).toBeTrue(); - }); - - it("returns false if it's mbox cookie and targetMigrationEnabled=false", () => { - build(); - expect(shouldTransferCookie("mbox")).toBeFalse(); - }); - - it("returns false if it's not a legacy cookie name", () => { - targetMigrationEnabled = true; - build(); - expect(shouldTransferCookie("foo")).toBeFalse(); - }); - - it("returns true for kndctr cookies", () => { - build(); - expect(shouldTransferCookie("kndctr_ABC_CustomOrg_mynewcookie")).toBeTrue(); - }); -}); diff --git a/test/unit/specs/core/network/getRequestRetryDelay.spec.js b/test/unit/specs/core/network/getRequestRetryDelay.spec.js deleted file mode 100644 index 9ab799f28..000000000 --- a/test/unit/specs/core/network/getRequestRetryDelay.spec.js +++ /dev/null @@ -1,104 +0,0 @@ -/* -Copyright 2021 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getRequestRetryDelay from "../../../../../src/core/network/getRequestRetryDelay.js"; - -describe("getRequestRetryDelay", () => { - beforeEach(() => { - jasmine.clock().install(); - jasmine.clock().mockDate(new Date(0)); - }); - - afterEach(() => { - jasmine.clock().uninstall(); - }); - - it("returns value derived from retry-after header in delay-seconds format", () => { - const response = jasmine.createSpyObj("request", { - getHeader: "123", - }); - const delay = getRequestRetryDelay({ - response, - retriesAttempted: 0, - }); - expect(delay).toBe(123000); - expect(response.getHeader).toHaveBeenCalledWith("Retry-After"); - }); - - it("returns value derived from retry-after header in http-date format in the future", () => { - const response = jasmine.createSpyObj("request", { - getHeader: "Thu, 01 Jan 1970 00:00:09 GMT", - }); - const delay = getRequestRetryDelay({ - response, - retriesAttempted: 0, - }); - expect(delay).toBe(9000); - expect(response.getHeader).toHaveBeenCalledWith("Retry-After"); - }); - - it("returns value derived from retry-after header exists in http-date format in the past", () => { - const response = jasmine.createSpyObj("request", { - getHeader: "Thu, 01 Jan 1969 11:59:51 GMT", - }); - const delay = getRequestRetryDelay({ - response, - retriesAttempted: 0, - }); - expect(delay).toBe(0); - expect(response.getHeader).toHaveBeenCalledWith("Retry-After"); - }); - - const retriesAttemptedScenarios = [ - { - retriesAttempted: 0, - lowDelay: 700, - highDelay: 1300, - }, - { - retriesAttempted: 1, - lowDelay: 1400, - highDelay: 2600, - }, - { - retriesAttempted: 2, - lowDelay: 2100, - highDelay: 3900, - }, - { - retriesAttempted: 3, - lowDelay: 2800, - highDelay: 5200, - }, - ]; - - retriesAttemptedScenarios.forEach((scenario) => { - it(`returns randomized, incremental value based on ${scenario.retriesAttempted} retries attempted`, () => { - spyOn(Math, "random"); - const response = jasmine.createSpyObj("request", ["getHeader"]); - - Math.random.and.returnValue(0); - const lowDelay = getRequestRetryDelay({ - response, - retriesAttempted: scenario.retriesAttempted, - }); - expect(lowDelay).toBe(scenario.lowDelay); - - Math.random.and.returnValue(0.999999999999); - const highDelay = getRequestRetryDelay({ - response, - retriesAttempted: scenario.retriesAttempted, - }); - expect(highDelay).toBe(scenario.highDelay); - }); - }); -}); diff --git a/test/unit/specs/core/network/injectSendNetworkRequest.spec.js b/test/unit/specs/core/network/injectSendNetworkRequest.spec.js deleted file mode 100644 index 3ee8c0529..000000000 --- a/test/unit/specs/core/network/injectSendNetworkRequest.spec.js +++ /dev/null @@ -1,246 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectSendNetworkRequest from "../../../../../src/core/network/injectSendNetworkRequest.js"; -import flushPromiseChains from "../../../helpers/flushPromiseChains.js"; - -describe("injectSendNetworkRequest", () => { - const url = "https://example.com"; - const payload = { - a: "b", - }; - const payloadJson = JSON.stringify(payload); - const requestId = "RID123"; - - let logger; - - const responseBody = { requestId: "myrequestid", handle: [] }; - const responseBodyJson = JSON.stringify(responseBody); - - let sendNetworkRequest; - let sendFetchRequest; - let sendBeaconRequest; - let isRequestRetryable; - let getRequestRetryDelay; - let getHeader; - - beforeEach(() => { - jasmine.clock().install(); - logger = jasmine.createSpyObj("logger", [ - "logOnBeforeNetworkRequest", - "logOnNetworkResponse", - "logOnNetworkError", - ]); - logger.enabled = true; - getHeader = jasmine.createSpy("getHeader"); - sendFetchRequest = jasmine.createSpy().and.returnValue( - Promise.resolve({ - statusCode: 200, - body: responseBodyJson, - getHeader, - }), - ); - sendBeaconRequest = jasmine.createSpy().and.returnValue( - Promise.resolve({ - statusCode: 204, - body: "", - getHeader: () => undefined, - }), - ); - isRequestRetryable = jasmine - .createSpy("isRequestRetryable") - .and.returnValue(false); - getRequestRetryDelay = jasmine - .createSpy("getRequestRetryDelay") - .and.returnValue(1000); - - sendNetworkRequest = injectSendNetworkRequest({ - logger, - sendFetchRequest, - sendBeaconRequest, - isRequestRetryable, - getRequestRetryDelay, - }); - }); - - afterEach(() => { - jasmine.clock().uninstall(); - }); - - it("sends the request", () => { - return sendNetworkRequest({ - requestId, - payload, - url, - useSendBeacon: false, - }).then(() => { - expect(logger.logOnBeforeNetworkRequest).toHaveBeenCalledWith({ - requestId, - url, - payload, - }); - expect(sendFetchRequest).toHaveBeenCalledWith(url, payloadJson); - }); - }); - - it("handles a response with a JSON body", () => { - return sendNetworkRequest({ - payload, - url, - requestId, - }).then((response) => { - expect(logger.logOnNetworkResponse).toHaveBeenCalledWith({ - requestId, - url, - payload, - statusCode: 200, - body: responseBodyJson, - parsedBody: responseBody, - retriesAttempted: 0, - getHeader, - }); - expect(response).toEqual({ - statusCode: 200, - body: responseBodyJson, - parsedBody: responseBody, - getHeader, - }); - }); - }); - - it("handles a response with a non-JSON body", () => { - sendFetchRequest.and.returnValue( - Promise.resolve({ - statusCode: 200, - body: "non-JSON body", - getHeader, - }), - ); - return sendNetworkRequest({ - payload, - url, - requestId, - }).then((response) => { - expect(logger.logOnNetworkResponse).toHaveBeenCalledWith({ - requestId, - url, - payload: { - a: "b", - }, - statusCode: 200, - body: "non-JSON body", - parsedBody: undefined, - retriesAttempted: 0, - getHeader, - }); - expect(response).toEqual({ - statusCode: 200, - body: "non-JSON body", - parsedBody: undefined, - getHeader, - }); - }); - }); - - it("handles a response with an empty body", () => { - sendFetchRequest.and.returnValue( - Promise.resolve({ - statusCode: 200, - body: "", - getHeader, - }), - ); - return sendNetworkRequest({ - payload, - url, - requestId, - }).then((response) => { - expect(logger.logOnNetworkResponse).toHaveBeenCalledWith({ - requestId, - url, - payload: { - a: "b", - }, - statusCode: 200, - body: "", - parsedBody: undefined, - retriesAttempted: 0, - getHeader, - }); - expect(response).toEqual({ - statusCode: 200, - body: "", - parsedBody: undefined, - getHeader, - }); - }); - }); - - it("rejects the promise when a network error occurs", () => { - sendFetchRequest.and.returnValue(Promise.reject(new Error("networkerror"))); - return sendNetworkRequest({ - payload, - url, - requestId, - }) - .then(fail) - .catch((error) => { - expect(error.message).toEqual( - "Network request failed.\nCaused by: networkerror", - ); - }); - }); - - it("resolves the promise for successful status and valid json", () => { - return sendNetworkRequest({ - payload, - url, - requestId, - }).then((response) => { - expect(response).toEqual({ - statusCode: 200, - body: responseBodyJson, - parsedBody: responseBody, - getHeader, - }); - }); - }); - - it(`retries requests until request is no longer retryable`, () => { - isRequestRetryable.and.returnValues(true, true, false); - sendNetworkRequest({ - payload, - url, - requestId, - }); - - expect(sendFetchRequest).toHaveBeenCalledTimes(1); - return flushPromiseChains() - .then(() => { - expect(sendFetchRequest).toHaveBeenCalledTimes(1); - jasmine.clock().tick(1000); - expect(sendFetchRequest).toHaveBeenCalledTimes(2); - return flushPromiseChains(); - }) - .then(() => { - expect(sendFetchRequest).toHaveBeenCalledTimes(2); - jasmine.clock().tick(1000); - expect(sendFetchRequest).toHaveBeenCalledTimes(3); - return flushPromiseChains(); - }) - .then(() => { - expect(sendFetchRequest).toHaveBeenCalledTimes(3); - jasmine.clock().tick(1000); - expect(sendFetchRequest).toHaveBeenCalledTimes(3); - }); - }); -}); diff --git a/test/unit/specs/core/network/isRequestRetryable.spec.js b/test/unit/specs/core/network/isRequestRetryable.spec.js deleted file mode 100644 index 93ae3de52..000000000 --- a/test/unit/specs/core/network/isRequestRetryable.spec.js +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isRequestRetryable from "../../../../../src/core/network/isRequestRetryable.js"; - -describe("isRequestRetryable", () => { - [429, 503, 502, 504].forEach((statusCode) => { - it(`returns true for ${statusCode} and retries attempted is under the limit`, () => { - const isRetryable = isRequestRetryable({ - response: { - statusCode, - }, - retriesAttempted: 2, - }); - expect(isRetryable).toBeTrue(); - }); - - it(`returns false for ${statusCode} and retries attempted is over the limit`, () => { - const isRetryable = isRequestRetryable({ - response: { - statusCode, - }, - retriesAttempted: 3, - }); - expect(isRetryable).toBeFalse(); - }); - }); - - [100, 199, 200, 299, 300, 399, 400, 499, 500, 599].forEach((statusCode) => { - it(`returns false for ${statusCode}`, () => { - const isRetryable = isRequestRetryable({ - response: { - statusCode, - }, - retriesAttempted: 0, - }); - expect(isRetryable).toBeFalse(); - }); - }); -}); diff --git a/test/unit/specs/core/network/requestMethods/injectSendBeaconRequest.spec.js b/test/unit/specs/core/network/requestMethods/injectSendBeaconRequest.spec.js deleted file mode 100644 index 7b347f01d..000000000 --- a/test/unit/specs/core/network/requestMethods/injectSendBeaconRequest.spec.js +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectSendBeaconRequest from "../../../../../../src/core/network/requestMethods/injectSendBeaconRequest.js"; - -// When running these tests in IE 11, they fail because IE doesn't like the -// way the blob is constructed (see -// https://github.com/bpampuch/pdfmake/pull/297/files for a workaround). -// Fortunately, if navigator.sendBeacon doesn't exist (IE 11), injectSendBeaconRequest -// should never be used (see injectNetworkStrategy.js), so we can skip -// these tests altogether. -const guardForSendBeaconAvailability = (spec) => { - return window.navigator.sendBeacon - ? spec - : () => pending("No sendBeacon API available."); -}; - -describe("injectSendBeaconRequest", () => { - it( - "falls back to sendFetchRequest if sendBeacon fails", - guardForSendBeaconAvailability(() => { - const sendBeacon = jasmine.createSpy().and.returnValue(false); - const sendFetchRequestPromise = Promise.resolve(); - const sendFetchRequest = jasmine - .createSpy() - .and.returnValue(sendFetchRequestPromise); - const logger = jasmine.createSpyObj(["info"]); - const sendBeaconRequest = injectSendBeaconRequest({ - sendBeacon, - sendFetchRequest, - logger, - }); - const body = { a: "b" }; - const result = sendBeaconRequest("https://example.com/endpoint", body); - expect(sendBeacon).toHaveBeenCalledWith( - "https://example.com/endpoint", - jasmine.any(Object), - ); - expect(sendFetchRequest).toHaveBeenCalledWith( - "https://example.com/endpoint", - body, - ); - expect(logger.info).toHaveBeenCalledWith( - jasmine.stringMatching("falling back to"), - ); - expect(result).toBe(sendFetchRequestPromise); - }), - ); - - it( - "does not fall back to sendFetchRequest if sendBeacon succeeds", - guardForSendBeaconAvailability(() => { - const sendBeacon = jasmine.createSpy().and.returnValue(true); - const body = { a: "b" }; - const sendFetchRequest = jasmine.createSpy(); - const sendBeaconRequest = injectSendBeaconRequest({ - sendBeacon, - sendFetchRequest, - }); - // eslint-disable-next-line consistent-return - return sendBeaconRequest("https://example.com/endpoint", body).then( - (result) => { - expect(sendFetchRequest).not.toHaveBeenCalled(); - expect(result.statusCode).toBe(204); - expect(result.getHeader("Content-Type")).toBeNull(); - expect(result.body).toBe(""); - }, - ); - }), - ); -}); diff --git a/test/unit/specs/core/network/requestMethods/injectSendFetchRequest.spec.js b/test/unit/specs/core/network/requestMethods/injectSendFetchRequest.spec.js deleted file mode 100644 index 12a96c191..000000000 --- a/test/unit/specs/core/network/requestMethods/injectSendFetchRequest.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectSendFetchRequest from "../../../../../../src/core/network/requestMethods/injectSendFetchRequest.js"; - -describe("injectSendFetchRequest", () => { - it("resolves returned promise upon network success", () => { - const fetchResult = { - status: 999, - headers: jasmine.createSpyObj("headers", { - get: "headervalue", - }), - text() { - return Promise.resolve("content"); - }, - }; - const fetch = jasmine - .createSpy() - .and.returnValue(Promise.resolve(fetchResult)); - const sendFetchRequest = injectSendFetchRequest({ fetch }); - return sendFetchRequest("http://example.com/endpoint", { a: "b" }).then( - (result) => { - expect(result.statusCode).toBe(999); - expect(result.getHeader("Content-Type")).toBe("headervalue"); - expect(result.body).toBe("content"); - expect(fetchResult.headers.get).toHaveBeenCalledWith("Content-Type"); - }, - ); - }); - - it("rejects returned promise upon network failure", () => { - const fetch = jasmine - .createSpy() - .and.returnValue(Promise.reject(new Error("No connection"))); - const sendFetchRequest = injectSendFetchRequest({ fetch }); - return sendFetchRequest("http://example.com/endpoint", { a: "b" }) - .then(fail) - .catch((error) => { - expect(error.message).toBe("No connection"); - }); - }); -}); diff --git a/test/unit/specs/core/requiredComponentCreators.spec.js b/test/unit/specs/core/requiredComponentCreators.spec.js deleted file mode 100644 index d3e281e00..000000000 --- a/test/unit/specs/core/requiredComponentCreators.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import * as requiredComponentCreators from "../../../../src/core/requiredComponentCreators.js"; - -describe("requiredComponentCreators", () => { - it("is an object of component creators", () => { - const c = Object.keys(requiredComponentCreators).reduce((acc, key) => { - acc.push(requiredComponentCreators[key]); - return acc; - }, []); - - expect(c).toEqual(jasmine.any(Array)); - - c.forEach((componentCreator) => { - expect(componentCreator).toEqual(jasmine.any(Function)); - expect(componentCreator.namespace).toEqual(jasmine.any(String)); - - if (componentCreator.configValidators) { - // should export a validator function - expect(componentCreator.configValidators).toEqual( - jasmine.any(Function), - ); - } - }); - }); -}); diff --git a/test/unit/specs/core/validateCommandOptions.spec.js b/test/unit/specs/core/validateCommandOptions.spec.js deleted file mode 100644 index cb2c43400..000000000 --- a/test/unit/specs/core/validateCommandOptions.spec.js +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import validateCommandOptions from "../../../../src/core/validateCommandOptions.js"; - -describe("validateCommandOptions", () => { - let command; - let options; - - beforeEach(() => { - options = {}; - command = { - commandName: "TEST", - run: () => {}, - }; - }); - - it("supports commands not implementing command options validation.", () => { - expect(() => { - validateCommandOptions({ command, options }); - }).not.toThrowError(); - }); - it("should throw exception if command options validator throws exception.", () => { - command.optionsValidator = () => { - throw new Error("Invalid Options"); - }; - expect(() => { - validateCommandOptions({ command, options }); - }).toThrowError(); - }); - it("should include custom documentation URI in error message if provided by command options validator.", () => { - command.optionsValidator = () => { - throw new Error("Invalid Options"); - }; - command.documentationUri = "https://example.com"; - let errorMessage; - try { - validateCommandOptions({ command, options }); - } catch (e) { - errorMessage = e.message; - } - expect(errorMessage).toMatch(/example.com/gm); - }); -}); diff --git a/test/unit/specs/karmaEntry.spec.cjs b/test/unit/specs/karmaEntry.spec.cjs deleted file mode 100644 index 5b0447e3b..000000000 --- a/test/unit/specs/karmaEntry.spec.cjs +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -// Without this entry file, Karma + Rollup would create a separate build -// for each spec file, resulting in slow tests runs, high memory usage, -// and file system errors. - -// eslint-disable-next-line import/no-unresolved,import/extensions -import "./*/**/*.spec.js"; diff --git a/test/unit/specs/utils/assignConcatArrayValues.spec.js b/test/unit/specs/utils/assignConcatArrayValues.spec.js deleted file mode 100644 index 932af9a69..000000000 --- a/test/unit/specs/utils/assignConcatArrayValues.spec.js +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import assignConcatArrayValues from "../../../../src/utils/assignConcatArrayValues.js"; - -describe("assignConcatArrayValues", () => { - it("throws an error if no arguments are passed", () => { - expect(() => assignConcatArrayValues()).toThrowError(); - }); - - it("returns an empty array if an empty array is passed", () => { - const obj = []; - expect(assignConcatArrayValues(obj)).toBe(obj); - }); - - it("returns the first object if only one argument is passed", () => { - const obj = {}; - expect(assignConcatArrayValues(obj)).toBe(obj); - }); - - it("works with two objects with different properties", () => { - const obj1 = { a: 1 }; - const obj2 = { b: 2 }; - const result = assignConcatArrayValues(obj1, obj2); - expect(result).toEqual({ a: 1, b: 2 }); - expect(result).toBe(obj1); - }); - - it("works with two objects with the same property", () => { - expect(assignConcatArrayValues({ a: 1 }, { a: 2 })).toEqual({ a: 2 }); - }); - - it("works with two objects with the same property that is an array", () => { - expect(assignConcatArrayValues({ a: [1] }, { a: [2] })).toEqual({ - a: [1, 2], - }); - }); - - it("works with three objects with the same property that is an array", () => { - expect(assignConcatArrayValues({ a: [1] }, { a: [] }, { a: [3] })).toEqual({ - a: [1, 3], - }); - }); - - it("works with three objects with the same property that is an array and different properties", () => { - expect( - assignConcatArrayValues( - { a: [1] }, - { a: [], c: true, d: false }, - { a: [3], b: "2", e: null }, - ), - ).toEqual({ - a: [1, 3], - b: "2", - c: true, - d: false, - e: null, - }); - }); - - it("skips non-objects", () => { - expect( - assignConcatArrayValues({ a: [1] }, null, { a: [3] }, false, [], 5), - ).toEqual({ - a: [1, 3], - }); - }); -}); diff --git a/test/unit/specs/utils/clone.spec.js b/test/unit/specs/utils/clone.spec.js deleted file mode 100644 index e4b5be1ce..000000000 --- a/test/unit/specs/utils/clone.spec.js +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import clone from "../../../../src/utils/clone.js"; - -describe("clone", () => { - it("clones the object using JSON serialization/deserialization", () => { - const obj = { - toJSON() { - return { foo: "bar" }; - }, - }; - - const result = clone(obj); - - expect(result).toEqual({ - foo: "bar", - }); - }); -}); diff --git a/test/unit/specs/utils/cookieJar.spec.js b/test/unit/specs/utils/cookieJar.spec.js deleted file mode 100644 index 7b0e98dcd..000000000 --- a/test/unit/specs/utils/cookieJar.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -// eslint-disable-next-line no-unused-vars -import cookieJar from "../../../../src/utils/cookieJar.js"; diff --git a/test/unit/specs/utils/crc32.spec.js b/test/unit/specs/utils/crc32.spec.js deleted file mode 100644 index c83b099a6..000000000 --- a/test/unit/specs/utils/crc32.spec.js +++ /dev/null @@ -1,674 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import crc32 from "../../../../src/utils/crc32.js"; - -const crc32Sample = { - Sanskrit: { - str: "काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥", - crc32Hash: 302792584, - }, - Sanskrit_standard_transcription: { - str: "kācaṃ śaknomyattum; nopahinasti mām.", - crc32Hash: 192677925, - }, - Classical_Greek: { - str: "ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει.", - crc32Hash: 3192255259, - }, - Greek_monotonic: { - str: "Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα.", - crc32Hash: 979070829, - }, - Greek_polytonic: { - str: "Μπορῶ νὰ φάω σπασμένα γυαλιὰ χωρὶς νὰ πάθω τίποτα.", - crc32Hash: 3154458923, - }, - Latin: { - str: "Vitrum edere possum; mihi non nocet.", - crc32Hash: 2428364193, - }, - Old_French: { - str: "Je puis mangier del voirre. Ne me nuit.", - crc32Hash: 766997560, - }, - French: { - str: "Je peux manger du verre, ça ne me fait pas mal.", - crc32Hash: 2691810324, - }, - Provençal_Occitan: { - str: "Pòdi manjar de veire, me nafrariá pas.", - crc32Hash: 1951523735, - }, - Québécois: { - str: "J'peux manger d'la vitre, ça m'fa pas mal.", - crc32Hash: 2390419946, - }, - Walloon: { - str: "Dji pou magnî do vêre, çoula m' freut nén må.", - crc32Hash: 636455546, - }, - Picard: { - str: "Ch'peux mingi du verre, cha m'foé mie n'ma.", - crc32Hash: 2432888641, - }, - Kreyòl_Ayisyen_Haitï: { - str: "Mwen kap manje vè, li pa blese'm.", - crc32Hash: 4249393419, - }, - Basque: { - str: "Kristala jan dezaket, ez dit minik ematen.", - crc32Hash: 402760186, - }, - Catalan_Català: { - str: "Puc menjar vidre, que no em fa mal.", - crc32Hash: 3154625029, - }, - Spanish: { - str: "Puedo comer vidrio, no me hace daño.", - crc32Hash: 1723637111, - }, - Aragonés: { - str: "Puedo minchar beire, no me'n fa mal .", - crc32Hash: 1505826585, - }, - Galician: { - str: "Eu podo xantar cristais e non cortarme.", - crc32Hash: 1109377029, - }, - European_Portuguese: { - str: "Posso comer vidro, não me faz mal.", - crc32Hash: 101185041, - }, - Brazilian_Portuguese_8: { - str: "Posso comer vidro, não me machuca.", - crc32Hash: 883982287, - }, - Caboverdiano_Kabuverdianu_Cape_Verde: { - str: "M' podê cumê vidru, ca ta maguâ-m'.", - crc32Hash: 4279814853, - }, - Papiamentu: { - str: "Ami por kome glas anto e no ta hasimi daño.", - crc32Hash: 2084937203, - }, - Italian: { - str: "Posso mangiare il vetro e non mi fa male.", - crc32Hash: 3047854232, - }, - Milanese: { - str: "Sôn bôn de magnà el véder, el me fa minga mal.", - crc32Hash: 2168316945, - }, - Roman: { - str: "Me posso magna' er vetro, e nun me fa male.", - crc32Hash: 1559137374, - }, - Napoletano: { - str: "M' pozz magna' o'vetr, e nun m' fa mal.", - crc32Hash: 502573463, - }, - Venetian: { - str: "Mi posso magnare el vetro, no'l me fa mae.", - crc32Hash: 616338953, - }, - Zeneise_Genovese: { - str: "Pòsso mangiâ o veddro e o no me fà mâ.", - crc32Hash: 28085942, - }, - Sicilian: { - str: "Puotsu mangiari u vitru, nun mi fa mali.", - crc32Hash: 1960166993, - }, - Romansch_Grischun: { - str: "Jau sai mangiar vaider, senza che quai fa donn a mai.", - crc32Hash: 515085501, - }, - Romanian: { - str: "Pot să mănânc sticlă și ea nu mă rănește.", - crc32Hash: 1879348622, - }, - Esperanto: { - str: "Mi povas manĝi vitron, ĝi ne damaĝas min.", - crc32Hash: 2514278986, - }, - Cornish: { - str: "Mý a yl dybry gwéder hag éf ny wra ow ankenya.", - crc32Hash: 14991194, - }, - Welsh: { - str: "Dw i'n gallu bwyta gwydr, 'dyw e ddim yn gwneud dolur i mi.", - crc32Hash: 1548251343, - }, - Manx_Gaelic: { - str: "Foddym gee glonney agh cha jean eh gortaghey mee.", - crc32Hash: 1378983096, - }, - Old_Irish_Ogham: { - str: "᚛᚛ᚉᚑᚅᚔᚉᚉᚔᚋ ᚔᚈᚔ ᚍᚂᚐᚅᚑ ᚅᚔᚋᚌᚓᚅᚐ᚜", - crc32Hash: 2248868407, - }, - Old_Irish_Latin: { - str: "Con·iccim ithi nglano. Ním·géna.", - crc32Hash: 864598796, - }, - Irish: { - str: "Is féidir liom gloinne a ithe. Ní dhéanann sí dochar ar bith dom.", - crc32Hash: 709577166, - }, - Ulster_Gaelic: { - str: "Ithim-sa gloine agus ní miste damh é.", - crc32Hash: 1206756846, - }, - Scottish_Gaelic: { - str: "S urrainn dhomh gloinne ithe; cha ghoirtich i mi.", - crc32Hash: 3064191386, - }, - "Anglo-Saxon_Runes": { - str: "ᛁᚳ᛫ᛗᚨᚷ᛫ᚷᛚᚨᛋ᛫ᛖᚩᛏᚪᚾ᛫ᚩᚾᛞ᛫ᚻᛁᛏ᛫ᚾᛖ᛫ᚻᛖᚪᚱᛗᛁᚪᚧ᛫ᛗᛖ᛬", - crc32Hash: 2170250096, - }, - "Anglo-Saxon_Latin": { - str: "Ic mæg glæs eotan ond hit ne hearmiað me.", - crc32Hash: 2974948093, - }, - Middle_English: { - str: "Ich canne glas eten and hit hirtiþ me nouȝt.", - crc32Hash: 2519549262, - }, - English: { - str: "I can eat glass and it doesn't hurt me.", - crc32Hash: 3522513655, - }, - English_IPA: { - str: "[aɪ kæn iːt glɑːs ænd ɪt dɐz nɒt hɜːt miː] (Received Pronunciation)", - crc32Hash: 2272956447, - }, - English_Braille: { - str: "⠊⠀⠉⠁⠝⠀⠑⠁⠞⠀⠛⠇⠁⠎⠎⠀⠁⠝⠙⠀⠊⠞⠀⠙⠕⠑⠎⠝⠞⠀⠓⠥⠗⠞⠀⠍⠑", - crc32Hash: 3787140422, - }, - Jamaican: { - str: "Mi kian niam glas han i neba hot mi.", - crc32Hash: 443133101, - }, - Lalland_Scots_Doric: { - str: "Ah can eat gless, it disnae hurt us.", - crc32Hash: 747768810, - }, - Gothic_4: { - str: "ЌЌЌ ЌЌЌЍ Ќ̈ЍЌЌ, ЌЌ ЌЌЍ ЍЌ ЌЌЌЌ ЌЍЌЌЌЌЌ.", - crc32Hash: 3686804294, - }, - Old_Norse_Runes: { - str: "ᛖᚴ ᚷᛖᛏ ᛖᛏᛁ ᚧ ᚷᛚᛖᚱ ᛘᚾ ᚦᛖᛋᛋ ᚨᚧ ᚡᛖ ᚱᚧᚨ ᛋᚨᚱ", - crc32Hash: 2108429530, - }, - Old_Norse_Latin: { - str: "Ek get etið gler án þess að verða sár.", - crc32Hash: 296353268, - }, - Norsk_Norwegian_Nynorsk: { - str: "Eg kan eta glas utan å skada meg.", - crc32Hash: 1577959981, - }, - Norsk_Norwegian_Bokmål: { - str: "Jeg kan spise glass uten å skade meg.", - crc32Hash: 2766158238, - }, - Føroyskt_Faroese: { - str: "Eg kann eta glas, skaðaleysur.", - crc32Hash: 1761767164, - }, - Íslenska_Icelandic: { - str: "Ég get etið gler án þess að meiða mig.", - crc32Hash: 964197683, - }, - Svenska_Swedish: { - str: "Jag kan äta glas utan att skada mig.", - crc32Hash: 2676580541, - }, - Dansk_Danish: { - str: "Jeg kan spise glas, det gør ikke ondt på mig.", - crc32Hash: 1496560676, - }, - Sønderjysk: { - str: "Æ ka æe glass uhen at det go mæ naue.", - crc32Hash: 1426070144, - }, - Frysk_Frisian: { - str: "Ik kin glês ite, it docht me net sear.", - crc32Hash: 1849595261, - }, - Nederlands_Dutch: { - str: "Ik kan glas eten, het doet mij geen kwaad.", - crc32Hash: 4016747965, - }, - Kirchröadsj_Bôchesserplat: { - str: "Iech ken glaas èèse, mer 't deet miech jing pieng.", - crc32Hash: 611147726, - }, - Afrikaans: { - str: "Ek kan glas eet, maar dit doen my nie skade nie.", - crc32Hash: 2665435467, - }, - Lëtzebuergescht_Luxemburgish: { - str: "Ech kan Glas iessen, daat deet mir nët wei.", - crc32Hash: 2414621007, - }, - Deutsch_German: { - str: "Ich kann Glas essen, ohne mir zu schaden.", - crc32Hash: 326341728, - }, - Ruhrdeutsch: { - str: "Ich kann Glas verkasematuckeln, ohne dattet mich wat jucken tut.", - crc32Hash: 390997622, - }, - Langenfelder_Platt: { - str: "Isch kann Jlaas kimmeln, uuhne datt mich datt weh dääd.", - crc32Hash: 172048332, - }, - 'Lausitzer_Mundart_"Lusatian"': { - str: "Ich koann Gloos assn und doas dudd merr ni wii.", - crc32Hash: 3814582543, - }, - Odenwälderisch: { - str: "Iech konn glaasch voschbachteln ohne dass es mir ebbs daun doun dud.", - crc32Hash: 3690429996, - }, - Sächsisch_Saxon: { - str: "'sch kann Glos essn, ohne dass'sch mer wehtue.", - crc32Hash: 1553361449, - }, - Pfälzisch: { - str: "Isch konn Glass fresse ohne dasses mer ebbes ausmache dud.", - crc32Hash: 2890933716, - }, - Schwäbisch_Swabian: { - str: "I kå Glas frässa, ond des macht mr nix!", - crc32Hash: 2705198893, - }, - Deutsch_Voralberg: { - str: "I ka glas eassa, ohne dass mar weh tuat.", - crc32Hash: 1802390038, - }, - Bayrisch_Bavarian: { - str: "I koh Glos esa, und es duard ma ned wei.", - crc32Hash: 1620844699, - }, - Allemannisch: { - str: "I kaun Gloos essen, es tuat ma ned weh.", - crc32Hash: 532444931, - }, - Schwyzerdütsch_Zürich: { - str: "Ich chan Glaas ässe, das schadt mir nöd.", - crc32Hash: 647555846, - }, - Schwyzerdütsch_Luzern: { - str: "Ech cha Glâs ässe, das schadt mer ned.", - crc32Hash: 2888870414, - }, - Hungarian: { - str: "Meg tudom enni az üveget, nem lesz tőle bajom.", - crc32Hash: 798141029, - }, - Suomi_Finnish: { - str: "Voin syödä lasia, se ei vahingoita minua.", - crc32Hash: 854800956, - }, - Sami_Northern: { - str: "Sáhtán borrat lása, dat ii leat bávččas.", - crc32Hash: 2134369501, - }, - Erzian: { - str: "Мон ярсан суликадо, ды зыян эйстэнзэ а ули.", - crc32Hash: 1195119622, - }, - Northern_Karelian: { - str: "Mie voin syvvä lasie ta minla ei ole kipie.", - crc32Hash: 825590056, - }, - Southern_Karelian: { - str: "Minä voin syvvä st'oklua dai minule ei ole kibie.", - crc32Hash: 1454844737, - }, - Estonian: { - str: "Ma võin klaasi süüa, see ei tee mulle midagi.", - crc32Hash: 1872139873, - }, - Latvian: { - str: "Es varu ēst stiklu, tas man nekaitē.", - crc32Hash: 1984101272, - }, - Lithuanian: { - str: "Aš galiu valgyti stiklą ir jis manęs nežeidžia", - crc32Hash: 462644696, - }, - Czech: { - str: "Mohu jíst sklo, neublíží mi.", - crc32Hash: 1941463124, - }, - Slovak: { - str: "Môžem jesť sklo. Nezraní ma.", - crc32Hash: 1401582521, - }, - Polska_Polish: { - str: "Mogę jeść szkło i mi nie szkodzi.", - crc32Hash: 4062381321, - }, - Slovenian: { - str: "Lahko jem steklo, ne da bi mi škodovalo.", - crc32Hash: 3517115573, - }, - "Bosnian,_Croatian,_Montenegrin_and_Serbian_Latin": { - str: "Ja mogu jesti staklo, i to mi ne šteti.", - crc32Hash: 526455862, - }, - "Bosnian,_Montenegrin_and_Serbian_Cyrillic": { - str: "Ја могу јести стакло, и то ми не штети.", - crc32Hash: 1697709634, - }, - Macedonian: { - str: "Можам да јадам стакло, а не ме штета.", - crc32Hash: 1164576497, - }, - Russian: { - str: "Я могу есть стекло, оно мне не вредит.", - crc32Hash: 3819750153, - }, - Belarusian_Cyrillic: { - str: "Я магу есці шкло, яно мне не шкодзіць.", - crc32Hash: 4029978467, - }, - Belarusian_Lacinka: { - str: "Ja mahu jeści škło, jano mne ne škodzić.", - crc32Hash: 1627307525, - }, - Ukrainian: { - str: "Я можу їсти скло, і воно мені не зашкодить.", - crc32Hash: 752793683, - }, - Bulgarian: { - str: "Мога да ям стъкло, то не ми вреди.", - crc32Hash: 3841600447, - }, - Georgian: { - str: "მინას ვჭამ და არა მტკივა.", - crc32Hash: 3209020393, - }, - Armenian: { - str: "Կրնամ ապակի ուտել և ինծի անհանգիստ չըներ։", - crc32Hash: 344432805, - }, - Albanian: { - str: "Unë mund të ha qelq dhe nuk më gjen gjë.", - crc32Hash: 541809055, - }, - Turkish: { - str: "Cam yiyebilirim, bana zararı dokunmaz.", - crc32Hash: 3446658794, - }, - Turkish_Ottoman: { - str: "جام ييه بلورم بڭا ضررى طوقونمز", - crc32Hash: 7569123, - }, - Bangla_Bengali: { - str: "আমি কাঁচ খেতে পারি, তাতে আমার কোনো ক্ষতি হয় না।", - crc32Hash: 3121877246, - }, - Marathi: { - str: "मी काच खाऊ शकतो, मला ते दुखत नाही.", - crc32Hash: 2262144017, - }, - Kannada: { - str: "ನನಗೆ ಹಾನಿ ಆಗದೆ, ನಾನು ಗಜನ್ನು ತಿನಬಹುದು", - crc32Hash: 3783527408, - }, - Hindi: { - str: "मैं काँच खा सकता हूँ और मुझे उससे कोई चोट नहीं पहुंचती.", - crc32Hash: 911621671, - }, - Tamil: { - str: "நான் கண்ணாடி சாப்பிடுவேன், அதனால் எனக்கு ஒரு கேடும் வராது.", - crc32Hash: 2492793518, - }, - Telugu: { - str: "నేను గాజు తినగలను మరియు అలా చేసినా నాకు ఏమి ఇబ్బంది లేదు", - crc32Hash: 1475959036, - }, - Sinhalese: { - str: "මට වීදුරු කෑමට හැකියි. එයින් මට කිසි හානියක් සිදු නොවේ.", - crc32Hash: 2353063362, - }, - Urdu3: { - str: "میں کانچ کھا سکتا ہوں اور مجھے تکلیف نہیں ہوتی ۔", - crc32Hash: 2416692885, - }, - Pashto3: { - str: "زه شيشه خوړلې شم، هغه ما نه خوږوي", - crc32Hash: 2167742720, - }, - Farsi_Persian3: { - str: ".من می توانم بدونِ احساس درد شيشه بخورم", - crc32Hash: 367406354, - }, - Arabic3: { - str: "أنا قادر على أكل الزجاج و هذا لا يؤلمني.", - crc32Hash: 2614809933, - }, - Maltese: { - str: "Nista' niekol il-ħġieġ u ma jagħmilli xejn.", - crc32Hash: 1075553078, - }, - Hebrew3: { - str: "אני יכול לאכול זכוכית וזה לא מזיק לי.", - crc32Hash: 611298455, - }, - Yiddish3: { - str: "איך קען עסן גלאָז און עס טוט מיר נישט װײ.", - crc32Hash: 1278369850, - }, - Twi: { - str: "Metumi awe tumpan, ɜnyɜ me hwee.", - crc32Hash: 1882519302, - }, - Hausa_Latin: { - str: "Inā iya taunar gilāshi kuma in gamā lāfiyā.", - crc32Hash: 3951714594, - }, - Hausa_Ajami_2: { - str: "إِنا إِىَ تَونَر غِلَاشِ كُمَ إِن غَمَا لَافِىَا", - crc32Hash: 2165839937, - }, - Yoruba4: { - str: "Mo lè je̩ dígí, kò ní pa mí lára.", - crc32Hash: 462513830, - }, - Lingala: { - str: "Nakokí kolíya biténi bya milungi, ekosála ngáí mabé tɛ́.", - crc32Hash: 3326201453, - }, - KiSwahili: { - str: "Naweza kula bilauri na sikunyui.", - crc32Hash: 1166963539, - }, - Malay: { - str: "Saya boleh makan kaca dan ia tidak mencederakan saya.", - crc32Hash: 1655550062, - }, - Tagalog: { - str: "Kaya kong kumain nang bubog at hindi ako masaktan.", - crc32Hash: 2604200406, - }, - Chamorro: { - str: "Siña yo' chumocho krestat, ti ha na'lalamen yo'.", - crc32Hash: 2067473716, - }, - Fijian: { - str: "Au rawa ni kana iloilo, ia au sega ni vakacacani kina.", - crc32Hash: 4067250732, - }, - Javanese: { - str: "Aku isa mangan beling tanpa lara.", - crc32Hash: 2987633961, - }, - Burmese: { - str: "က္ယ္ဝန္‌တော္‌၊က္ယ္ဝန္‌မ မ္ယက္‌စားနုိင္‌သည္‌။ ၎က္ရောင္‌့ ထိခုိက္‌မ္ဟု မရ္ဟိပာ။ (9)", - crc32Hash: 699725753, - }, - Vietnamese_quốc_ngữ: { - str: "Tôi có thể ăn thủy tinh mà không hại gì.", - crc32Hash: 2565707606, - }, - Vietnamese_nôm_4: { - str: "些 ࣎ 世 咹 水 晶 ও 空 ࣎ 害 咦", - crc32Hash: 1216417303, - }, - Khmer: { - str: "ខ្ញុំអាចញុំកញ្ចក់បាន ដោយគ្មានបញ្ហារ", - crc32Hash: 2350771137, - }, - Lao: { - str: "ຂອ້ຍກິນແກ້ວໄດ້ໂດຍທີ່ມັນບໍ່ໄດ້ເຮັດໃຫ້ຂອ້ຍເຈັບ.", - crc32Hash: 1733276636, - }, - Thai: { - str: "ฉันกินกระจกได้ แต่มันไม่ทำให้ฉันเจ็บ", - crc32Hash: 227782237, - }, - Mongolian_Cyrillic: { - str: "Би шил идэй чадна, надад хортой биш", - crc32Hash: 2254455591, - }, - Mongolian_Classic_5: { - str: "ᠪᠢ ᠰᠢᠯᠢ ᠢᠳᠡᠶᠦ ᠴᠢᠳᠠᠨᠠ ᠂ ᠨᠠᠳᠤᠷ ᠬᠣᠤᠷᠠᠳᠠᠢ ᠪᠢᠰᠢ", - crc32Hash: 412025584, - }, - Nepali: { - str: "म काँच खान सक्छू र मलाई केहि नी हुन्‍न् ।", - crc32Hash: 92247149, - }, - Tibetan: { - str: "ཤེལ་སྒོ་ཟ་ནས་ང་ན་གི་མ་རེད།", - crc32Hash: 2943025548, - }, - Chinese: { - str: "我能吞下玻璃而不伤身体。", - crc32Hash: 3340264501, - }, - Chinese_Traditional: { - str: "我能吞下玻璃而不傷身體。", - crc32Hash: 617692142, - }, - Taiwanese6: { - str: "Góa ē-tàng chia̍h po-lê, mā bē tio̍h-siong.", - crc32Hash: 3845385888, - }, - Japanese: { - str: "私はガラスを食べられます。それは私を傷つけません。", - crc32Hash: 1060378668, - }, - Korean: { - str: "나는 유리를 먹을 수 있어요. 그래도 아프지 않아요", - crc32Hash: 3701809983, - }, - Bislama: { - str: "Mi save kakae glas, hemi no save katem mi.", - crc32Hash: 1105523983, - }, - Hawaiian: { - str: "Hiki iaʻu ke ʻai i ke aniani; ʻaʻole nō lā au e ʻeha.", - crc32Hash: 3890531562, - }, - Marquesan: { - str: "E koʻana e kai i te karahi, mea ʻā, ʻaʻe hauhau.", - crc32Hash: 1236763367, - }, - Inuktitut_10: { - str: "ᐊᓕᒍᖅ ᓂᕆᔭᕌᖓᒃᑯ ᓱᕋᙱᑦᑐᓐᓇᖅᑐᖓ", - crc32Hash: 557265846, - }, - Chinook_Jargon: { - str: "Naika məkmək kakshət labutay, pi weyk ukuk munk-sik nay.", - crc32Hash: 4059264750, - }, - Navajo: { - str: "Tsésǫʼ yishą́ągo bííníshghah dóó doo shił neezgai da.", - crc32Hash: 3770336662, - }, - Lojban: { - str: "mi kakne le nu citka le blaci .iku'i le se go'i na xrani mi", - crc32Hash: 3008094941, - }, - Nórdicg: { - str: "Ljœr ye caudran créneþ ý jor cẃran.", - crc32Hash: 4211638728, - }, -}; - -describe("crc32", () => { - it("should hash a string and return a number ", () => { - const str = "hello"; - const result = crc32(str); - expect(typeof result).toBe("number"); - expect(result).toEqual(907060870); - }); - - it("should create same hash every time", () => { - const idsTohash = { - email: { - id: "me@me.com", - authState: 0, - }, - }; - const resultOne = crc32(JSON.stringify(idsTohash)); - const resultTwo = crc32(JSON.stringify(idsTohash)); - expect(typeof resultOne).toBe("number"); - expect(resultOne).toBe(3158443042); - expect(resultTwo).toBe(3158443042); - }); - - it("should always return a positive number", () => { - const idOneTohash = "x+x"; - const idTwoTohash = "a*b/100-220"; - const resultOne = crc32(JSON.stringify(idOneTohash)); - const resultTwo = crc32(JSON.stringify(idTwoTohash)); - expect(typeof resultOne).toBe("number"); - expect(resultOne).toBeGreaterThan(0); - expect(typeof resultTwo).toBe("number"); - expect(resultTwo).toBeGreaterThan(0); - }); - - it("should hash strings with special characters", () => { - const stringToHash = "hello@#&^hq10"; - const result = crc32(stringToHash); - expect(result).toBe(864118309); - }); - - it("should create different hashes for different strings", () => { - const stringOneToHash = "hello@#&^hq10"; - const stringTwoToHash = "hello@#&h^q10"; - const resultOne = crc32(stringOneToHash); - const resultTwo = crc32(stringTwoToHash); - expect(resultOne).not.toEqual(resultTwo); - expect(resultOne).toBe(864118309); - expect(resultTwo).toBe(3365964926); - }); - - describe("hashing of various of unicode chars", () => { - Object.keys(crc32Sample).forEach((lang) => { - const sample = crc32Sample[lang]; - it(`should create right hash of a ${lang} string`, () => { - expect(crc32(sample.str)).toBe(sample.crc32Hash); - }); - }); - }); -}); diff --git a/test/unit/specs/utils/createCallbackAggregator.spec.js b/test/unit/specs/utils/createCallbackAggregator.spec.js deleted file mode 100644 index 636ee5fdf..000000000 --- a/test/unit/specs/utils/createCallbackAggregator.spec.js +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createCallbackAggregator from "../../../../src/utils/createCallbackAggregator.js"; - -describe("createCallbackAggregator", () => { - let callbackAggregator; - - beforeEach(() => { - callbackAggregator = createCallbackAggregator(); - }); - - it("calls all added callbacks and returns a combined promise", () => { - const callback1 = jasmine.createSpy("callback1").and.returnValue("foo"); - const callback2 = jasmine.createSpy("callback2").and.returnValue("bar"); - callbackAggregator.add(callback1); - callbackAggregator.add(callback2); - return callbackAggregator.call("cherry", "tree").then((result) => { - expect(callback1).toHaveBeenCalledWith("cherry", "tree"); - expect(callback2).toHaveBeenCalledWith("cherry", "tree"); - expect(result).toEqual(["foo", "bar"]); - }); - }); - - it("doesn't throw errors when there are no callbacks", () => { - return callbackAggregator.call("cherry", "tree").then((result) => { - expect(result).toEqual([]); - }); - }); - - it("doesn't throw errors when there are no arguments", () => { - const callback = jasmine.createSpy("callback").and.returnValue("foo"); - callbackAggregator.add(callback); - return callbackAggregator.call().then((result) => { - expect(result).toEqual(["foo"]); - }); - }); -}); diff --git a/test/unit/specs/utils/createCollect.spec.js b/test/unit/specs/utils/createCollect.spec.js deleted file mode 100644 index d3ba7214c..000000000 --- a/test/unit/specs/utils/createCollect.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createCollect from "../../../../src/utils/createCollect.js"; -import { PropositionEventType } from "../../../../src/constants/propositionEventType.js"; - -describe("Utils::createCollect", () => { - let eventManager; - let mergeDecisionsMeta; - const decisionsMeta = [ - { - id: 1, - decisionId: "foo", - }, - ]; - const event = { - mergeXdm: jasmine.createSpy(), - }; - - beforeEach(() => { - eventManager = jasmine.createSpyObj("eventManager", { - sendEvent: undefined, - createEvent: event, - }); - mergeDecisionsMeta = jasmine.createSpy("mergeDecisionsMeta"); - }); - - it("collects and sends event with metadata", () => { - const collect = createCollect({ eventManager, mergeDecisionsMeta }); - collect({ decisionsMeta }); - expect(eventManager.createEvent).toHaveBeenCalled(); - expect(event.mergeXdm).toHaveBeenCalledWith({ - eventType: "decisioning.propositionDisplay", - }); - expect(mergeDecisionsMeta).toHaveBeenCalledWith( - event, - decisionsMeta, - [PropositionEventType.DISPLAY], - undefined, - ); - expect(eventManager.sendEvent).toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/utils/createLoggingCookieJar.spec.js b/test/unit/specs/utils/createLoggingCookieJar.spec.js deleted file mode 100644 index 5b9c8d259..000000000 --- a/test/unit/specs/utils/createLoggingCookieJar.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createLoggingCookieJar from "../../../../src/utils/createLoggingCookieJar.js"; - -describe("loggingCookieJar", () => { - let cookieJar; - let logger; - let loggingCookieJar; - - beforeEach(() => { - cookieJar = jasmine.createSpyObj("cookieJar", ["set", "get"]); - logger = jasmine.createSpyObj("logger", ["info"]); - loggingCookieJar = createLoggingCookieJar({ cookieJar, logger }); - }); - - it("logs a message", () => { - loggingCookieJar.set("mykey", "myvalue", { myoption: "myoptionvalue" }); - expect(logger.info).toHaveBeenCalledOnceWith("Setting cookie", { - name: "mykey", - value: "myvalue", - myoption: "myoptionvalue", - }); - }); - - it("calls set", () => { - loggingCookieJar.set("mykey", "myvalue", { myoption: "myoptionvalue" }); - expect(cookieJar.set).toHaveBeenCalledOnceWith("mykey", "myvalue", { - myoption: "myoptionvalue", - }); - }); - - it("calls get", () => { - loggingCookieJar.get("mykey"); - expect(cookieJar.get).toHaveBeenCalledOnceWith("mykey"); - }); - - it("returns the value from get", () => { - cookieJar.get.and.returnValue("myvalue"); - expect(loggingCookieJar.get("mykey")).toEqual("myvalue"); - }); -}); diff --git a/test/unit/specs/utils/createMerger.spec.js b/test/unit/specs/utils/createMerger.spec.js deleted file mode 100644 index f826d5e6f..000000000 --- a/test/unit/specs/utils/createMerger.spec.js +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createMerger from "../../../../src/utils/createMerger.js"; - -describe("createMerger", () => { - it("populates key if key doesn't exist", () => { - const content = {}; - createMerger( - content, - "fruit", - )({ - type: "apple", - }); - expect(content).toEqual({ - fruit: { - type: "apple", - }, - }); - }); - - it("deeply merges if key does exist", () => { - const content = { - foods: { - fruits: { - apple: { - calories: 95, - }, - banana: { - calories: 105, - }, - }, - }, - }; - createMerger( - content, - "foods", - )({ - fruits: { - banana: { - calories: 110, - }, - cherry: { - calories: 77, - }, - }, - }); - expect(content).toEqual({ - foods: { - fruits: { - apple: { - calories: 95, - }, - banana: { - calories: 110, - }, - cherry: { - calories: 77, - }, - }, - }, - }); - }); -}); diff --git a/test/unit/specs/utils/createSubscription.spec.js b/test/unit/specs/utils/createSubscription.spec.js deleted file mode 100644 index ca84aec48..000000000 --- a/test/unit/specs/utils/createSubscription.spec.js +++ /dev/null @@ -1,167 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import createSubscription from "../../../../src/utils/createSubscription.js"; - -describe("createSubscription", () => { - const value = { something: 42 }; - - let callback1; - let callback2; - let callback3; - - beforeEach(() => { - callback1 = jasmine.createSpy("callback1"); - callback2 = jasmine.createSpy("callback2"); - callback3 = jasmine.createSpy("callback3"); - }); - - it("supports a single subscription", () => { - const subscription = createSubscription(); - expect(subscription.hasSubscriptions()).toBeFalse(); - - const { unsubscribe } = subscription.add(callback1); - - expect(subscription.hasSubscriptions()).toBeTrue(); - - subscription.emit(value); - - expect(callback1).toHaveBeenCalledOnceWith(value); - - unsubscribe(); - - expect(subscription.hasSubscriptions()).toBeFalse(); - subscription.emit(value); - - expect(callback1).toHaveBeenCalledOnceWith(value); - }); - - it("supports multiple subscriptions", () => { - const subscription = createSubscription(); - expect(subscription.hasSubscriptions()).toBeFalse(); - - const { unsubscribe: unsubscribe1 } = subscription.add(callback1); - const { unsubscribe: unsubscribe2 } = subscription.add(callback2); - const { unsubscribe: unsubscribe3 } = subscription.add(callback3); - - expect(subscription.hasSubscriptions()).toBeTrue(); - - subscription.emit(value); - - expect(callback1).toHaveBeenCalledOnceWith(value); - expect(callback2).toHaveBeenCalledOnceWith(value); - expect(callback3).toHaveBeenCalledOnceWith(value); - - // unsubscribe the first callback - unsubscribe1(); - - expect(subscription.hasSubscriptions()).toBeTrue(); - - subscription.emit(value); - - expect(callback1).toHaveBeenCalledTimes(1); - expect(callback2).toHaveBeenCalledTimes(2); - expect(callback3).toHaveBeenCalledTimes(2); - - // unsubscribe the second callback - unsubscribe2(); - - expect(subscription.hasSubscriptions()).toBeTrue(); - - subscription.emit(value); - - expect(callback1).toHaveBeenCalledTimes(1); - expect(callback2).toHaveBeenCalledTimes(2); - expect(callback3).toHaveBeenCalledTimes(3); - - // unsubscribe the third callback - unsubscribe3(); - - expect(subscription.hasSubscriptions()).toBeFalse(); - - subscription.emit(value); - - expect(callback1).toHaveBeenCalledTimes(1); - expect(callback2).toHaveBeenCalledTimes(2); - expect(callback3).toHaveBeenCalledTimes(3); - }); - - it("emits distinct values for multiple subscriptions", () => { - const subscription = createSubscription(); - subscription.setEmissionPreprocessor((params, basePrice) => { - const { name, profitMargin } = params; - const price = basePrice * profitMargin; - return [`hello ${name}! The price is $${price}`]; - }); - - const { unsubscribe: unsubscribe1 } = subscription.add(callback1, { - name: "jim", - profitMargin: 3, - }); - const { unsubscribe: unsubscribe2 } = subscription.add(callback2, { - name: "bob", - profitMargin: 1.8, - }); - const { unsubscribe: unsubscribe3 } = subscription.add(callback3, { - name: "tina", - profitMargin: 1.1, - }); - - subscription.emit(10); - - expect(callback1).toHaveBeenCalledOnceWith("hello jim! The price is $30"); - expect(callback2).toHaveBeenCalledOnceWith("hello bob! The price is $18"); - expect(callback3).toHaveBeenCalledOnceWith("hello tina! The price is $11"); - - unsubscribe1(); - unsubscribe2(); - unsubscribe3(); - }); - - it("emits distinct values conditionally", () => { - const subscription = createSubscription(); - subscription.setEmissionPreprocessor((params, basePrice) => { - const { name, profitMargin } = params; - const price = basePrice * profitMargin; - return [`hello ${name}! The price is $${price}`]; - }); - subscription.setEmissionCondition((params, result) => { - const price = parseInt( - result.substring(result.length - 2, result.length), - 10, - ); - return price < 20; - }); - - const { unsubscribe: unsubscribe1 } = subscription.add(callback1, { - name: "jim", - profitMargin: 3, - }); - const { unsubscribe: unsubscribe2 } = subscription.add(callback2, { - name: "bob", - profitMargin: 1.8, - }); - const { unsubscribe: unsubscribe3 } = subscription.add(callback3, { - name: "tina", - profitMargin: 1.1, - }); - - subscription.emit(10); - - expect(callback1).not.toHaveBeenCalled(); // price is > 20, so no emission - expect(callback2).toHaveBeenCalledOnceWith("hello bob! The price is $18"); - expect(callback3).toHaveBeenCalledOnceWith("hello tina! The price is $11"); - - unsubscribe1(); - unsubscribe2(); - unsubscribe3(); - }); -}); diff --git a/test/unit/specs/utils/createTaskQueue.spec.js b/test/unit/specs/utils/createTaskQueue.spec.js deleted file mode 100644 index 0754d4c8d..000000000 --- a/test/unit/specs/utils/createTaskQueue.spec.js +++ /dev/null @@ -1,152 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createTaskQueue from "../../../../src/utils/createTaskQueue.js"; -import { defer } from "../../../../src/utils/index.js"; -import flushPromiseChains from "../../helpers/flushPromiseChains.js"; - -describe("createTaskQueue", () => { - it("executes a single task once even when it throws an error", () => { - const queue = createTaskQueue(); - const task1 = jasmine - .createSpy("task1") - .and.returnValue(Promise.reject(Error("myerror"))); - return queue.addTask(task1).then(fail, (e) => { - expect(e.message).toEqual("myerror"); - expect(task1).toHaveBeenCalledTimes(1); - }); - }); - - it("executes tasks in sequence when first task succeeds", () => { - const queue = createTaskQueue(); - const task1Deferred = defer(); - const task1 = jasmine - .createSpy("task1") - .and.returnValue(task1Deferred.promise); - const task2Deferred = defer(); - const task2 = jasmine - .createSpy("task2") - .and.returnValue(task2Deferred.promise); - - const task1OnFulfilled = jasmine.createSpy("task1OnFulfilled"); - queue.addTask(task1).then(task1OnFulfilled); - const task2OnFulfilled = jasmine.createSpy("task2OnFulfilled"); - queue.addTask(task2).then(task2OnFulfilled); - - return flushPromiseChains() - .then(() => { - expect(task1).toHaveBeenCalled(); - expect(task2).not.toHaveBeenCalled(); - expect(task1OnFulfilled).not.toHaveBeenCalled(); - expect(task2OnFulfilled).not.toHaveBeenCalled(); - task1Deferred.resolve("task1Result"); - return flushPromiseChains(); - }) - .then(() => { - expect(task2).toHaveBeenCalled(); - expect(task1OnFulfilled).toHaveBeenCalledWith("task1Result"); - expect(task2OnFulfilled).not.toHaveBeenCalled(); - task2Deferred.resolve("task2Result"); - return flushPromiseChains(); - }) - .then(() => { - expect(task2OnFulfilled).toHaveBeenCalledWith("task2Result"); - }); - }); - - it("executes tasks in sequence when first task rejects promise", () => { - const queue = createTaskQueue(); - const task1Deferred = defer(); - const task1 = jasmine - .createSpy("task1") - .and.returnValue(task1Deferred.promise); - const task2Deferred = defer(); - const task2 = jasmine - .createSpy("task2") - .and.returnValue(task2Deferred.promise); - - const task1OnRejected = jasmine.createSpy("task1OnRejected"); - queue.addTask(task1).catch(task1OnRejected); - const task2OnFulfilled = jasmine.createSpy("task2OnFulfilled"); - queue.addTask(task2).then(task2OnFulfilled); - - return flushPromiseChains() - .then(() => { - expect(task1).toHaveBeenCalled(); - expect(task2).not.toHaveBeenCalled(); - expect(task1OnRejected).not.toHaveBeenCalled(); - expect(task2OnFulfilled).not.toHaveBeenCalled(); - task1Deferred.reject(new Error("task1Error")); - return flushPromiseChains(); - }) - .then(() => { - expect(task2).toHaveBeenCalled(); - expect(task1OnRejected).toHaveBeenCalledWith(new Error("task1Error")); - expect(task2OnFulfilled).not.toHaveBeenCalled(); - task2Deferred.resolve("task2Result"); - return flushPromiseChains(); - }) - .then(() => { - expect(task2OnFulfilled).toHaveBeenCalledWith("task2Result"); - }); - }); - - it("executes tasks in sequence when first task throws error", () => { - const queue = createTaskQueue(); - const task1 = jasmine - .createSpy("task1") - .and.throwError(new Error("task1Error")); - const task2Deferred = defer(); - const task2 = jasmine - .createSpy("task2") - .and.returnValue(task2Deferred.promise); - - const task1OnRejected = jasmine.createSpy("task1OnRejected"); - queue.addTask(task1).catch(task1OnRejected); - const task2OnFulfilled = jasmine.createSpy("task2OnFulfilled"); - queue.addTask(task2).then(task2OnFulfilled); - - return flushPromiseChains() - .then(() => { - expect(task1).toHaveBeenCalled(); - expect(task2).toHaveBeenCalled(); - expect(task1OnRejected).toHaveBeenCalledWith(new Error("task1Error")); - expect(task2OnFulfilled).not.toHaveBeenCalled(); - task2Deferred.resolve("task2Result"); - return flushPromiseChains(); - }) - .then(() => { - expect(task2OnFulfilled).toHaveBeenCalledWith("task2Result"); - }); - }); - - it("accurately reports the size of the queue", () => { - const queue = createTaskQueue(); - const task1Deferred = defer(); - const task2Deferred = defer(); - expect(queue.length).toEqual(0); - queue.addTask(() => task1Deferred.promise); - expect(queue.length).toEqual(1); - queue.addTask(() => task2Deferred.promise); - expect(queue.length).toEqual(2); - task1Deferred.resolve(); - return flushPromiseChains() - .then(() => { - expect(queue.length).toEqual(1); - task2Deferred.resolve(); - return flushPromiseChains(); - }) - .then(() => { - expect(queue.length).toEqual(0); - }); - }); -}); diff --git a/test/unit/specs/utils/decodeUriComponentSafely.spec.js b/test/unit/specs/utils/decodeUriComponentSafely.spec.js deleted file mode 100644 index 2f8f64186..000000000 --- a/test/unit/specs/utils/decodeUriComponentSafely.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import decodeUriComponentSafely from "../../../../src/utils/decodeUriComponentSafely.js"; - -describe("decodeUriComponentSafely", () => { - it("decodes a uri encoded string", () => { - expect(decodeUriComponentSafely("%3Fx%3Dtest")).toEqual("?x=test"); - }); - - it("returns an empty string when an invalid encoded URI component is provided", () => { - expect( - decodeUriComponentSafely( - "MCORGID%3D5BFE274A5F6980A50A495C08%2540AdobeOrg%ttt", - ), - ).toEqual(""); - }); -}); diff --git a/test/unit/specs/utils/deduplicateArray.spec.js b/test/unit/specs/utils/deduplicateArray.spec.js deleted file mode 100644 index 08c205208..000000000 --- a/test/unit/specs/utils/deduplicateArray.spec.js +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { deduplicateArray } from "../../../../src/utils/index.js"; - -describe("deduplicateArray", () => { - it("should return an empty array if input is empty", () => { - expect(deduplicateArray([])).toEqual([]); - }); - - it("should return an array with one item if input has one item", () => { - const input = [1]; - expect(deduplicateArray(input)).toEqual(input); - }); - - it("should return an array with one item if input has two equal items", () => { - const input = [1, 1]; - expect(deduplicateArray(input)).toEqual([1]); - }); - - it("should return an array with two items if input has two different items", () => { - const input = [1, 2]; - expect(deduplicateArray(input)).toEqual(input); - }); - - it("should return an array with two items if input has three items with two equal items", () => { - const input = [1, 1, 2]; - expect(deduplicateArray(input)).toEqual([1, 2]); - }); - - it("should accept a custom equality function", () => { - const input = [{ id: 1 }, { id: 1 }, { id: 2 }]; - const isEqual = (a, b) => a.id === b.id; - expect(deduplicateArray(input, isEqual)).toEqual([{ id: 1 }, { id: 2 }]); - }); -}); diff --git a/test/unit/specs/utils/deepAssign.spec.js b/test/unit/specs/utils/deepAssign.spec.js deleted file mode 100644 index d9c7438d8..000000000 --- a/test/unit/specs/utils/deepAssign.spec.js +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import deepAssign from "../../../../src/utils/deepAssign.js"; - -describe("deepAssign", () => { - it("should throw when target is null or undefined", () => { - expect(() => { - deepAssign(null, { a: 1 }); - }).toThrow(); - - expect(() => { - deepAssign(undefined, { a: 1 }); - }).toThrow(); - }); - - it("should assign when target is string", () => { - const result1 = deepAssign("foo", { a: 1 }); - const result2 = Object.assign("foo", { a: 1 }); - - expect(result1).toEqual(result2); - }); - - it("should assign when target is number", () => { - const result1 = deepAssign(1, { a: 1 }); - const result2 = Object.assign(1, { a: 1 }); - - expect(result1).toEqual(result2); - }); - - it("should assign when target is array", () => { - const result1 = deepAssign([1], { a: 1 }); - const result2 = Object.assign([1], { a: 1 }); - - expect(result1).toEqual(result2); - }); - - it("should assign when target is object and source is string", () => { - const result1 = deepAssign({}, "foo"); - const result2 = { ..."foo" }; - - expect(result1).toEqual(result2); - }); - - it("should assign when target is object and source is number", () => { - const result1 = deepAssign({}, 1); - const result2 = { ...1 }; - - expect(result1).toEqual(result2); - }); - - it("should assign when target is object and source is array", () => { - const result1 = deepAssign({}, [1]); - const result2 = { ...[1] }; - - expect(result1).toEqual(result2); - }); - - it("should assign values recursively", () => { - const result = deepAssign( - {}, - { a: { c: 1 } }, - { b: 2 }, - { a: { c: 2, d: 3 } }, - ); - - expect(result).toEqual({ a: { c: 2, d: 3 }, b: 2 }); - }); -}); diff --git a/test/unit/specs/utils/defer.spec.js b/test/unit/specs/utils/defer.spec.js deleted file mode 100644 index b2f8bc4f2..000000000 --- a/test/unit/specs/utils/defer.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import defer from "../../../../src/utils/defer.js"; - -describe("defer", () => { - it("resolves an exposed promise", (done) => { - const deferred = defer(); - deferred.promise.then((value) => { - expect(value).toBe("abc"); - done(); - }); - deferred.resolve("abc"); - }); - - it("rejects an exposed promise", (done) => { - const deferred = defer(); - deferred.promise.then( - () => {}, - (value) => { - expect(value).toBe("abc"); - done(); - }, - ); - deferred.reject("abc"); - }); -}); diff --git a/test/unit/specs/utils/dom/appendNode.spec.js b/test/unit/specs/utils/dom/appendNode.spec.js deleted file mode 100644 index 15b8c23f9..000000000 --- a/test/unit/specs/utils/dom/appendNode.spec.js +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createNode from "../../../../../src/utils/dom/createNode.js"; -import appendNode from "../../../../../src/utils/dom/appendNode.js"; -import selectNodes from "../../../../../src/utils/dom/selectNodes.js"; -import removeNode from "../../../../../src/utils/dom/removeNode.js"; - -describe("DOM::appendNode", () => { - afterEach(() => { - selectNodes("div").forEach(removeNode); - }); - - it("should append a node to head tag", () => { - const elem = createNode("div", { id: "append" }); - - appendNode(document.head, elem); - - expect(selectNodes("#append").length).toEqual(1); - }); -}); diff --git a/test/unit/specs/utils/dom/createNode.spec.js b/test/unit/specs/utils/dom/createNode.spec.js deleted file mode 100644 index cdfa2a8fb..000000000 --- a/test/unit/specs/utils/dom/createNode.spec.js +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createNode from "../../../../../src/utils/dom/createNode.js"; - -describe("DOM::createNode", () => { - it("should createNode with tag only", () => { - const element = createNode("DIV"); - - expect(element.tagName).toEqual("DIV"); - }); - - it("should createNode with tag and attrs", () => { - const element = createNode("DIV", { id: "create" }); - - expect(element.tagName).toEqual("DIV"); - expect(element.id).toEqual("create"); - }); - - it("should createNode with tag, child", () => { - const element = createNode("DIV", {}, {}, [createNode("p")]); - - expect(element.tagName).toEqual("DIV"); - expect(element.firstElementChild.tagName).toEqual("P"); - }); - - it("supports style attribute objects", () => { - const element = createNode("DIV", {}, { style: { color: "blue" } }); - expect(element.tagName).toEqual("DIV"); - expect(element.style.color).toEqual("blue"); - }); - - it("supports style attribute strings", () => { - const element = createNode("DIV", { style: "color: blue;" }); - expect(element.tagName).toEqual("DIV"); - expect(element.style.color).toEqual("blue"); - }); -}); diff --git a/test/unit/specs/utils/dom/isShadowSelector.spec.js b/test/unit/specs/utils/dom/isShadowSelector.spec.js deleted file mode 100644 index 7fa55cec9..000000000 --- a/test/unit/specs/utils/dom/isShadowSelector.spec.js +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright 2021 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isShadowSelector from "../../../../../src/utils/dom/isShadowSelector.js"; - -describe("Utils::DOM::isShadowSelector", () => { - it("should detect shadow selectors", () => { - let selector = - "BODY > BUY-NOW-BUTTON:nth-of-type(2):shadow > DIV:nth-of-type(1)"; - let result = isShadowSelector(selector); - expect(result).toBeTrue(); - selector = "BODY > BUY-NOW-BUTTON:nth-of-type(2) > DIV:nth-of-type(1)"; - result = isShadowSelector(selector); - expect(result).toBeFalse(); - }); -}); diff --git a/test/unit/specs/utils/dom/matchesSelector.spec.js b/test/unit/specs/utils/dom/matchesSelector.spec.js deleted file mode 100644 index 10df57fd2..000000000 --- a/test/unit/specs/utils/dom/matchesSelector.spec.js +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import matchesSelector from "../../../../../src/utils/dom/matchesSelector.js"; - -describe("DOM::matchesSelector", () => { - it("should match selector for existing element", () => { - expect(matchesSelector("BODY", document.body)).toEqual(true); - }); - - it("should not match selector for non-existing element", () => { - expect(matchesSelector("#bla-bla", document.body)).toEqual(false); - }); -}); diff --git a/test/unit/specs/utils/dom/querySelectorAll.spec.js b/test/unit/specs/utils/dom/querySelectorAll.spec.js deleted file mode 100644 index 79593bc38..000000000 --- a/test/unit/specs/utils/dom/querySelectorAll.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2021 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - appendNode, - createNode, - removeNode, - selectNodes, -} from "../../../../../src/utils/dom/index.js"; -import querySelectorAll from "../../../../../src/utils/dom/querySelectorAll.js"; - -describe("Personalization::DOM::querySelectorAll", () => { - afterEach(() => { - selectNodes(".qsa").forEach(removeNode); - }); - - it("should select with querySelectorAll", () => { - const node = createNode( - "DIV", - { id: "abc", class: "qsa" }, - { - innerHTML: `
    Test
    `, - }, - ); - - appendNode(document.body, node); - - const selector = ".test"; - const result = querySelectorAll(document, selector); - expect(Array.isArray(result)).toBeTrue(); - expect(result[0]).toEqual(node.children[0]); - }); -}); diff --git a/test/unit/specs/utils/dom/removeNode.spec.js b/test/unit/specs/utils/dom/removeNode.spec.js deleted file mode 100644 index 5ec9890d0..000000000 --- a/test/unit/specs/utils/dom/removeNode.spec.js +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import createNode from "../../../../../src/utils/dom/createNode.js"; -import appendNode from "../../../../../src/utils/dom/appendNode.js"; -import removeNode from "../../../../../src/utils/dom/removeNode.js"; -import selectNodes from "../../../../../src/utils/dom/selectNodes.js"; - -describe("DOM::removeNode", () => { - afterEach(() => { - selectNodes("div").forEach(removeNode); - }); - - it("should remove a node from head tag", () => { - const node = createNode("div", { id: "remove" }); - - removeNode(appendNode(document.head, node)); - - expect(selectNodes("#remove").length).toEqual(0); - }); -}); diff --git a/test/unit/specs/utils/dom/selectNodes.spec.js b/test/unit/specs/utils/dom/selectNodes.spec.js deleted file mode 100644 index 55bd284f0..000000000 --- a/test/unit/specs/utils/dom/selectNodes.spec.js +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import selectNodes from "../../../../../src/utils/dom/selectNodes.js"; - -describe("DOM::selectNodes", () => { - it("should return array when nodes are present", () => { - expect(selectNodes("HEAD").length).toEqual(1); - }); - - it("should return array when nodes are NOT present", () => { - expect(selectNodes("FOO").length).toEqual(0); - }); -}); diff --git a/test/unit/specs/utils/dom/selectNodesWithShadow.spec.js b/test/unit/specs/utils/dom/selectNodesWithShadow.spec.js deleted file mode 100644 index e90b7e84d..000000000 --- a/test/unit/specs/utils/dom/selectNodesWithShadow.spec.js +++ /dev/null @@ -1,206 +0,0 @@ -/* -Copyright 2021 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -// eslint-disable-next-line max-classes-per-file -import { - createNode, - appendNode, - selectNodes, - removeNode, -} from "../../../../../src/utils/dom/index.js"; -import { selectNodesWithEq } from "../../../../../src/components/Personalization/dom-actions/dom/index.js"; - -const ieDetected = () => !!document.documentMode; - -const defineCustomElements = () => { - if (!customElements || customElements.get("buy-now-button")) { - return; - } - - const buyNowContent = ` -
    - -
    -
    - -
    -
    -
    - `; - customElements.define( - "buy-now-button", - class extends HTMLElement { - constructor() { - super(); - - const shadowRoot = this.attachShadow({ mode: "open" }); - shadowRoot.innerHTML = buyNowContent; - } - }, - ); - - const productOrderContent = `

    Product order

    Buy
    `; - customElements.define( - "product-order", - class extends HTMLElement { - constructor() { - super(); - - const shadowRoot = this.attachShadow({ mode: "open" }); - shadowRoot.innerHTML = productOrderContent; - } - }, - ); -}; - -describe("Utils::DOM::selectNodesWithShadow", () => { - const CLEANUP_CLASS = "cleanup"; - - afterEach(() => { - selectNodes(`.${CLEANUP_CLASS}`).forEach(removeNode); - }); - - it("should select when no shadow", () => { - appendNode( - document.body, - createNode("DIV", { id: "noShadow", class: CLEANUP_CLASS }), - ); - - const result = selectNodes("#noShadow"); - - expect(result[0].tagName).toEqual("DIV"); - expect(result[0].id).toEqual("noShadow"); - }); - - it("should select when one shadow node", () => { - if (ieDetected()) { - return; - } - - defineCustomElements(); - - const content = ` -
    - FirstButton - SecondButton - -
    `; - - appendNode( - document.body, - createNode( - "DIV", - { id: "abc", class: CLEANUP_CLASS }, - { innerHTML: content }, - ), - ); - - const result = selectNodesWithEq( - "#abc:eq(0) > FORM:nth-of-type(1) > BUY-NOW-BUTTON:nth-of-type(2):shadow > DIV:nth-of-type(1) > LABEL:nth-of-type(1)", - ); - - expect(result.length).toEqual(1); - - expect(result[0].tagName).toEqual("LABEL"); - expect(result[0].textContent).toEqual("Buy Now"); - }); - - it("should select when multiple nested shadow nodes", () => { - if (ieDetected()) { - return; - } - - defineCustomElements(); - - const content = ` -
    - FirstButton - SecondButton - FirstOrder - SecondOrder - -
    `; - - appendNode( - document.body, - createNode( - "DIV", - { id: "abc", class: CLEANUP_CLASS }, - { innerHTML: content }, - ), - ); - - const result = selectNodesWithEq( - "#abc:eq(0) > FORM:nth-of-type(1) > PRODUCT-ORDER:nth-of-type(2):shadow > *:eq(0) > BUY-NOW-BUTTON:nth-of-type(1):shadow > DIV:nth-of-type(1) > LABEL:nth-of-type(1)", - ); - - expect(result[0].tagName).toEqual("LABEL"); - expect(result[0].textContent).toEqual("Buy Now"); - }); - - it("should select when chained :eq:shadow selector", () => { - if (ieDetected()) { - return; - } - - defineCustomElements(); - - const content = ` -
    - FirstButton - SecondButton - FirstOrder - SecondOrder - -
    `; - - appendNode( - document.body, - createNode( - "DIV", - { id: "abc", class: CLEANUP_CLASS }, - { innerHTML: content }, - ), - ); - - const result = selectNodesWithEq( - "#abc:eq(0) > FORM:nth-of-type(1) > PRODUCT-ORDER:eq(1):shadow > *:eq(0) > BUY-NOW-BUTTON:eq(0):shadow > DIV:nth-of-type(1) > LABEL:nth-of-type(1)", - ); - - expect(result[0].tagName).toEqual("LABEL"); - expect(result[0].textContent).toEqual("Buy Now"); - }); - - it("should respect child selectors", () => { - const content = ` -
    -
    - -
    - -
    - `; - - const node = createNode( - "DIV", - { id: "target", class: CLEANUP_CLASS }, - { innerHTML: content }, - ); - - appendNode(document.body, node); - - const result = selectNodesWithEq("#target > div:eq(0) > span"); - - expect(result[0].tagName).toEqual("SPAN"); - expect(result[0].id).toEqual("right"); - }); -}); diff --git a/test/unit/specs/utils/event.spec.js b/test/unit/specs/utils/event.spec.js deleted file mode 100644 index 17c9bec38..000000000 --- a/test/unit/specs/utils/event.spec.js +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { mergeDecisionsMeta, mergeQuery } from "../../../../src/utils/event.js"; -import { PropositionEventType } from "../../../../src/constants/propositionEventType.js"; - -describe("Utils::event", () => { - let event; - - beforeEach(() => { - event = jasmine.createSpyObj("event", ["mergeXdm", "mergeQuery"]); - }); - - describe("mergeDecisionsMeta", () => { - it("merges decisions meta", () => { - const decisionsMeta = [ - { - id: "abc", - scope: "home", - }, - { - id: "def", - scope: "cart", - }, - ]; - mergeDecisionsMeta(event, decisionsMeta, [PropositionEventType.DISPLAY]); - expect(event.mergeXdm).toHaveBeenCalledWith({ - _experience: { - decisioning: { - propositions: [ - { - id: "abc", - scope: "home", - }, - { - id: "def", - scope: "cart", - }, - ], - propositionEventType: { - display: 1, - }, - }, - }, - }); - }); - }); - - describe("mergeQuery", () => { - it("merges query details", () => { - const details = { - foo: "bar", - }; - mergeQuery(event, details); - expect(event.mergeQuery).toHaveBeenCalledWith({ - personalization: { - foo: "bar", - }, - }); - }); - }); -}); diff --git a/test/unit/specs/utils/filterObject.spec.js b/test/unit/specs/utils/filterObject.spec.js deleted file mode 100644 index c67bf89f1..000000000 --- a/test/unit/specs/utils/filterObject.spec.js +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { filterObject } from "../../../../src/utils/index.js"; - -describe("utils:filterObject", () => { - it("should filter out keys with values that do not pass the predicate", () => { - const obj = { - a: 5, - b: 6, - }; - const predicate = (val) => val > 5; - expect(filterObject(obj, predicate)).toEqual({ b: 6 }); - }); - - it("should filter out nested keys with values that do not pass the predicate", () => { - const obj = { - a: 5, - b: { c: 6 }, - }; - const predicate = (val) => val > 5; - expect(filterObject(obj, predicate)).toEqual({ b: { c: 6 } }); - }); - - it("should filter out deeply nested keys with values that do not pass the predicate", () => { - const obj = { - a: 5, - b: { c: { d: 4, e: 6 } }, - f: { g: 4 }, - }; - const predicate = (val) => val > 5; - expect(filterObject(obj, predicate)).toEqual({ b: { c: { e: 6 } } }); - }); -}); diff --git a/test/unit/specs/utils/fireImage.spec.js b/test/unit/specs/utils/fireImage.spec.js deleted file mode 100644 index 7aecea8ff..000000000 --- a/test/unit/specs/utils/fireImage.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -// eslint-disable-next-line no-unused-vars -import fireImage from "../../../../src/utils/fireImage.js"; diff --git a/test/unit/specs/utils/flattenArray.spec.js b/test/unit/specs/utils/flattenArray.spec.js deleted file mode 100644 index 845172940..000000000 --- a/test/unit/specs/utils/flattenArray.spec.js +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import flattenArray from "../../../../src/utils/flattenArray.js"; - -describe("flattenArray", () => { - it("recursively flattens an array", () => { - expect( - flattenArray([ - "a", - ["b", "c"], - "d", - ["e"], - "f", - ["g"], - [ - "h", - [ - "i", - ["j"], - "k", - ["l", ["m"], ["n", ["o"], ["p", ["q"], "r"], "s"], "t"], - ], - "u", - ], - "v", - "w", - "x", - "y", - "z", - ]), - ).toEqual([ - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", - ]); - }); - - it("handles non arrays", () => { - expect(flattenArray({ wat: true })).toEqual({ wat: true }); - }); -}); diff --git a/test/unit/specs/utils/flattenObject.spec.js b/test/unit/specs/utils/flattenObject.spec.js deleted file mode 100644 index 1388d2f6d..000000000 --- a/test/unit/specs/utils/flattenObject.spec.js +++ /dev/null @@ -1,116 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import flattenObject from "../../../../src/utils/flattenObject.js"; - -describe("flattenObject", () => { - it("flattens event object", () => { - expect( - flattenObject({ - xdm: { - web: { - webPageDetails: { - viewName: "contact", - URL: "https://localhost/aep.html#contact", - }, - webReferrer: { - URL: "https://google.com", - }, - }, - timestamp: "2023-04-12T17:37:56.519Z", - implementationDetails: { - name: "https://ns.adobe.com/experience/alloy", - version: "2.15.0", - environment: "browser", - }, - }, - data: { - moo: "woof", - }, - }), - ).toEqual({ - "xdm.web.webPageDetails.viewName": "contact", - "xdm.web.webPageDetails.URL": "https://localhost/aep.html#contact", - "xdm.web.webReferrer.URL": "https://google.com", - "xdm.timestamp": "2023-04-12T17:37:56.519Z", - "xdm.implementationDetails.name": "https://ns.adobe.com/experience/alloy", - "xdm.implementationDetails.version": "2.15.0", - "xdm.implementationDetails.environment": "browser", - "data.moo": "woof", - }); - }); - - it("flattens nested arrays", () => { - expect( - flattenObject({ - pre: true, - a: { - one: 1, - two: 2, - three: { - aa: 2, - bb: 43, - cc: [ - "alf", - "fred", - { - cool: "beans", - lets: "go", - }, - ], - }, - }, - b: { - one: 1, - two: 2, - three: { - poo: true, - }, - }, - c: { - uno: true, - dos: false, - tres: { - value: "yeah ok", - }, - }, - }), - ).toEqual({ - pre: true, - "a.one": 1, - "a.two": 2, - "a.three.aa": 2, - "a.three.bb": 43, - "a.three.cc.0": "alf", - "a.three.cc.1": "fred", - "a.three.cc.2.cool": "beans", - "a.three.cc.2.lets": "go", - "b.one": 1, - "b.two": 2, - "b.three.poo": true, - "c.uno": true, - "c.dos": false, - "c.tres.value": "yeah ok", - }); - }); - - it("handles non-objects", () => { - expect(flattenObject(true)).toEqual(true); - expect(flattenObject([1, 2, 3])).toEqual([1, 2, 3]); - expect(flattenObject("hello")).toEqual("hello"); - - let obj = new Set(); - expect(obj).toEqual(obj); - - obj = () => undefined; - expect(flattenObject(obj)).toEqual(obj); - }); -}); diff --git a/test/unit/specs/utils/getApexDomain.spec.js b/test/unit/specs/utils/getApexDomain.spec.js deleted file mode 100644 index a8aac21e8..000000000 --- a/test/unit/specs/utils/getApexDomain.spec.js +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getApexDomain from "../../../../src/utils/getApexDomain.js"; - -const mockWindowWithHostname = (hostname) => { - return { - location: { - hostname, - }, - }; -}; - -describe("getTld", () => { - it("returns an empty string when only one host part exists", () => { - const window = mockWindowWithHostname("localhost"); - const cookieJar = { - get() {}, - set() {}, - remove() {}, - }; - expect(getApexDomain(window, cookieJar)).toBe(""); - }); - - it("returns the first host that allows a cookie to be set", () => { - const window = mockWindowWithHostname("a.b.c.co.uk"); - let storedValue; - const cookieJar = { - get() { - return storedValue; - }, - set(name, value, options) { - if (options.domain === "c.co.uk") { - storedValue = value; - } - }, - remove: jasmine.createSpy(), - }; - - expect(getApexDomain(window, cookieJar)).toBe("c.co.uk"); - expect(cookieJar.remove).toHaveBeenCalled(); - }); - - it("tries all segments of the hostname if necessary", () => { - const window = mockWindowWithHostname("10.30.34.68"); - let storedValue; - const cookieJar = { - get() { - return storedValue; - }, - set(name, value, options) { - if (options.domain === "10.30.34.68") { - storedValue = value; - } - }, - remove: jasmine.createSpy(), - }; - - expect(getApexDomain(window, cookieJar)).toBe("10.30.34.68"); - expect(cookieJar.remove).toHaveBeenCalled(); - }); -}); diff --git a/test/unit/specs/utils/getLastArrayItems.spec.js b/test/unit/specs/utils/getLastArrayItems.spec.js deleted file mode 100644 index 82eff1393..000000000 --- a/test/unit/specs/utils/getLastArrayItems.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getLastArrayItems from "../../../../src/utils/getLastArrayItems.js"; - -describe("getLastArrayItems", () => { - const letters = ["a", "b", "c"]; - - it("returns last items from array larger than count", () => { - expect(getLastArrayItems(letters, 2)).toEqual(["b", "c"]); - }); - - it("returns all items from array smaller than or equal to count", () => { - expect(getLastArrayItems(letters, 10)).toEqual(["a", "b", "c"]); - expect(getLastArrayItems(letters, 3)).toEqual(["a", "b", "c"]); - }); -}); diff --git a/test/unit/specs/utils/getNamespacedCookieName.spec.js b/test/unit/specs/utils/getNamespacedCookieName.spec.js deleted file mode 100644 index 5185f1d99..000000000 --- a/test/unit/specs/utils/getNamespacedCookieName.spec.js +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import getNamespacedCookieName from "../../../../src/utils/getNamespacedCookieName.js"; - -describe("getNamespacedCookieName", () => { - it("returns namespaced cookie name", () => { - const result = getNamespacedCookieName("ABC@CustomOrg", "foo"); - expect(result).toBe("kndctr_ABC_CustomOrg_foo"); - }); -}); diff --git a/test/unit/specs/utils/getNamespacedStorage.spec.js b/test/unit/specs/utils/getNamespacedStorage.spec.js deleted file mode 100644 index c1627d523..000000000 --- a/test/unit/specs/utils/getNamespacedStorage.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectStorage from "../../../../src/utils/injectStorage.js"; - -const getNamespacedStorage = injectStorage(window); -const storage = getNamespacedStorage("namespace"); - -describe("getNamespacedStorage", () => { - it("is able to write and read from session storage", () => { - storage.session.setItem("test", "session-storage"); - expect(storage.session.getItem("test")).toBe("session-storage"); - }); - it("is able to write and read from persistent storage", () => { - storage.persistent.setItem("test", "persistent-storage"); - expect(storage.persistent.getItem("test")).toBe("persistent-storage"); - }); -}); diff --git a/test/unit/specs/utils/groupBy.spec.js b/test/unit/specs/utils/groupBy.spec.js deleted file mode 100644 index 2f953537b..000000000 --- a/test/unit/specs/utils/groupBy.spec.js +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import groupBy from "../../../../src/utils/groupBy.js"; - -describe("groupBy", () => { - it("expects empty obj if array is empty", () => { - const array = []; - expect(groupBy(array, null)).toEqual({}); - }); - - it("expects to group by key getter provided", () => { - const array = [ - { id: 1, name: "Foo" }, - { id: 2, name: "Foo2" }, - { id: 2, name: "Foo3" }, - ]; - - const map = { - 1: [{ id: 1, name: "Foo" }], - 2: [ - { id: 2, name: "Foo2" }, - { id: 2, name: "Foo3" }, - ], - }; - - expect(groupBy(array, (item) => item.id || "default")).toEqual(map); - }); - - it("expects to group by key getter provided or to the default key", () => { - const array = [ - { id: 1, name: "Foo" }, - { id: 2, name: "Foo2" }, - { id: 2, name: "Foo3" }, - { noId: 2, name: "Foo3" }, - ]; - - const map = { - 1: [{ id: 1, name: "Foo" }], - 2: [ - { id: 2, name: "Foo2" }, - { id: 2, name: "Foo3" }, - ], - default: [{ noId: 2, name: "Foo3" }], - }; - - expect(groupBy(array, (item) => item.id || "default")).toEqual(map); - }); -}); diff --git a/test/unit/specs/utils/injectAreThirdPartyCookiesSupportedByDefault.spec.js b/test/unit/specs/utils/injectAreThirdPartyCookiesSupportedByDefault.spec.js deleted file mode 100644 index f688d4075..000000000 --- a/test/unit/specs/utils/injectAreThirdPartyCookiesSupportedByDefault.spec.js +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectAreThirdPartyCookiesSupportedByDefault from "../../../../src/utils/injectAreThirdPartyCookiesSupportedByDefault.js"; -import { - CHROME, - EDGE, - EDGE_CHROMIUM, - FIREFOX, - IE, - SAFARI, - UNKNOWN, -} from "../../../../src/constants/browser.js"; - -const browsersWithSupport = [CHROME, EDGE, EDGE_CHROMIUM, IE, UNKNOWN]; -const browsersWithoutSupport = [FIREFOX, SAFARI]; - -describe("areThirdPartyCookiesSupportedByDefault", () => { - let getBrowser; - let areThirdPartyCookiesSupportedByDefault; - - beforeEach(() => { - getBrowser = jasmine.createSpy(); - areThirdPartyCookiesSupportedByDefault = - injectAreThirdPartyCookiesSupportedByDefault({ getBrowser }); - }); - - browsersWithSupport.forEach((browser) => { - it(`reports true for ${browser}`, () => { - getBrowser.and.returnValue(browser); - expect(areThirdPartyCookiesSupportedByDefault()).toBeTrue(); - }); - }); - - browsersWithoutSupport.forEach((browser) => { - it(`reports false for ${browser}`, () => { - getBrowser.and.returnValue(browser); - expect(areThirdPartyCookiesSupportedByDefault()).toBeFalse(); - }); - }); -}); diff --git a/test/unit/specs/utils/injectDoesIdentityCookieExist.spec.js b/test/unit/specs/utils/injectDoesIdentityCookieExist.spec.js deleted file mode 100644 index 0a72f2b85..000000000 --- a/test/unit/specs/utils/injectDoesIdentityCookieExist.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - injectDoesIdentityCookieExist, - cookieJar, -} from "../../../../src/utils/index.js"; -import removeAllCookies from "../../helpers/removeAllCookies.js"; - -describe("Identity::injectDoesIdentityCookieExist", () => { - beforeEach(removeAllCookies); - afterEach(removeAllCookies); - - it("returns false if cookie does not exist", () => { - const doesIdentityCookieExist = injectDoesIdentityCookieExist({ - orgId: "org@adobe", - }); - expect(doesIdentityCookieExist()).toBeFalse(); - }); - - it("returns true if cookie exists", () => { - cookieJar.set("kndctr_org_adobe_identity", "user@adobe"); - const doesIdentityCookieExist = injectDoesIdentityCookieExist({ - orgId: "org@adobe", - }); - expect(doesIdentityCookieExist()).toBeTrue(); - }); -}); diff --git a/test/unit/specs/utils/injectFireReferrerHideableImage.spec.js b/test/unit/specs/utils/injectFireReferrerHideableImage.spec.js deleted file mode 100644 index e1df08f81..000000000 --- a/test/unit/specs/utils/injectFireReferrerHideableImage.spec.js +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectFireReferrerHideableImage from "../../../../src/utils/injectFireReferrerHideableImage.js"; - -describe("injectFireReferrerHideableImage", () => { - let appendNodeMock; - let awaitSelectorMock; - let createNodeMock; - let fireImageMock; - let fireReferrerHideableImage; - - beforeEach(() => { - appendNodeMock = jasmine - .createSpy("appendNode") - .and.callFake(() => ({ contentWindow: { document: {} } })); - awaitSelectorMock = jasmine - .createSpy("awaitSelector") - .and.callFake(() => Promise.resolve(["body"])); - createNodeMock = jasmine - .createSpy("createNode") - .and.callFake(() => ({ contentWindow: { document: {} } })); - fireImageMock = jasmine - .createSpy("fireImage") - .and.callFake(() => Promise.resolve()); - fireReferrerHideableImage = injectFireReferrerHideableImage({ - appendNode: appendNodeMock, - awaitSelector: awaitSelectorMock, - createNode: createNodeMock, - fireImage: fireImageMock, - }); - }); - - it("should create an iframe for a request that hides the referrer", async () => { - const request = { - hideReferrer: true, - url: "https://adobe.com/test-referrer.jpg", - }; - await fireReferrerHideableImage(request); - - expect(createNodeMock).toHaveBeenCalled(); - expect(createNodeMock.calls.argsFor(0)).toContain("IFRAME"); - expect(fireImageMock).toHaveBeenCalled(); - }); - - it("should fire the image on the page for a request that does not hide the referrer", async () => { - const request = { - hideReferrer: false, - url: "https://adobe.com/test-referrer.jpg", - }; - await fireReferrerHideableImage(request); - - expect(createNodeMock).not.toHaveBeenCalled(); - expect(fireImageMock).toHaveBeenCalled(); - }); - - it("should only create one iframe when called multiple times", async () => { - const request = { - hideReferrer: true, - url: "https://adobe.com/test-invalid-referrer.jpg", - }; - const secondRequest = { - hideReferrer: true, - url: "https://adobe.com/test-invalid-referrer2.jpg", - }; - - await fireReferrerHideableImage(request); - await fireReferrerHideableImage(secondRequest); - - expect(createNodeMock).toHaveBeenCalledTimes(1); - expect(createNodeMock.calls.argsFor(0)).toContain("IFRAME"); - expect(fireImageMock).toHaveBeenCalledTimes(2); - }); -}); diff --git a/test/unit/specs/utils/injectGetBrowser.spec.js b/test/unit/specs/utils/injectGetBrowser.spec.js deleted file mode 100644 index 9de0514e5..000000000 --- a/test/unit/specs/utils/injectGetBrowser.spec.js +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectGetBrowser from "../../../../src/utils/injectGetBrowser.js"; - -import { - EDGE, - EDGE_CHROMIUM, - CHROME, - FIREFOX, - IE, - SAFARI, - UNKNOWN, -} from "../../../../src/constants/browser.js"; - -const userAgentsByBrowser = { - [EDGE]: [ - "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10136", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134", - ], - [EDGE_CHROMIUM]: [ - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3763.0 Safari/537.36 Edg/75.0.131.0", - ], - // Chrome on iOS will not be detected as Chrome. That is because Chrome on - // iOS is just Safari wrapped in Chrome. - [CHROME]: [ - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36", - "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36", - ], - [FIREFOX]: [ - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:69.0) Gecko/20100101 Firefox/69.0", - "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0", - ], - [IE]: [ - "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; Tablet PC 2.0; rv:11.0) like Gecko", - "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko", - ], - [SAFARI]: [ - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15", - "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13 Mobile/15E148 Safari/604.1", - ], - [UNKNOWN]: [ - "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 OPR/43.0.2442.991", // Opera - "Mozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9", // Konqueror - ], -}; - -describe("getBrowser", () => { - Object.keys(userAgentsByBrowser).forEach((browser) => { - const userAgents = userAgentsByBrowser[browser]; - userAgents.forEach((userAgent) => { - it(`reports ${browser} for ${userAgent}`, () => { - expect(injectGetBrowser({ userAgent })()).toBe(browser); - }); - }); - }); -}); diff --git a/test/unit/specs/utils/injectStorage.spec.js b/test/unit/specs/utils/injectStorage.spec.js deleted file mode 100644 index b28168f0b..000000000 --- a/test/unit/specs/utils/injectStorage.spec.js +++ /dev/null @@ -1,111 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import injectStorage from "../../../../src/utils/injectStorage.js"; - -describe("injectStorage", () => { - [ - { - storageProperty: "session", - windowProperty: "sessionStorage", - }, - { - storageProperty: "persistent", - windowProperty: "localStorage", - }, - ].forEach(({ storageProperty, windowProperty }) => { - describe(storageProperty, () => { - describe("setItem", () => { - it("sets item", () => { - const window = { - [windowProperty]: { - setItem: jasmine.createSpy().and.returnValue(true), - }, - }; - const storage = injectStorage(window)("example."); - const result = storage[storageProperty].setItem("foo", "bar"); - expect(window[windowProperty].setItem).toHaveBeenCalledWith( - "com.adobe.alloy.example.foo", - "bar", - ); - expect(result).toBeTrue(); - }); - - it("returns false if an error occurs setting item", () => { - const window = { - [windowProperty]: { - setItem: jasmine.createSpy().and.throwError(), - }, - }; - const storage = injectStorage(window)("example."); - const result = storage[storageProperty].setItem("foo", "bar"); - expect(result).toBeFalse(); - }); - }); - - describe("getItem", () => { - it("gets item", () => { - const window = { - [windowProperty]: { - getItem: jasmine.createSpy().and.returnValue("abc"), - }, - }; - const storage = injectStorage(window)("example."); - const result = storage[storageProperty].getItem("foo"); - expect(window[windowProperty].getItem).toHaveBeenCalledWith( - "com.adobe.alloy.example.foo", - ); - expect(result).toBe("abc"); - }); - - it("returns null if an error occurs while getting item", () => { - const window = { - [windowProperty]: { - getItem: jasmine.createSpy().and.throwError(), - }, - }; - const storage = injectStorage(window)("example."); - const result = storage[storageProperty].getItem("foo"); - expect(result).toBeNull(); - }); - }); - - describe("clear", () => { - it("clears all with the namespace prefix", () => { - const window = { - [windowProperty]: { - removeItem: jasmine.createSpy(), - "com.adobe.alloy.example.a": "1", - "com.adobe.alloy.example.b": "2", - c: "3", - "com.adobe.alloy.d": "4", - }, - }; - const storage = injectStorage(window)("example."); - storage[storageProperty].clear(); - expect(window[windowProperty].removeItem).toHaveBeenCalledWith( - "com.adobe.alloy.example.a", - ); - expect(window[windowProperty].removeItem).toHaveBeenCalledWith( - "com.adobe.alloy.example.b", - ); - expect(window[windowProperty].removeItem).not.toHaveBeenCalledWith( - "c", - ); - expect(window[windowProperty].removeItem).not.toHaveBeenCalledWith( - "com.adobe.alloy.d", - ); - }); - }); - }); - }); -}); diff --git a/test/unit/specs/utils/intersection.spec.js b/test/unit/specs/utils/intersection.spec.js deleted file mode 100644 index f7e720226..000000000 --- a/test/unit/specs/utils/intersection.spec.js +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import intersection from "../../../../src/utils/intersection.js"; - -describe("intersection", () => { - it("returns items that are found within both arrays", () => { - const result = intersection(["a", "b", "c", "d"], ["z", "b", "d"]); - expect(result).toEqual(["b", "d"]); - }); - - it("returns an empty array if there are no matches", () => { - const result = intersection(["a", "b", "c", "d"], ["e"]); - expect(result).toEqual([]); - }); -}); diff --git a/test/unit/specs/utils/isBlankString.spec.js b/test/unit/specs/utils/isBlankString.spec.js deleted file mode 100644 index 72c256bfd..000000000 --- a/test/unit/specs/utils/isBlankString.spec.js +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2021 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isBlankString from "../../../../src/utils/isBlankString.js"; - -describe("isBlankString", () => { - it("returns true when null", () => { - expect(isBlankString(null)).toBe(true); - }); - - it("returns true when not a string", () => { - expect(isBlankString(42)).toBe(true); - }); - - it("returns true for a blank string", () => { - expect(isBlankString("")).toBe(true); - }); - - it("returns false for a string", () => { - expect(isBlankString("hi")).toBe(false); - }); -}); diff --git a/test/unit/specs/utils/isBoolean.spec.js b/test/unit/specs/utils/isBoolean.spec.js deleted file mode 100644 index d796f72d5..000000000 --- a/test/unit/specs/utils/isBoolean.spec.js +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isBoolean from "../../../../src/utils/isBoolean.js"; - -const nonBooleans = [{}, [], new Date(), /abc/, "foo", 123]; - -describe("isString", () => { - it("returns true if the value is boolean", () => { - expect(isBoolean(true)).toBe(true); - expect(isBoolean(false)).toBe(true); - }); - - it("returns false if the value is not a boolean", () => { - nonBooleans.forEach((value) => { - expect(isBoolean(value)).toBe(false); - }); - }); -}); diff --git a/test/unit/specs/utils/isEmptyObject.spec.js b/test/unit/specs/utils/isEmptyObject.spec.js deleted file mode 100644 index 439612dda..000000000 --- a/test/unit/specs/utils/isEmptyObject.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isEmptyObject from "../../../../src/utils/isEmptyObject.js"; - -const nonEmptyObjects = ["abc", { a: 123 }]; - -describe("isEmptyObject", () => { - it("returns true if the value is an empty object", () => { - expect(isEmptyObject({})).toBe(true); - }); - - it("returns false if the value is not an empty object", () => { - nonEmptyObjects.forEach((obj) => { - expect(isEmptyObject(obj)).toBe(false); - }); - }); -}); diff --git a/test/unit/specs/utils/isFunction.spec.js b/test/unit/specs/utils/isFunction.spec.js deleted file mode 100644 index 526ae0f76..000000000 --- a/test/unit/specs/utils/isFunction.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isFunction from "../../../../src/utils/isFunction.js"; - -const nonFunctions = [{}, [], new Date(), /abc/, true, false, "text", 123]; - -describe("isFunction", () => { - it("returns true if the value is a function", () => { - expect(isFunction(() => {})).toBe(true); - }); - - it("returns false if the value is not a function", () => { - nonFunctions.forEach((nonFunction) => { - expect(isFunction(nonFunction)).toBe(false); - }); - }); -}); diff --git a/test/unit/specs/utils/isInteger.spec.js b/test/unit/specs/utils/isInteger.spec.js deleted file mode 100644 index d0a28c0a8..000000000 --- a/test/unit/specs/utils/isInteger.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isInteger from "../../../../src/utils/isInteger.js"; - -describe("isInteger", () => { - it("returns true if the value is an integer", () => { - [123, -123].forEach((value) => expect(isInteger(value)).toBe(true)); - }); - - // eslint-disable-next-line no-restricted-globals - it("returns false if the value is not an integer", () => { - [null, undefined, NaN, "abc", "123", 123.45, -123.45].forEach((value) => - expect(isInteger(value)).toBe(false), - ); - }); -}); diff --git a/test/unit/specs/utils/isNamespacedCookieName.spec.js b/test/unit/specs/utils/isNamespacedCookieName.spec.js deleted file mode 100644 index ce893ea5b..000000000 --- a/test/unit/specs/utils/isNamespacedCookieName.spec.js +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isNamespacedCookieName from "../../../../src/utils/isNamespacedCookieName.js"; - -describe("isNamespacedCookieName", () => { - it("returns true if it's a namespaced cookie name", () => { - const result = isNamespacedCookieName( - "ABC@CustomOrg", - "kndctr_ABC_CustomOrg_foo", - ); - expect(result).toBeTrue(); - }); - - it("returns false if it's not a namespaced cookie name", () => { - const result = isNamespacedCookieName( - "kndctr_DEF_CustomOrg_foo", - "ABC@CustomOrg", - ); - expect(result).toBeFalse(); - }); -}); diff --git a/test/unit/specs/utils/isNil.spec.js b/test/unit/specs/utils/isNil.spec.js deleted file mode 100644 index cbf2de7cc..000000000 --- a/test/unit/specs/utils/isNil.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isNil from "../../../../src/utils/isNil.js"; - -describe("isNil", () => { - it("returns true when null", () => { - expect(isNil(null)).toBe(true); - }); - - it("returns true when undefined", () => { - expect(isNil(undefined)).toBe(true); - }); - - it("returns false when value", () => { - expect(isNil(1)).toBe(false); - expect(isNil({})).toBe(false); - expect(isNil("aaa")).toBe(false); - expect(isNil(true)).toBe(false); - expect(isNil(false)).toBe(false); - expect(isNil([])).toBe(false); - }); -}); diff --git a/test/unit/specs/utils/isNonEmptyArray.spec.js b/test/unit/specs/utils/isNonEmptyArray.spec.js deleted file mode 100644 index 45e644bf9..000000000 --- a/test/unit/specs/utils/isNonEmptyArray.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isNonEmptyArray from "../../../../src/utils/isNonEmptyArray.js"; - -describe("isNonEmptyArray", () => { - it("returns true when array with values", () => { - expect(isNonEmptyArray([1, 2, 3])).toBe(true); - }); - - it("returns false when array is empty", () => { - expect(isNonEmptyArray([])).toBe(false); - }); - - it("returns false when undefined or null", () => { - expect(isNonEmptyArray(undefined)).toBe(false); - expect(isNonEmptyArray(null)).toBe(false); - }); - - it("returns false when non array", () => { - expect(isNonEmptyArray("123")).toBe(false); - }); -}); diff --git a/test/unit/specs/utils/isNonEmptyString.spec.js b/test/unit/specs/utils/isNonEmptyString.spec.js deleted file mode 100644 index 2da0f25c3..000000000 --- a/test/unit/specs/utils/isNonEmptyString.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isNonEmptyString from "../../../../src/utils/isNonEmptyString.js"; - -describe("isNonEmptyString", () => { - it("returns true when string", () => { - expect(isNonEmptyString("1234")).toBe(true); - }); - - it("returns false when empty string", () => { - expect(isNonEmptyString("")).toBe(false); - }); - - it("returns false when undefined string", () => { - expect(isNonEmptyString(undefined)).toBe(false); - }); -}); diff --git a/test/unit/specs/utils/isNumber.spec.js b/test/unit/specs/utils/isNumber.spec.js deleted file mode 100644 index a3faa3a22..000000000 --- a/test/unit/specs/utils/isNumber.spec.js +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isNumber from "../../../../src/utils/isNumber.js"; - -describe("isNumber", () => { - it("returns true if the value is a number", () => { - [123, 123.45, -123, -123.45].forEach((value) => - expect(isNumber(value)).toBe(true), - ); - }); - - // eslint-disable-next-line no-restricted-globals - it("returns false if the value is not a number", () => { - [null, undefined, NaN, "abc", "123"].forEach((value) => - expect(isNumber(value)).toBe(false), - ); - }); -}); diff --git a/test/unit/specs/utils/isObject.spec.js b/test/unit/specs/utils/isObject.spec.js deleted file mode 100644 index 9bae50a9d..000000000 --- a/test/unit/specs/utils/isObject.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isObject from "../../../../src/utils/isObject.js"; - -const nonObjects = [[], true, false, 123]; - -describe("isObject", () => { - it("returns true if the value is an object", () => { - expect(isObject({})).toBe(true); - }); - - it("returns false if the value is not an object", () => { - nonObjects.forEach((obj) => { - expect(isObject(obj)).toBe(false); - }); - }); -}); diff --git a/test/unit/specs/utils/isString.spec.js b/test/unit/specs/utils/isString.spec.js deleted file mode 100644 index df84bb329..000000000 --- a/test/unit/specs/utils/isString.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isString from "../../../../src/utils/isString.js"; - -const nonStrings = [{}, [], new Date(), /abc/, true, false, 123]; - -describe("isString", () => { - it("returns true if the value is a string", () => { - expect(isString("123")).toBe(true); - }); - - it("returns false if the value is not a string", () => { - nonStrings.forEach((str) => { - expect(isString(str)).toBe(false); - }); - }); -}); diff --git a/test/unit/specs/utils/isUnique.spec.js b/test/unit/specs/utils/isUnique.spec.js deleted file mode 100644 index 2cf9e193d..000000000 --- a/test/unit/specs/utils/isUnique.spec.js +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isUnique from "../../../../src/utils/isUnique.js"; - -describe("isUnique", () => { - it("returns true if array values are unique", () => { - expect(isUnique(["item1", "item2", "item3"])).toBe(true); - }); - - it("returns false if array contains duplicate values", () => { - expect(isUnique(["item1", "item1", "item3"])).toBe(false); - }); -}); diff --git a/test/unit/specs/utils/isValidRegExp.spec.js b/test/unit/specs/utils/isValidRegExp.spec.js deleted file mode 100644 index 2e60cf672..000000000 --- a/test/unit/specs/utils/isValidRegExp.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import isValidRegExp from "../../../../src/utils/isValidRegExp.js"; - -describe("isValidRegExp", () => { - ["steel|bronze", "/a/", "/^[a-z0-9+]:///i"].forEach((value) => { - it(`validates ${value}`, () => { - expect(isValidRegExp(value)).toBe(true); - }); - }); - ["[", "*"].forEach((value) => { - it(`rejects ${value}`, () => { - expect(isValidRegExp(value)).toBe(false); - }); - }); -}); diff --git a/test/unit/specs/utils/lazy.spec.js b/test/unit/specs/utils/lazy.spec.js deleted file mode 100644 index 558f40d87..000000000 --- a/test/unit/specs/utils/lazy.spec.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import lazy from "../../../../src/utils/lazy.js"; - -describe("lazy", () => { - let factory; - let getter; - beforeEach(() => { - factory = jasmine.createSpy("factory"); - factory.and.returnValue("result"); - getter = lazy(factory); - }); - - it("doesn't call the factory function before the first time the getter is called", () => { - expect(factory).not.toHaveBeenCalled(); - }); - - it("calls the factory function the first time the getter is called", () => { - getter(); - expect(factory).toHaveBeenCalledTimes(1); - }); - - it("doesn't call the factory function the second time the getter is called", () => { - getter(); - getter(); - expect(factory).toHaveBeenCalledTimes(1); - }); - - it("returns the result of the factory function", () => { - expect(getter()).toBe("result"); - }); - - it("returns the same result the second time the getter is called", () => { - const result = getter(); - expect(getter()).toBe(result); - }); - - it("handles factory functions that return undefined", () => { - factory.and.returnValue(undefined); - getter(); - expect(getter()).toBeUndefined(); - expect(factory).toHaveBeenCalledTimes(1); - }); -}); diff --git a/test/unit/specs/utils/noop.spec.js b/test/unit/specs/utils/noop.spec.js deleted file mode 100644 index 1d27ac3ba..000000000 --- a/test/unit/specs/utils/noop.spec.js +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import noop from "../../../../src/utils/noop.js"; - -describe("noop", () => { - it("accepts any arguments and returns undefined", () => { - expect(noop("foo")).toBeUndefined(); - }); -}); diff --git a/test/unit/specs/utils/parseUrl.spec.js b/test/unit/specs/utils/parseUrl.spec.js deleted file mode 100644 index 75e679543..000000000 --- a/test/unit/specs/utils/parseUrl.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import parseUrl from "../../../../src/utils/parseUrl.js"; - -describe("parseUrl", () => { - it("should parse a valid URL with all components", () => { - const url = "https://example.com/path/to/page?param=value#section"; - - const result = parseUrl(url); - expect(result.path).toBe("/path/to/page"); - expect(result.query).toBe("param=value"); - expect(result.fragment).toBe("section"); - expect(result.domain).toBe("example.com"); - expect(result.subdomain).toBe(""); - expect(result.topLevelDomain).toBe("com"); - }); - - it("should handle URL without subdomain", () => { - const url = "https://example.com"; - - const result = parseUrl(url); - - expect(result.path).toBe(""); - expect(result.query).toBe(""); - expect(result.fragment).toBe(""); - expect(result.domain).toBe("example.com"); - expect(result.subdomain).toBe(""); - expect(result.topLevelDomain).toBe("com"); - }); - - it("should handle empty URL and return default values", () => { - const url = ""; - - const result = parseUrl(url); - expect(result.path).toBe(""); - expect(result.query).toBe(""); - expect(result.fragment).toBe(""); - expect(result.domain).toBe(""); - expect(result.subdomain).toBe(""); - expect(result.topLevelDomain).toBe(""); - }); - - it("should handle URL with subdomain", () => { - const url = "https://www.example.com"; - - const result = parseUrl(url); - expect(result.path).toBe(""); - expect(result.query).toBe(""); - expect(result.fragment).toBe(""); - expect(result.domain).toBe("www.example.com"); - expect(result.subdomain).toBe(""); - expect(result.topLevelDomain).toBe("com"); - }); -}); diff --git a/test/unit/specs/utils/prepareConfigOverridesForEdge.spec.js b/test/unit/specs/utils/prepareConfigOverridesForEdge.spec.js deleted file mode 100644 index a61bc31f2..000000000 --- a/test/unit/specs/utils/prepareConfigOverridesForEdge.spec.js +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { prepareConfigOverridesForEdge } from "../../../../src/utils/index.js"; - -describe("utils:prepareConfigOverridesForEdge", () => { - it("should filter out functions, unused objects and keys, empty arrays, and empty strings", () => { - expect( - prepareConfigOverridesForEdge({ - com_adobe_experience_platform: { - datasets: { - event: { datasetId: "werewr" }, - profile: { datasetId: "" }, - }, - enabled: false, - }, - com_adobe_analytics: { - reportSuites: [], - }, - com_adobe_identity: {}, - com_adobe_target: { - propertyToken: "rrr", - environmentId: 0, - }, - toString: () => "{ com_adobe_experience_platform: '' }", - }), - ).toEqual({ - com_adobe_experience_platform: { - datasets: { - event: { datasetId: "werewr" }, - }, - enabled: false, - }, - com_adobe_target: { - propertyToken: "rrr", - environmentId: 0, - }, - }); - }); - - it("should return null for empty config objects", () => { - expect( - prepareConfigOverridesForEdge({ - com_adobe_experience_platform: { - datasets: { - event: { datasetId: "" }, - profile: { datasetId: "" }, - }, - }, - com_adobe_analytics: { - reportSuites: [], - }, - com_adobe_identity: { - idSyncContainerId: "", - }, - com_adobe_target: { - propertyToken: "", - }, - }), - ).toBeNull(); - }); -}); diff --git a/test/unit/specs/utils/querystring.spec.js b/test/unit/specs/utils/querystring.spec.js deleted file mode 100644 index 0ff717eb9..000000000 --- a/test/unit/specs/utils/querystring.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -// eslint-disable-next-line no-unused-vars -import querystring from "../../../../src/utils/querystring.js"; diff --git a/test/unit/specs/utils/request/createAddIdentity.spec.js b/test/unit/specs/utils/request/createAddIdentity.spec.js deleted file mode 100644 index 6b761ad68..000000000 --- a/test/unit/specs/utils/request/createAddIdentity.spec.js +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { createAddIdentity } from "../../../../../src/utils/request/index.js"; - -describe("createAddIdentity", () => { - it("should return a function to add identity", () => { - const content = {}; - const addIdentity = createAddIdentity(content); - expect(typeof addIdentity).toBe("function"); - addIdentity("IDNS", { - id: "ABC123", - }); - expect(content).toEqual({ - xdm: { - identityMap: { - IDNS: [ - { - id: "ABC123", - }, - ], - }, - }, - }); - }); - it("should append identity map if called more than once", () => { - const content = {}; - const addIdentity = createAddIdentity(content); - addIdentity("IDNS", { - id: "ABC123", - }); - addIdentity("IDNS", { - id: "ABC456", - }); - expect(content).toEqual({ - xdm: { - identityMap: { - IDNS: [ - { - id: "ABC123", - }, - { - id: "ABC456", - }, - ], - }, - }, - }); - addIdentity("IDNS2", { - id: "ABC456", - }); - expect(content).toEqual({ - xdm: { - identityMap: { - IDNS: [ - { - id: "ABC123", - }, - { - id: "ABC456", - }, - ], - IDNS2: [ - { - id: "ABC456", - }, - ], - }, - }, - }); - }); -}); diff --git a/test/unit/specs/utils/request/createDataCollectionRequest.spec.js b/test/unit/specs/utils/request/createDataCollectionRequest.spec.js deleted file mode 100644 index 077eccd4c..000000000 --- a/test/unit/specs/utils/request/createDataCollectionRequest.spec.js +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { createDataCollectionRequest } from "../../../../../src/utils/request/index.js"; -import describeRequest from "../../../helpers/describeRequest.js"; - -describe("createDataCollectionRequest", () => { - describeRequest(createDataCollectionRequest); - - it("uses collect with sendBeacon if document may unload and identity is established", () => { - const payload = { - getDocumentMayUnload() { - return true; - }, - }; - const request = createDataCollectionRequest({ payload }); - request.setIsIdentityEstablished(); - expect(request.getAction()).toBe("collect"); - expect(request.getUseSendBeacon()).toBeTrue(); - }); - - it("uses interact without sendBeacon if document may unload but identity has not been established", () => { - const payload = { - getDocumentMayUnload() { - return true; - }, - }; - const request = createDataCollectionRequest({ payload }); - expect(request.getAction()).toBe("interact"); - expect(request.getUseSendBeacon()).toBeFalse(); - }); - - it("uses interact without sendBeacon if identity has been established but document will not unload", () => { - const payload = { - getDocumentMayUnload() { - return false; - }, - }; - const request = createDataCollectionRequest({ payload }); - request.setIsIdentityEstablished(); - expect(request.getAction()).toBe("interact"); - expect(request.getUseSendBeacon()).toBeFalse(); - }); - - it("uses interact without sendBeacon if document will not unload and identity has not been established", () => { - const payload = { - getDocumentMayUnload() { - return false; - }, - }; - const request = createDataCollectionRequest({ payload }); - expect(request.getAction()).toBe("interact"); - expect(request.getUseSendBeacon()).toBeFalse(); - }); - - it("passes the datastreamIdOverride to the request", () => { - const payload = {}; - const datastreamIdOverride = "my-edge-config-id-override"; - const request = createDataCollectionRequest({ - payload, - datastreamIdOverride, - }); - expect(request.getDatastreamIdOverride()).toBe(datastreamIdOverride); - }); -}); diff --git a/test/unit/specs/utils/request/createDataCollectionRequestPayload.spec.js b/test/unit/specs/utils/request/createDataCollectionRequestPayload.spec.js deleted file mode 100644 index 8a47dea64..000000000 --- a/test/unit/specs/utils/request/createDataCollectionRequestPayload.spec.js +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { createDataCollectionRequestPayload } from "../../../../../src/utils/request/index.js"; -import createEvent from "../../../../../src/core/createEvent.js"; -import describeRequestPayload from "../../../helpers/describeRequestPayload.js"; - -describe("createDataCollectionRequestPayload", () => { - describeRequestPayload(createDataCollectionRequestPayload); - - it("adds an identity", () => { - const payload = createDataCollectionRequestPayload(); - payload.addIdentity("IDNS", { - id: "ABC123", - }); - payload.addIdentity("IDNS", { - id: "DEF456", - }); - expect(JSON.parse(JSON.stringify(payload))).toEqual({ - xdm: { - identityMap: { - IDNS: [ - { - id: "ABC123", - }, - { - id: "DEF456", - }, - ], - }, - }, - }); - }); - - it("adds events and serializes them properly", () => { - const payload = createDataCollectionRequestPayload(); - payload.addEvent({ xdm: { a: "b" } }); - payload.addEvent({ xdm: { c: "d" } }); - expect(JSON.parse(JSON.stringify(payload))).toEqual({ - events: [{ xdm: { a: "b" } }, { xdm: { c: "d" } }], - }); - }); - - it("returns that document may unload if any event reports that it may unload", () => { - const payload = createDataCollectionRequestPayload(); - const event1 = createEvent(); - payload.addEvent(event1); - const event2 = createEvent(); - event2.documentMayUnload(); - payload.addEvent(event2); - expect(payload.getDocumentMayUnload()).toBeTrue(); - }); - - it("returns that document will not unload if no event reports that it may unload", () => { - const payload = createDataCollectionRequestPayload(); - const event1 = createEvent(); - payload.addEvent(event1); - const event2 = createEvent(); - payload.addEvent(event2); - expect(payload.getDocumentMayUnload()).toBeFalse(); - }); - - it("returns that document will not unload if the payload contains no events", () => { - const payload = createDataCollectionRequestPayload(); - expect(payload.getDocumentMayUnload()).toBeFalse(); - }); -}); diff --git a/test/unit/specs/utils/request/createGetAssuranceValidationTokenParams.spec.js b/test/unit/specs/utils/request/createGetAssuranceValidationTokenParams.spec.js deleted file mode 100644 index af906f33c..000000000 --- a/test/unit/specs/utils/request/createGetAssuranceValidationTokenParams.spec.js +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { createGetAssuranceValidationTokenParams } from "../../../../../src/utils/request/index.js"; -import { injectStorage } from "../../../../../src/utils/index.js"; -import uuidV4Regex from "../../../constants/uuidV4Regex.js"; - -const win = { - location: { - search: "", - }, - localStorage: window.localStorage, -}; - -describe("createGetAssuranceValidationTokenParams", () => { - it("gets validation token params", () => { - let result; - let token; - let firstClientId; - let clientId; - const getAssuranceValidationTokenParams = - createGetAssuranceValidationTokenParams({ - window: win, - createNamespacedStorage: injectStorage(win), - }); - expect(getAssuranceValidationTokenParams()).toEqual(""); - - win.location.search = "?adb_validation_sessionid=abc-123"; - result = getAssuranceValidationTokenParams(); - // eslint-disable-next-line prefer-const - [token, firstClientId] = result.split("%7C"); - expect(token).toEqual("&adobeAepValidationToken=abc-123"); - expect(uuidV4Regex.test(firstClientId)).toBeTrue(); - expect( - win.localStorage.getItem("com.adobe.alloy.validation.clientId"), - ).toEqual(firstClientId); - - win.location.search = "?adb_validation_sessionid=abc-123%20fgh"; - result = getAssuranceValidationTokenParams(); - [token, clientId] = result.split("%7C"); - expect(token).toEqual("&adobeAepValidationToken=abc-123%20fgh"); - expect(clientId).toEqual(firstClientId); - - win.location.search = - "?lang=en&sort=relevancy&f:el_product=[Data%20Collection]&adb_validation_sessionid=abc-123"; - result = getAssuranceValidationTokenParams(); - [token, clientId] = result.split("%7C"); - expect(token).toEqual("&adobeAepValidationToken=abc-123"); - expect(clientId).toEqual(firstClientId); - - win.location.search = - "?lang=en&sort=relevancy&f:el_product=[Data%20Collection]"; - expect(getAssuranceValidationTokenParams()).toEqual(""); - - win.location.search = "?adb_validation_sessionid="; - expect(getAssuranceValidationTokenParams()).toEqual(""); - }); -}); diff --git a/test/unit/specs/utils/request/createHasIdentity.spec.js b/test/unit/specs/utils/request/createHasIdentity.spec.js deleted file mode 100644 index 87b54951c..000000000 --- a/test/unit/specs/utils/request/createHasIdentity.spec.js +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - createHasIdentity, - createAddIdentity, -} from "../../../../../src/utils/request/index.js"; - -describe("createHasIdentity", () => { - let content; - let hasIdentity; - let addIdentity; - - beforeEach(() => { - content = {}; - hasIdentity = createHasIdentity(content); - addIdentity = createAddIdentity(content); - }); - - it("should return false when no xdm has been set", () => { - expect(hasIdentity("myid")).toBe(false); - }); - it("should return false if no identity has been set", () => { - content.xdm = {}; - expect(hasIdentity("myid")).toBe(false); - }); - it("should return false if there are other identities", () => { - addIdentity("other", "myotherid"); - expect(hasIdentity("myid")).toBe(false); - }); - it("should return true when there already is an identity", () => { - addIdentity("myid", "myidvalue"); - expect(hasIdentity("myid")).toBe(true); - }); -}); diff --git a/test/unit/specs/utils/request/createRequest.spec.js b/test/unit/specs/utils/request/createRequest.spec.js deleted file mode 100644 index 469602d80..000000000 --- a/test/unit/specs/utils/request/createRequest.spec.js +++ /dev/null @@ -1,17 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -// eslint-disable-next-line no-unused-vars -import { createRequest } from "../../../../../src/utils/request/index.js"; - -// This module is tested thoroughly through the different types of requests -// that leverage this module. diff --git a/test/unit/specs/utils/request/createRequestParams.spec.js b/test/unit/specs/utils/request/createRequestParams.spec.js deleted file mode 100644 index 156a07620..000000000 --- a/test/unit/specs/utils/request/createRequestParams.spec.js +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { createRequestParams } from "../../../../../src/utils/request/index.js"; - -describe("createRequestParams", () => { - let payload; - let globalConfigOverrides; - let localConfigOverrides; - beforeEach(() => { - payload = jasmine.createSpyObj("payload", ["mergeConfigOverride"]); - }); - - it("returns the payload and datastreamIdOverride", () => { - const result = createRequestParams({ - payload, - localConfigOverrides: { - datastreamId: "123", - }, - }); - expect(result).toEqual({ - payload, - datastreamIdOverride: "123", - }); - }); - - it("works fine without overrides", () => { - const result = createRequestParams({ - payload, - }); - expect(result).toEqual({ - payload, - }); - }); - - it("merges the global and local config overrides", () => { - globalConfigOverrides = { - a: "b", - c: "d", - }; - localConfigOverrides = { - a: "e", - }; - createRequestParams({ - payload, - globalConfigOverrides, - localConfigOverrides, - }); - expect(payload.mergeConfigOverride).toHaveBeenCalledWith({ - a: "b", - c: "d", - }); - expect(payload.mergeConfigOverride).toHaveBeenCalledWith({ - a: "e", - }); - }); -}); diff --git a/test/unit/specs/utils/request/createRequestPayload.spec.js b/test/unit/specs/utils/request/createRequestPayload.spec.js deleted file mode 100644 index b7b1af9e2..000000000 --- a/test/unit/specs/utils/request/createRequestPayload.spec.js +++ /dev/null @@ -1,17 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -// eslint-disable-next-line no-unused-vars -import { createRequestPayload } from "../../../../../src/utils/request/index.js"; - -// This module is tested thoroughly through the different types of request -// payloads that leverage this module. diff --git a/test/unit/specs/utils/sanitizeOrgIdForCookieName.spec.js b/test/unit/specs/utils/sanitizeOrgIdForCookieName.spec.js deleted file mode 100644 index 4a99dfb14..000000000 --- a/test/unit/specs/utils/sanitizeOrgIdForCookieName.spec.js +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import sanitizeOrgIdForCookieName from "../../../../src/utils/sanitizeOrgIdForCookieName.js"; - -describe("sanitizeOrgIdForCookieName", () => { - it("replaces @ with _", () => { - const result = sanitizeOrgIdForCookieName("ABC@CustomOrg"); - expect(result).toBe("ABC_CustomOrg"); - }); -}); diff --git a/test/unit/specs/utils/stackError.spec.js b/test/unit/specs/utils/stackError.spec.js deleted file mode 100644 index 5dbe707e4..000000000 --- a/test/unit/specs/utils/stackError.spec.js +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import stackError from "../../../../src/utils/stackError.js"; - -describe("stackError", () => { - it("stacks message onto error instance", () => { - const error = new Error("Conundrum encountered."); - const result = stackError({ error, message: "Predicament discovered." }); - expect(result).toEqual(jasmine.any(Error)); - expect(result.message).toBe( - "Predicament discovered.\nCaused by: Conundrum encountered.", - ); - }); - - it("stacks message onto non-error instance", () => { - const error = "Conundrum encountered."; - const result = stackError({ error, message: "Predicament discovered." }); - expect(result).toEqual(jasmine.any(Error)); - expect(result.message).toBe( - "Predicament discovered.\nCaused by: Conundrum encountered.", - ); - }); -}); diff --git a/test/unit/specs/utils/stringToBoolean.spec.js b/test/unit/specs/utils/stringToBoolean.spec.js deleted file mode 100644 index 515d4bbf2..000000000 --- a/test/unit/specs/utils/stringToBoolean.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { stringToBoolean } from "../../../../src/utils/index.js"; - -describe("stringToBoolean", () => { - ["true", "TRUE", "True"].forEach((str) => { - it(`parses '${str}' as true`, () => { - expect(stringToBoolean(str)).toBe(true); - }); - }); - - ["false", "0", "foo", ""].forEach((str) => { - it(`parses '${str}' as false`, () => { - expect(stringToBoolean(str)).toBe(false); - }); - }); -}); diff --git a/test/unit/specs/utils/toArray.spec.js b/test/unit/specs/utils/toArray.spec.js deleted file mode 100644 index 2cecd6d3b..000000000 --- a/test/unit/specs/utils/toArray.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import toArray from "../../../../src/utils/toArray.js"; - -describe("toArray", () => { - it("does not convert value if already an array", () => { - const value = []; - expect(toArray(value)).toBe(value); - }); - - it("converts undefined to empty array", () => { - expect(toArray()).toEqual([]); - }); - - it("converts null to empty array", () => { - expect(toArray(null)).toEqual([]); - }); - - it("converts array-like value to array", () => { - const result = toArray(document.querySelectorAll("body")); - expect(Array.isArray(result)).toBe(true); - expect(result.length).toBe(1); - expect(result[0]).toBe(document.body); - }); -}); diff --git a/test/unit/specs/utils/toError.spec.js b/test/unit/specs/utils/toError.spec.js deleted file mode 100644 index bf7393f6c..000000000 --- a/test/unit/specs/utils/toError.spec.js +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import toError from "../../../../src/utils/toError.js"; - -describe("toError", () => { - it("returns an error if value is not an error", () => { - const message = "Conundrum encountered."; - const result = toError(message); - expect(result).toEqual(jasmine.any(Error)); - expect(result.message).toBe("Conundrum encountered."); - }); - - it("returns the value unmodified if value is an error", () => { - const error = new Error("Conundrum encountered."); - const result = toError(error); - expect(result).toBe(error); - }); -}); diff --git a/test/unit/specs/utils/toISOStringLocal.spec.js b/test/unit/specs/utils/toISOStringLocal.spec.js deleted file mode 100644 index 7cdef6b6e..000000000 --- a/test/unit/specs/utils/toISOStringLocal.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { toISOStringLocal } from "../../../../src/utils/index.js"; - -describe("toISOStringLocal", () => { - it("handles a date in Utah", () => { - const date = new Date("August 9, 2019 10:59:42"); - spyOn(date, "getTimezoneOffset").and.returnValue(7 * 60); - expect(toISOStringLocal(date)).toEqual("2019-08-09T10:59:42.000-07:00"); - }); - - it("handles a date in india", () => { - const date = new Date("December 31, 2019 22:36:00"); - spyOn(date, "getTimezoneOffset").and.returnValue(-5 * 60 - 30); - expect(toISOStringLocal(date)).toEqual("2019-12-31T22:36:00.000+05:30"); - }); - - it("handles a weird offset", () => { - const date = new Date("January 01, 2020 00:00:42"); - spyOn(date, "getTimezoneOffset").and.returnValue(-176); - expect(toISOStringLocal(date)).toEqual("2020-01-01T00:00:42.000+02:56"); - }); - - it("handles a UTC timezone", () => { - const date = new Date("December 31, 2019 22:36:00"); - spyOn(date, "getTimezoneOffset").and.returnValue(0); - expect(toISOStringLocal(date)).toEqual("2019-12-31T22:36:00.000+00:00"); - }); -}); diff --git a/test/unit/specs/utils/toInteger.spec.js b/test/unit/specs/utils/toInteger.spec.js deleted file mode 100644 index fbc6b9562..000000000 --- a/test/unit/specs/utils/toInteger.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import toInteger from "../../../../src/utils/toInteger.js"; - -describe("toInteger", () => { - [ - ["0", 0], - ["1", 1], - ["true", undefined], - ["1.1", 1], - ["-4", -4], - ["123abc", undefined], - [-42, -42], - [3.14, 3], - [3.99, 4], - [null, undefined], - [undefined, undefined], - [{}, undefined], - [true, undefined], - [false, undefined], - [() => 42, undefined], - ["1234.5", 1235], - ].forEach(([input, output]) => { - it(`converts "${input}" to ${output}`, () => { - expect(toInteger(input)).toEqual(output); - }); - }); - - it("uses the passed value for the default", () => { - expect(toInteger("foo", 0)).toEqual(0); - }); -}); diff --git a/test/unit/specs/utils/updateErrorMessage.spec.js b/test/unit/specs/utils/updateErrorMessage.spec.js deleted file mode 100644 index 0ba1c35e6..000000000 --- a/test/unit/specs/utils/updateErrorMessage.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import updateErrorMessage from "../../../../src/utils/updateErrorMessage.js"; - -describe("updateErrorMessage", () => { - it("updates error message if the message property is writeable", () => { - const error = new Error("Conundrum encountered."); - const message = "Predicament discovered."; - updateErrorMessage({ error, message }); - expect(error.message).toEqual("Predicament discovered."); - }); - - it("does not update error message if the message property is read-only", () => { - let error; - try { - // This will cause a DOMException, which has a read-only message property. - const invalidSelector = "div:foo"; - document.querySelectorAll(invalidSelector); - } catch (e) { - error = e; - } - updateErrorMessage({ error, message: "Predicament discovered." }); - expect(error.message).not.toContain("Predicament discovered."); - }); -}); diff --git a/test/unit/specs/utils/uuid.spec.js b/test/unit/specs/utils/uuid.spec.js deleted file mode 100644 index 595c2773b..000000000 --- a/test/unit/specs/utils/uuid.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -// eslint-disable-next-line no-unused-vars -import uuid from "../../../../src/utils/uuid.js"; diff --git a/test/unit/specs/utils/validateConfigOverride.spec.js b/test/unit/specs/utils/validateConfigOverride.spec.js deleted file mode 100644 index 807b043fa..000000000 --- a/test/unit/specs/utils/validateConfigOverride.spec.js +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright 2022 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import validateConfigOverride from "../../../../src/utils/validateConfigOverride.js"; -import describeValidation from "../../helpers/describeValidation.js"; - -describeValidation("utils:validateConfigOverride", validateConfigOverride, [ - // empty configuration - { value: {} }, - // standard configuration - { - value: { - experience_platform: { - datasets: { - event: "werewr", - profile: "www", - }, - }, - analytics: { - reportSuites: ["sdfsfd"], - }, - identity: { - idSyncContainerId: "rrr", - }, - target: { - propertyToken: "rrr", - }, - }, - }, - // arbitrarily nested objects - { - value: { - experience_platform: { - datasets: { - event: { - morning: { - first: { - withoutAction: "222", - }, - }, - }, - }, - }, - }, - }, - // non-object top level keys are valid - { - value: { - foo: "bar", - biz: {}, - }, - }, - // value must be an object - { value: true, error: true }, - { value: false, error: true }, - { value: "", error: true }, - { value: [], error: true }, - { value: 123, error: true }, -]); diff --git a/test/unit/specs/utils/validateIdentityMap.spec.js b/test/unit/specs/utils/validateIdentityMap.spec.js deleted file mode 100644 index a85255f33..000000000 --- a/test/unit/specs/utils/validateIdentityMap.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { validateIdentityMap } from "../../../../src/utils/index.js"; -import describeValidation from "../../helpers/describeValidation.js"; - -describeValidation("utils:validateIdentityMap", validateIdentityMap, [ - { value: { a: [{ authenticatedState: "unknown" }] }, error: true }, - { value: { a: [{ authenticatedState: "authenticated" }] } }, - { value: { a: [{ id: 123 }] }, error: true }, - { value: { a: [{ id: "123" }] } }, - { value: { a: [{ namespace: { unknown: "field" } }] }, error: true }, - { value: { a: [{ namespace: { code: "123" } }] } }, - { value: { a: [{ primary: 1 }] }, error: true }, - { value: { a: [{ primary: true }] } }, - { value: { a: [{ xid: 123 }] }, error: true }, - { value: { a: [{ xid: "123" }] } }, - { value: { a: [{ unknown: "field" }] }, error: true }, - { value: null }, - { value: undefined }, - { value: [], error: true }, - { value: { a: [] } }, - { value: { a: null }, error: true }, - { value: { a: undefined }, error: true }, - { value: { a: "string" }, error: true }, - { value: {} }, -]); diff --git a/test/unit/specs/utils/validation/anythingValidator.spec.js b/test/unit/specs/utils/validation/anythingValidator.spec.js deleted file mode 100644 index 9a86ce9f1..000000000 --- a/test/unit/specs/utils/validation/anythingValidator.spec.js +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { anything } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::anything", () => { - describeValidation("optional anything", anything(), [ - { value: {} }, - { value: { a: 1 } }, - { value: [] }, - { value: ["hello"] }, - { value: 1 }, - { value: true }, - { value: undefined }, - { value: null }, - { value: () => undefined }, - ]); - - describeValidation("required anything", anything().required(), [ - { value: {} }, - { value: { a: 1 } }, - { value: [] }, - { value: ["hello"] }, - { value: 1 }, - { value: true }, - { value: undefined, error: true }, - { value: null, error: true }, - ]); - - describeValidation("default anything", anything().default("foo"), [ - { value: {} }, - { value: { a: 1 } }, - { value: [] }, - { value: ["hello"] }, - { value: 1 }, - { value: true }, - { value: undefined, expected: "foo" }, - { value: null, expected: "foo" }, - ]); -}); diff --git a/test/unit/specs/utils/validation/booleanValidator.spec.js b/test/unit/specs/utils/validation/booleanValidator.spec.js deleted file mode 100644 index 382cbe4c3..000000000 --- a/test/unit/specs/utils/validation/booleanValidator.spec.js +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { boolean } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::boolean", () => { - describeValidation("optional boolean", boolean(), [ - { value: "", error: true }, - { value: "true", error: true }, - { value: [1], error: true }, - { value: {}, error: true }, - { value: 0, error: true }, - { value: 42, error: true }, - { value: true }, - { value: false }, - { value: null }, - { value: undefined }, - ]); - - describeValidation("required boolean", boolean().required(), [ - { value: true }, - { value: false }, - { value: null, error: true }, - { value: undefined, error: true }, - ]); - - describeValidation("default true boolean", boolean().default(true), [ - { value: null, expected: true }, - { value: undefined, expected: true }, - { value: true }, - { value: false }, - ]); -}); diff --git a/test/unit/specs/utils/validation/callbackValidator.spec.js b/test/unit/specs/utils/validation/callbackValidator.spec.js deleted file mode 100644 index 9f4845fcc..000000000 --- a/test/unit/specs/utils/validation/callbackValidator.spec.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { callback } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::callback", () => { - describeValidation("optional callback", callback(), [ - { value: "", error: true }, - { value: "true", error: true }, - { value: [1], error: true }, - { value: {}, error: true }, - { value: 0, error: true }, - { value: () => undefined }, - { value: function func() {} }, - ]); - - describeValidation("required callback", callback().required(), [ - { value: () => undefined }, - { value: null, error: true }, - { value: undefined, error: true }, - ]); - - const func1 = () => {}; - const func2 = () => {}; - describeValidation("callback with default value", callback().default(func1), [ - { value: null, expected: func1 }, - { value: undefined, expected: func1 }, - { value: func2, expected: func2 }, - ]); -}); diff --git a/test/unit/specs/utils/validation/createAnyOfValidator.spec.js b/test/unit/specs/utils/validation/createAnyOfValidator.spec.js deleted file mode 100644 index a416a3083..000000000 --- a/test/unit/specs/utils/validation/createAnyOfValidator.spec.js +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - anyOf, - objectOf, - boolean, - arrayOf, - string, - literal, -} from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation:anyOf", () => { - describeValidation( - "require one or the other", - anyOf( - [ - objectOf({ - renderDecisions: literal(true).required(), - decisionScopes: arrayOf(string()), - }).required(), - objectOf({ - renderDecisions: boolean(), - decisionScopes: arrayOf(string()).nonEmpty().required(), - }).required(), - ], - "either renderDecisions set to true or decisionScopes set to a nonEmpty array", - ), - [ - { value: undefined, error: true }, - { value: null, error: true }, - { value: {}, error: true }, - { value: { renderDecisions: true }, error: false }, - { value: { renderDecisions: false }, error: true }, - { value: { renderDecisions: "foo" }, error: true }, - { value: { decisionScopes: [] }, error: true }, - { value: { decisionScopes: ["a"] }, error: false }, - { value: { decisionScopes: "bar" }, error: true }, - { - value: { renderDecisions: true, decisionScopes: ["a", "b"] }, - error: false, - }, - { value: { renderDecisions: true, decisionScopes: "foo" }, error: true }, - { value: { renderDecisions: "foo", decisionScopes: ["a"] }, error: true }, - ], - ); -}); diff --git a/test/unit/specs/utils/validation/createArrayOfValidator.spec.js b/test/unit/specs/utils/validation/createArrayOfValidator.spec.js deleted file mode 100644 index 031dbccb9..000000000 --- a/test/unit/specs/utils/validation/createArrayOfValidator.spec.js +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { arrayOf, string } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::arrayOf", () => { - describeValidation( - "optional array with required values", - arrayOf(string().required()), - [ - { value: ["foo", undefined], error: true }, - { value: [true, "bar"], error: true }, - { value: "non-array", error: true }, - { value: ["foo"] }, - { value: ["foo", "bar"] }, - { value: [] }, - { value: null }, - { value: undefined }, - ], - ); - - describeValidation( - "optional array with optional values", - arrayOf(string().default("hello")), - [ - { - value: ["a", null, undefined, "b"], - expected: ["a", "hello", "hello", "b"], - }, - ], - ); - - describeValidation( - "required array with optional values", - arrayOf(string()).required(), - [ - { value: [null] }, - { value: null, error: true }, - { value: undefined, error: true }, - ], - ); -}); diff --git a/test/unit/specs/utils/validation/createDefaultValidator.spec.js b/test/unit/specs/utils/validation/createDefaultValidator.spec.js deleted file mode 100644 index 9f345f192..000000000 --- a/test/unit/specs/utils/validation/createDefaultValidator.spec.js +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { string } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::default", () => { - describeValidation("default string", string().default("my default"), [ - { value: null, expected: "my default" }, - { value: undefined, expected: "my default" }, - { value: "" }, - { value: "hello" }, - ]); -}); diff --git a/test/unit/specs/utils/validation/createDeprecatedValidator.spec.js b/test/unit/specs/utils/validation/createDeprecatedValidator.spec.js deleted file mode 100644 index cc3484964..000000000 --- a/test/unit/specs/utils/validation/createDeprecatedValidator.spec.js +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2024 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - objectOf, - callback, - string, - boolean, -} from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::deprecated", () => { - describeValidation( - "works for a string field", - objectOf({ - old: string().deprecated(), - new: string(), - }), - [ - { value: { old: "a" }, expected: { old: "a" }, warning: true }, - { value: {}, expected: {}, warning: false }, - { value: { new: "b" }, expected: { new: "b" }, warning: false }, - ], - ); - - describeValidation( - "works for a boolean field", - objectOf({ - old: boolean().deprecated(), - new: boolean(), - }), - [ - { value: { old: true }, expected: { old: true }, warning: true }, - { value: {}, expected: {}, warning: false }, - { value: { new: false }, expected: { new: false }, warning: false }, - ], - ); - - const noop = () => undefined; - describeValidation( - "works for a callback field", - objectOf({ - old: callback().deprecated(), - new: callback(), - }), - [ - { - value: { old: noop, new: noop }, - expected: { old: noop, new: noop }, - warning: true, - }, - { value: {}, expected: {}, warning: false }, - { value: { new: noop }, expected: { new: noop }, warning: false }, - ], - ); -}); diff --git a/test/unit/specs/utils/validation/createLiteralValidator.spec.js b/test/unit/specs/utils/validation/createLiteralValidator.spec.js deleted file mode 100644 index 82d712d61..000000000 --- a/test/unit/specs/utils/validation/createLiteralValidator.spec.js +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { literal } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation:literal", () => { - describeValidation("literal optional string", literal("hello"), [ - { value: undefined, error: false }, - { value: null, error: false }, - { value: "hello", error: false }, - { value: {}, error: true }, - { value: "", error: true }, - { value: "goodbye", error: true }, - ]); - describeValidation("literal required integer", literal(42).required(), [ - { value: 42, error: false }, - { value: 41, error: true }, - { value: null, error: true }, - { value: undefined, error: true }, - ]); -}); diff --git a/test/unit/specs/utils/validation/createMapOfValuesValidator.spec.js b/test/unit/specs/utils/validation/createMapOfValuesValidator.spec.js deleted file mode 100644 index ea73e44ad..000000000 --- a/test/unit/specs/utils/validation/createMapOfValuesValidator.spec.js +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - mapOfValues, - arrayOf, - anything, - string, -} from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::mapOfValues", () => { - describeValidation( - "map of required strings", - mapOfValues(string().required()).required(), - [ - { value: {} }, - { value: { a: "1" } }, - { value: { a: "1", b: "2", c: "3" } }, - { value: undefined, error: true }, - { value: null, error: true }, - { value: { a: 123 }, error: true }, - { value: 123, error: true }, - { value: { a: undefined }, error: true }, - ], - ); - - describeValidation("map of arrays", mapOfValues(arrayOf(anything())), [ - { value: { a: [], b: [true, 1, 0.1, "string", undefined, null] } }, - { value: { a: "string" }, error: true }, - { value: { a: undefined }, expected: {} }, - { value: { a: null } }, - { value: undefined }, - { value: null }, - ]); -}); diff --git a/test/unit/specs/utils/validation/createMaximumValidator.spec.js b/test/unit/specs/utils/validation/createMaximumValidator.spec.js deleted file mode 100644 index 93076d36d..000000000 --- a/test/unit/specs/utils/validation/createMaximumValidator.spec.js +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { number } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::maximum", () => { - describeValidation("optional maximum", number().integer().maximum(4), [ - { value: 3 }, - { value: 5, error: true }, - { value: null }, - { value: undefined }, - ]); - - describeValidation( - "required maximum", - number().integer().maximum(2).required(), - [ - { value: null, error: true }, - { value: undefined, error: true }, - { value: 3, error: true }, - { value: 1 }, - ], - ); - - describeValidation( - "default maximum", - number().integer().maximum(10).default(8), - [ - { value: null, expected: 8 }, - { value: undefined, expected: 8 }, - { value: 11, error: true }, - ], - ); -}); diff --git a/test/unit/specs/utils/validation/createMinimumValidator.spec.js b/test/unit/specs/utils/validation/createMinimumValidator.spec.js deleted file mode 100644 index 46fcaa37c..000000000 --- a/test/unit/specs/utils/validation/createMinimumValidator.spec.js +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { number } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::minimum", () => { - describeValidation("optional minimum", number().integer().minimum(4), [ - { value: 3, error: true }, - { value: 4 }, - { value: 5 }, - { value: null }, - { value: undefined }, - ]); - - describeValidation( - "required minimum", - number().integer().minimum(1).required(), - [ - { value: null, error: true }, - { value: undefined, error: true }, - { value: 0, error: true }, - { value: 1 }, - ], - ); - - describeValidation( - "default minimum", - number().integer().minimum(10).default(42), - [ - { value: null, expected: 42 }, - { value: undefined, expected: 42 }, - { value: 0, error: true }, - ], - ); -}); diff --git a/test/unit/specs/utils/validation/createNoUnknownFieldsValidator.spec.js b/test/unit/specs/utils/validation/createNoUnknownFieldsValidator.spec.js deleted file mode 100644 index c72de8c1a..000000000 --- a/test/unit/specs/utils/validation/createNoUnknownFieldsValidator.spec.js +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { objectOf, string } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::noUnknownFields", () => { - describeValidation("optional", objectOf({ a: string() }).noUnknownFields(), [ - { value: { b: "world" }, error: true }, - { value: { a: "hello", b: "world" }, error: true }, - { value: { a: "hello" }, error: false }, - { value: {}, error: false }, - ]); - - describeValidation( - "required", - objectOf({ a: string().required() }).noUnknownFields().required(), - [ - { value: null, error: true }, - { value: undefined, error: true }, - { value: {}, error: true }, - { value: { a: "Hello" }, error: false }, - ], - ); - - describeValidation( - "default", - objectOf({ a: string().default("hello") }) - .noUnknownFields() - .default({ a: "world" }), - [ - { value: null, expected: { a: "world" } }, - { value: undefined, expected: { a: "world" } }, - { value: {}, expected: { a: "hello" } }, - { value: { b: "goodbye" }, error: true }, - ], - ); -}); diff --git a/test/unit/specs/utils/validation/createNonEmptyValidator.spec.js b/test/unit/specs/utils/validation/createNonEmptyValidator.spec.js deleted file mode 100644 index f1347cb19..000000000 --- a/test/unit/specs/utils/validation/createNonEmptyValidator.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { string } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::nonEmpty", () => { - describeValidation("optional nonEmpty", string().nonEmpty(), [ - { value: "key" }, - { value: "", error: true }, - { value: null }, - { value: undefined }, - ]); - describeValidation("required nonEmpty", string().nonEmpty().required(), [ - { value: "abc" }, - { value: null, error: true }, - { value: undefined, error: true }, - ]); - describeValidation( - "default nonEmpty", - string().nonEmpty().default("mydefault"), - [ - { value: null, expected: "mydefault" }, - { value: undefined, expected: "mydefault" }, - { value: "abc" }, - ], - ); -}); diff --git a/test/unit/specs/utils/validation/createObjectOfValidator.spec.js b/test/unit/specs/utils/validation/createObjectOfValidator.spec.js deleted file mode 100644 index 93a751abb..000000000 --- a/test/unit/specs/utils/validation/createObjectOfValidator.spec.js +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { objectOf, string } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::objectOf", () => { - describeValidation( - "optional object with various values", - objectOf({ - a: string().required(), - b: string().default("b default"), - c: string(), - }), - [ - { value: {}, error: true }, - { value: { a: "1" }, expected: { a: "1", b: "b default" } }, - { value: { a: "1", b: "2", c: "3" } }, - { value: undefined }, - { value: null }, - { value: { a: 123 }, error: true }, - { value: 123, error: true }, - ], - ); - - describeValidation( - "nested object", - objectOf({ - a: objectOf({ - aa: string().required(), - }).required(), - }), - [ - { value: {}, error: true }, - { value: { a: {} }, error: true }, - { value: { a: { aa: "11" } } }, - ], - ); - - describeValidation( - "concat", - objectOf({ - a: string().required(), - }) - .concat( - objectOf({ - b: string().default("b default"), - }), - ) - .concat( - objectOf({ - c: string(), - }), - ), - [ - { value: {}, error: true }, - { value: { a: "1" }, expected: { a: "1", b: "b default" } }, - { value: { a: "1", b: "2", c: "3" } }, - { value: undefined }, - { value: null }, - { value: { a: 123 }, error: true }, - { value: 123, error: true }, - ], - ); -}); diff --git a/test/unit/specs/utils/validation/createRenamedValidator.spec.js b/test/unit/specs/utils/validation/createRenamedValidator.spec.js deleted file mode 100644 index 2a5918573..000000000 --- a/test/unit/specs/utils/validation/createRenamedValidator.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { objectOf, string } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::renamed", () => { - const testCases = [ - { value: { old: "a", new: "a" }, expected: { new: "a" }, warning: true }, - { value: { old: "a" }, expected: { new: "a" }, warning: true }, - { value: { new: "a" } }, - { value: { old: "a", new: "b" }, error: true }, - { value: "foo", error: true }, - { value: 1, error: true }, - { value: undefined }, - ]; - - describeValidation( - "works for a single deprecated field", - objectOf({ - new: string().required(), - }).renamed("old", string(), "new"), - testCases, - ); - - describeValidation( - "works for multiple deprecated fields", - objectOf({ - new1: string().required(), - new2: string().required(), - }) - .renamed("old1", string(), "new1") - .renamed("old2", string(), "new2"), - [ - { - value: { old1: "a", old2: "b" }, - expected: { new1: "a", new2: "b" }, - warning: true, - }, - ], - ); -}); diff --git a/test/unit/specs/utils/validation/createUniqueItemsValidator.spec.js b/test/unit/specs/utils/validation/createUniqueItemsValidator.spec.js deleted file mode 100644 index b7c240d71..000000000 --- a/test/unit/specs/utils/validation/createUniqueItemsValidator.spec.js +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { - string, - arrayOf, - number, -} from "../../../../../src/utils/validation/index.js"; - -describe("validation::createUniqueItems", () => { - it(`validates an empty array`, () => { - const validator = arrayOf(string()).uniqueItems(); - expect(validator([])).toEqual([]); - }); - - it(`validates an array of one item`, () => { - const validator = arrayOf(string()).uniqueItems(); - expect(validator(["a"])).toEqual(["a"]); - }); - - it(`throws an error on an array with duplicate (string) items`, () => { - const validator = arrayOf(string()).uniqueItems(); - expect(() => validator(["a", "b", "a", "e"])).toThrowError(); - }); - - it(`throws an error on an array with duplicate integers`, () => { - const validator = arrayOf(number()).uniqueItems(); - expect(() => validator([1, 2, 3, 4, 4, 5])).toThrowError(); - }); - - it(`validates an array of enums`, () => { - const validator = arrayOf(number()).uniqueItems(); - expect(validator([])).toEqual([]); - }); - - it(`validates an array of null or undefined`, () => { - const validator = arrayOf(string()).uniqueItems(); - expect(validator([null, undefined])).toEqual([null, undefined]); - }); - - it(`complains about required when null or undefined`, () => { - const validator = arrayOf(string().required()).uniqueItems(); - expect(() => validator([null, undefined])).toThrowError(); - }); -}); diff --git a/test/unit/specs/utils/validation/createUniqueValidator.spec.js b/test/unit/specs/utils/validation/createUniqueValidator.spec.js deleted file mode 100644 index 70d78e95a..000000000 --- a/test/unit/specs/utils/validation/createUniqueValidator.spec.js +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { string } from "../../../../../src/utils/validation/index.js"; - -describe("validation::createUnique", () => { - [["a"], ["a", "b", "c"]].forEach((values) => { - it(`should accept ${JSON.stringify(values)}`, () => { - const validator = string().unique(); - values.forEach((value) => { - expect(validator(value, "mykey")).toEqual(value); - }); - }); - }); - - [ - ["a", "a"], - ["a", "b", "a"], - ["a", "b", "b"], - ].forEach((values) => { - it(`should reject ${JSON.stringify(values)}`, () => { - const validator = string().unique(); - values.forEach((value, i) => { - if (i + 1 === values.length) { - expect(() => validator(value, "mykey")).toThrowError(); - } else { - expect(validator(value, "mykey")).toEqual(value); - } - }); - }); - }); - - [null, undefined].forEach((value) => { - it(`complains about required when ${JSON.stringify(value)}`, () => { - const validator = string().unique().required(); - expect(() => validator(value, "key")).toThrowError(); - }); - }); -}); diff --git a/test/unit/specs/utils/validation/domainValidator.spec.js b/test/unit/specs/utils/validation/domainValidator.spec.js deleted file mode 100644 index 3c5655f02..000000000 --- a/test/unit/specs/utils/validation/domainValidator.spec.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { string } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::domain", () => { - describeValidation("domain", string().domain(), [ - { value: "stats.adobe.com" }, - { value: "stats-edge.adobe.com" }, - { value: "https://stats.adobe.com", error: true }, - { value: "stats.adobe.com\n", error: true }, - { value: "stats.adobe.com\nbad", error: true }, - ]); -}); diff --git a/test/unit/specs/utils/validation/enumOfValidator.spec.js b/test/unit/specs/utils/validation/enumOfValidator.spec.js deleted file mode 100644 index 5d9d96dc7..000000000 --- a/test/unit/specs/utils/validation/enumOfValidator.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { enumOf } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation:enumOf", () => { - describeValidation("optional enum", enumOf("in", 1234, 0.1, false), [ - { value: undefined, error: false }, - { value: 1234, error: false }, - { value: "in", error: false }, - { value: null, error: false }, - { value: 0.1, error: false }, - { value: false, error: false }, - { value: "out", error: true }, - { value: "", error: true }, - { value: {}, error: true }, - { value: [], error: true }, - ]); - describeValidation("required enum", enumOf("in", "pending").required(), [ - { value: "in", error: false }, - { value: "pending", error: false }, - { value: null, error: true }, - { value: undefined, error: true }, - { value: 0.1, error: true }, - { value: false, error: true }, - { value: "out", error: true }, - { value: "", error: true }, - { value: {}, error: true }, - { value: [], error: true }, - ]); -}); diff --git a/test/unit/specs/utils/validation/integerValidator.spec.js b/test/unit/specs/utils/validation/integerValidator.spec.js deleted file mode 100644 index 043235e24..000000000 --- a/test/unit/specs/utils/validation/integerValidator.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { number } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::integer()", () => { - describeValidation("optional integer", number().integer(), [ - { value: 42.01, error: true }, - { value: -1.1, error: true }, - { value: NaN, error: true }, - { value: 0 }, - { value: 42 }, - { value: -1 }, - ]); - - describeValidation("required integer", number().integer().required(), [ - { value: null, error: true }, - { value: undefined, error: true }, - { value: 10 }, - ]); - - describeValidation("default integer", number().integer().default(12345), [ - { value: null, expected: 12345 }, - { value: undefined, expected: 12345 }, - { value: 10, expected: 10 }, - ]); -}); diff --git a/test/unit/specs/utils/validation/matchesRegexpValidator.spec.js b/test/unit/specs/utils/validation/matchesRegexpValidator.spec.js deleted file mode 100644 index eb50c9dff..000000000 --- a/test/unit/specs/utils/validation/matchesRegexpValidator.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { string } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -const regexp = /^[A-z]+$/; - -describe("validation::matchesRegexp", () => { - describeValidation("optional matchesRegexp", string().matches(regexp), [ - { value: "abc" }, - { value: "ABCD" }, - { value: "*", error: true }, - { value: "123", error: true }, - { value: null }, - { value: undefined }, - ]); - - describeValidation("required regexp", string().regexp(regexp).required(), [ - { value: null, error: true }, - { value: undefined, error: true }, - { value: "" }, - ]); - - describeValidation("default regexp", string().regexp(regexp).default("abc"), [ - { value: null, expected: "abc" }, - { value: undefined, expected: "abc" }, - { value: "a" }, - ]); -}); diff --git a/test/unit/specs/utils/validation/numberValidator.spec.js b/test/unit/specs/utils/validation/numberValidator.spec.js deleted file mode 100644 index 58eadd2b2..000000000 --- a/test/unit/specs/utils/validation/numberValidator.spec.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { number } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::number", () => { - describeValidation("optional number", number(), [ - { value: true, error: true }, - { value: "", error: true }, - { value: "42", error: true }, - { value: [1], error: true }, - { value: {}, error: true }, - { value: NaN, error: true }, - { value: 0 }, - { value: 0.01 }, - { value: Infinity }, - ]); - - describeValidation("required number", number().required(), [ - { value: null, error: true }, - { value: undefined, error: true }, - { value: 123 }, - ]); - - describeValidation("default number", number().default(-1), [ - { value: null, expected: -1 }, - { value: undefined, expected: -1 }, - { value: 123 }, - ]); -}); diff --git a/test/unit/specs/utils/validation/regexpValidator.spec.js b/test/unit/specs/utils/validation/regexpValidator.spec.js deleted file mode 100644 index f68041807..000000000 --- a/test/unit/specs/utils/validation/regexpValidator.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { string } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::regexp", () => { - describeValidation("optional regexp", string().regexp(), [ - { value: "steel|bronze" }, - { value: "/a/" }, - { value: "/^[a-z0-9+]:///i" }, - { value: "[", error: true }, - { value: "*", error: true }, - { value: null }, - { value: undefined }, - ]); - - describeValidation("required regexp", string().regexp().required(), [ - { value: null, error: true }, - { value: undefined, error: true }, - { value: "" }, - ]); - - describeValidation("default regexp", string().regexp().default("/default/"), [ - { value: null, expected: "/default/" }, - { value: undefined, expected: "/default/" }, - { value: "a" }, - ]); -}); diff --git a/test/unit/specs/utils/validation/requiredValidator.spec.js b/test/unit/specs/utils/validation/requiredValidator.spec.js deleted file mode 100644 index 63491c51e..000000000 --- a/test/unit/specs/utils/validation/requiredValidator.spec.js +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2020 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { string } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::required", () => { - describeValidation("required string", string().required(), [ - { value: null, error: true }, - { value: undefined, error: true }, - { value: "" }, - { value: "hello" }, - ]); -}); diff --git a/test/unit/specs/utils/validation/stringValidator.spec.js b/test/unit/specs/utils/validation/stringValidator.spec.js deleted file mode 100644 index 9e123a776..000000000 --- a/test/unit/specs/utils/validation/stringValidator.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { string } from "../../../../../src/utils/validation/index.js"; -import describeValidation from "../../../helpers/describeValidation.js"; - -describe("validation::string", () => { - describeValidation("optional string", string(), [ - { value: false, error: true }, - { value: 0, error: true }, - { value: [], error: true }, - { value: () => {}, error: true }, - { value: null }, - { value: undefined }, - ]); - - describeValidation("required string", string().required(), [ - { value: null, error: true }, - { value: undefined, error: true }, - { value: "" }, - { value: "hello" }, - ]); - - describeValidation("default string", string().default("default"), [ - { value: null, expected: "default" }, - { value: undefined, expected: "default" }, - { value: "hello" }, - ]); -}); diff --git a/test/unit/specs/utils/validation/utils.spec.js b/test/unit/specs/utils/validation/utils.spec.js deleted file mode 100644 index 02122c6b1..000000000 --- a/test/unit/specs/utils/validation/utils.spec.js +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { - chain, - nullSafeChain, - assertValid, -} from "../../../../../src/utils/validation/utils.js"; - -describe("validation::utils", () => { - describe("chain", () => { - it("calls the validators with the correct params", () => { - const validator1 = jasmine.createSpy(); - const validator2 = jasmine.createSpy(); - const validator3 = jasmine.createSpy(); - validator1.and.returnValue("validator1return"); - validator2.and.returnValue("validator2return"); - validator3.and.returnValue("validator3return"); - const subject = chain(chain(validator1, validator2), validator3); - expect(subject("myCurrentValue", "myKey")).toEqual("validator3return"); - expect(validator1).toHaveBeenCalledTimes(1); - expect(validator1).toHaveBeenCalledWith("myCurrentValue", "myKey"); - expect(validator2).toHaveBeenCalledTimes(1); - expect(validator2).toHaveBeenCalledWith("validator1return", "myKey"); - expect(validator3).toHaveBeenCalledTimes(1); - expect(validator3).toHaveBeenCalledWith("validator2return", "myKey"); - }); - - it("short circuits evaluation", () => { - const validator1 = jasmine.createSpy(); - const validator2 = jasmine.createSpy(); - const validator3 = jasmine.createSpy(); - validator1.and.returnValue("validator1return"); - validator2.and.throwError("My Error!"); - validator3.and.returnValue("validator3return"); - const subject = chain(chain(validator1, validator2), validator3); - expect(() => subject("myCurrentValue", "myKey")).toThrow( - Error("My Error!"), - ); - expect(validator3).not.toHaveBeenCalled(); - }); - }); - - describe("nullSafeChain", () => { - it("doesn't call the underlying validators when null is passed in", () => { - const validator1 = jasmine.createSpy(); - const validator2 = jasmine.createSpy(); - const validator3 = jasmine.createSpy(); - validator1.and.returnValue(null); - const subject = nullSafeChain( - nullSafeChain(validator1, validator2), - validator3, - ); - expect(subject(null, "myKey")).toEqual(null); - expect(validator1).toHaveBeenCalledTimes(1); - expect(validator1).toHaveBeenCalledWith(null, "myKey"); - expect(validator2).toHaveBeenCalledTimes(0); - expect(validator3).toHaveBeenCalledTimes(0); - }); - }); - - describe("assertValid", () => { - it("throws an error when it is invalid", () => { - expect(() => - assertValid(false, "myValue", "myPath", "myMessage"), - ).toThrowMatching((e) => { - return /'myPath': Expected myMessage, but got "myValue"\./.test( - e.message, - ); - }); - }); - - it("does not throw an error when it is valid", () => { - expect( - assertValid(true, "myValue", "myPath", "myMessage"), - ).toBeUndefined(); - }); - }); -}); From e145bb353ef9c502ca2459fc73a37b7bc6eafc93 Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Wed, 8 Jan 2025 13:46:19 -0700 Subject: [PATCH 09/15] Move vitest unit tests to the old karma unit tests. --- .husky/pre-commit | 1 - eslint.config.js | 2 +- package.json | 4 +- {vtest => test}/unit/constants/uuidV4Regex.js | 0 .../unit/helpers/assertFunctionCallOrder.js | 0 .../unit/helpers/cleanUpDomChanges.js | 0 .../createDecoratePropositionForTest.js | 0 .../unit/helpers/createMockProposition.js | 0 .../unit/helpers/createResponse.js | 0 .../unit/helpers/describeRequest.js | 0 .../unit/helpers/describeRequestPayload.js | 0 .../unit/helpers/describeValidation.js | 0 .../unit/helpers/flushPromiseChains.js | 0 {vtest => test}/unit/helpers/pause.js | 0 .../unit/helpers/removeAllCookies.js | 0 .../unit/helpers/testConfigValidators.js | 0 .../attachClickActivityCollector.spec.js | 0 .../configValidators.spec.js | 0 .../createClickActivityStorage.spec.js | 0 .../createClickedElementProperties.spec.js | 0 .../createGetClickedElementProperties.spec.js | 0 ...eateInjectClickedElementProperties.spec.js | 0 ...lAndInjectClickedElementProperties.spec.js | 0 .../createStorePageViewProperties.spec.js | 0 .../ActivityCollector/getLinkName.spec.js | 0 .../ActivityCollector/getLinkRegion.spec.js | 0 .../utils/activityMapExtensionEnabled.spec.js | 0 .../utils/createTransientStorage.spec.js | 0 .../utils/determineLinkType.spec.js | 0 .../utils/dom/elementHasClickHandler.spec.js | 0 .../utils/dom/extractDomain.spec.js | 0 .../utils/dom/findClickableElement.spec.js | 0 .../getAbsoluteUrlFromAnchorElement.spec.js | 0 .../utils/dom/isButtonSubmitElement.spec.js | 0 .../utils/dom/isDownloadLink.spec.js | 0 .../utils/dom/isExitLink.spec.js | 0 .../utils/dom/isInputSubmitElement.spec.js | 0 .../dom/isSupportedAnchorElement.spec.js | 0 .../utils/dom/isSupportedTextNode.spec.js | 0 .../utils/hasPageName.spec.js | 0 .../utils/isDifferentDomains.spec.js | 0 .../utils/trimQueryFromUrl.spec.js | 0 .../utils/truncateWhiteSpace.spec.js | 0 .../utils/urlStartsWithScheme.spec.js | 0 .../injectProcessDestinations.spec.js | 0 .../Audiences/injectProcessResponse.spec.js | 0 .../Consent/computeConsentHash.spec.js | 0 .../Consent/configValidators.spec.js | 0 .../Consent/createComponent.spec.js | 0 .../Consent/createConsentHashStore.spec.js | 0 .../Consent/createConsentRequest.spec.js | 0 .../createConsentRequestPayload.spec.js | 0 .../Consent/createStoredConsent.spec.js | 0 .../injectSendSetConsentRequest.spec.js | 0 .../Consent/parseConsentCookie.spec.js | 0 .../Consent/validateSetConsentOptions.spec.js | 0 .../Context/createComponent.spec.js | 0 .../Context/implementationDetails.spec.js | 0 .../components/Context/injectDevice.spec.js | 0 .../Context/injectEnvironment.spec.js | 0 .../injectHighEntropyUserAgentHints.spec.js | 0 .../Context/injectPlaceContext.spec.js | 0 .../Context/injectTimestamp.spec.js | 0 .../components/Context/injectWeb.spec.js | 0 .../components/DataCollector/index.spec.js | 0 .../validateApplyResponse.spec.js | 0 .../validateUserEventOptions.spec.js | 0 .../EventMerge/createComponent.spec.js | 0 .../EventMerge/createEventMergeId.spec.js | 0 .../Identity/addEcidToPayload.spec.js | 0 ...ppendIdentityToUrlOptionsValidator.spec.js | 0 .../injectAppendIdentityToUrl.spec.js | 0 .../Identity/configValidators.spec.js | 0 .../Identity/createComponent.spec.js | 0 .../Identity/createLegacyIdentity.spec.js | 0 .../getIdentity/createGetIdentity.spec.js | 0 .../createGetIdentityOptionsValidator.spec.js | 0 .../getIdentity/createIdentityRequest.spec.js | 0 .../createIdentityRequestPayload.spec.js | 0 .../getNamespacesFromResponse.spec.js | 0 .../injectAddEcidQueryToPayload.spec.js | 0 .../injectAddLegacyEcidToPayload.spec.js | 0 ...ectAddQueryStringIdentityToPayload.spec.js | 0 .../injectAwaitIdentityCookie.spec.js | 0 .../injectEnsureSingleIdentity.spec.js | 0 .../injectHandleResponseForIdSyncs.spec.js | 0 .../Identity/injectProcessIdSyncs.spec.js | 0 ...SetDomainForInitialIdentityPayload.spec.js | 0 .../visitorService/awaitVisitorOptIn.spec.js | 0 .../visitorService/getVisitor.spec.js | 0 .../injectGetEcidFromVisitor.spec.js | 0 .../components/LibraryInfo/index.spec.js | 0 .../createGetInstance.spec.js | 0 ...reateMediaAnalyticsBridgeComponent.spec.js | 0 .../createMediaHelper.spec.js | 0 .../createActionsProvider.spec.js | 0 .../createApplyPropositions.spec.js | 0 .../createClickStorage.spec.js | 0 .../Personalization/createComponent.spec.js | 0 .../createFetchDataHandler.spec.js | 0 .../createGetPageLocation.spec.js | 0 .../createHandleConsentFlicker.spec.js | 0 .../createInteractionStorage.spec.js | 0 .../createNotificationHandler.spec.js | 0 .../createOnClickHandler.spec.js | 0 .../createOnDecisionHandler.spec.js | 0 .../createPersonalizationDetails.spec.js | 0 .../createPreprocessors.spec.js | 0 .../createSetTargetMigration.spec.js | 0 .../createViewCacheManager.spec.js | 0 .../createViewChangeHandler.spec.js | 0 .../addNonceToInlineStyleElements.spec.js | 0 .../dom-actions/appendHtml.spec.js | 0 .../dom-actions/clicks/collectClicks.spec.js | 0 .../clicks/collectInteractions.spec.js | 0 .../dom-actions/collectInteractions.spec.js | 0 .../dom-actions/createPreprocess.spec.js | 0 .../dom-actions/createRedirect.spec.js | 0 .../dom-actions/customCode.spec.js | 0 .../dom-actions/dom/createFragment.spec.js | 0 .../dom-actions/dom/getAttribute.spec.js | 0 .../dom-actions/dom/getChildNodes.spec.js | 0 .../dom-actions/dom/getChildren.spec.js | 0 .../dom-actions/dom/getElementById.spec.js | 0 .../dom-actions/dom/getFirstChild.spec.js | 0 .../dom-actions/dom/getNextSibling.spec.js | 0 .../dom-actions/dom/getNonce.spec.js | 0 .../dom-actions/dom/getParent.spec.js | 0 .../dom-actions/dom/helperForEq.spec.js | 0 .../dom-actions/dom/insertAfter.spec.js | 0 .../dom-actions/dom/isDomElement.spec.js | 0 .../dom/matchesSelectorWithEq.spec.js | 0 .../dom-actions/dom/removeAttribute.spec.js | 0 .../dom-actions/dom/selectNodesWithEq.spec.js | 0 .../dom-actions/dom/setAttribute.spec.js | 0 .../dom-actions/dom/setStyle.spec.js | 0 .../dom-actions/dom/util.spec.js | 0 .../dom-actions/images.spec.js | 0 .../dom-actions/initDomActionsModules.spec.js | 0 .../dom-actions/insertHtmlAfter.spec.js | 0 .../dom-actions/insertHtmlBefore.spec.js | 0 .../Personalization/dom-actions/move.spec.js | 0 .../dom-actions/prependHtml.spec.js | 0 .../dom-actions/rearrangeChildren.spec.js | 0 .../dom-actions/remapCustomCodeOffers.spec.js | 0 .../dom-actions/remove.spec.js | 0 .../dom-actions/replaceHtml.spec.js | 0 .../dom-actions/resize.spec.js | 0 .../dom-actions/scripts.spec.js | 0 .../dom-actions/setAttributes.spec.js | 0 .../dom-actions/setHtml.spec.js | 0 .../dom-actions/setImageSource.spec.js | 0 .../dom-actions/setStyles.spec.js | 0 .../dom-actions/setText.spec.js | 0 .../Personalization/flicker/index.spec.js | 0 .../createDecorateProposition.spec.js | 0 .../handlers/createProcessDomAction.spec.js | 0 .../handlers/createProcessHtmlContent.spec.js | 0 .../createProcessInAppMessage.spec.js | 0 .../createProcessPropositions.spec.js | 0 .../handlers/createProcessRedirect.spec.js | 0 .../handlers/injectCreateProposition.spec.js | 0 .../handlers/processDefaultContent.spec.js | 0 .../actions/displayIframeContent.spec.js | 0 .../initInAppMessageActionsModules.spec.js | 0 .../in-app-message-actions/utils.spec.js | 0 .../responsesMock/eventResponses.js | 0 .../Personalization/topLevel/buildAlloy.js | 0 .../Personalization/topLevel/buildMocks.js | 0 .../topLevel/cartViewDecisions.spec.js | 0 .../topLevel/mergedMetricDecisions.spec.js | 0 .../topLevel/mixedPropositions.spec.js | 0 ...eDecisionsWithDomActionSchemaItems.spec.js | 0 .../topLevel/pageWideScopeDecisions.spec.js | 0 ...cisionsWithoutDomActionSchemaItems.spec.js | 0 .../topLevel/productsViewDecisions.spec.js | 0 .../redirectPageWideScopeDecision.spec.js | 0 .../Personalization/topLevel/resetMocks.js | 0 .../topLevel/scopesFoo1Foo2Decisions.spec.js | 0 .../addRenderAttemptedToDecisions.spec.js | 0 .../utils/createAsyncArray.spec.js | 0 .../utils/isAuthoringModeEnabled.spec.js | 0 .../Personalization/utils/metaUtils.spec.js | 0 .../utils/surfaceUtils.spec.js | 0 .../validateApplyPropositionsOptions.spec.js | 0 .../inAppMessageConsequenceAdapter.spec.js | 0 .../schemaTypeConsequenceAdapter.spec.js | 0 .../RulesEngine/contextTestUtils.js | 0 .../RulesEngine/createApplyResponse.spec.js | 0 .../createConsequenceAdapter.spec.js | 0 .../RulesEngine/createContextProvider.spec.js | 0 .../RulesEngine/createDecisionHistory.spec.js | 0 .../createDecisionProvider.spec.js | 0 .../createEvaluableRulesetPayload.spec.js | 0 .../createEvaluateRulesetsCommand.spec.js | 0 .../RulesEngine/createEventRegistry.spec.js | 0 .../createOnResponseHandler.spec.js | 0 .../createSubscribeRulesetItems.spec.js | 0 .../decisioningContext.browser.spec.js | 0 .../decisioningContext.page.spec.js | 0 .../decisioningContext.referringPage.spec.js | 0 .../decisioningContext.sdkVersion.spec.js | 0 .../decisioningContext.timestamp.spec.js | 0 .../decisioningContext.window.spec.js | 0 .../components/RulesEngine/index.spec.js | 0 .../components/RulesEngine/utils.spec.js | 0 .../StreamingMedia/configValidators.spec.js | 0 .../createMediaEventManager.spec.js | 0 .../StreamingMedia/createMediaRequest.spec.js | 0 .../createMediaResponseHandler.spec.js | 0 .../createMediaSessionCacheManager.spec.js | 0 .../createStreamingMediaComponent.spec.js | 0 .../createTrackMediaEvent.spec.js | 0 .../createTrackMediaSession.spec.js | 0 .../validateMediaEventOptions.spec.js | 0 .../validateMediaSessionOptions.spec.js | 0 .../specs/core/buildAndValidateConfig.spec.js | 0 .../unit/specs/core/componentCreators.spec.js | 0 .../specs/core/config/createConfig.spec.js | 0 .../core/config/createCoreConfigs.spec.js | 0 .../specs/core/consent/createConsent.spec.js | 0 .../consent/createConsentStateMachine.spec.js | 0 .../core/createComponentRegistry.spec.js | 0 .../specs/core/createCookieTransfer.spec.js | 0 .../unit/specs/core/createEvent.spec.js | 0 .../specs/core/createEventManager.spec.js | 0 .../specs/core/createInstanceFunction.spec.js | 0 .../unit/specs/core/createLifecycle.spec.js | 0 .../specs/core/createLogController.spec.js | 0 .../unit/specs/core/createLogger.spec.js | 0 .../edgeNetwork/handleRequestFailure.spec.js | 0 .../edgeNetwork/injectApplyResponse.spec.js | 0 .../edgeNetwork/injectExtractEdgeInfo.spec.js | 0 .../edgeNetwork/injectGetLocationHint.spec.js | 0 .../injectProcessWarningsAndErrors.spec.js | 0 .../injectSendEdgeNetworkRequest.spec.js | 0 .../mergeLifecycleResponses.spec.js | 0 .../specs/core/initializeComponents.spec.js | 0 .../specs/core/injectCreateResponse.spec.js | 0 .../specs/core/injectExecuteCommand.spec.js | 0 .../unit/specs/core/injectHandleError.spec.js | 0 .../core/injectShouldTransferCookie.spec.js | 0 .../core/network/getRequestRetryDelay.spec.js | 0 .../network/injectSendNetworkRequest.spec.js | 0 .../core/network/isRequestRetryable.spec.js | 0 .../injectSendBeaconRequest.spec.js | 0 .../injectSendFetchRequest.spec.js | 0 .../core/requiredComponentCreators.spec.js | 0 .../specs/core/validateCommandOptions.spec.js | 0 .../utils/assignConcatArrayValues.spec.js | 0 .../unit/specs/utils/clone.spec.js | 0 .../unit/specs/utils/crc32.spec.js | 0 .../utils/createCallbackAggregator.spec.js | 0 .../unit/specs/utils/createCollect.spec.js | 0 .../utils/createLoggingCookieJar.spec.js | 0 .../unit/specs/utils/createMerger.spec.js | 0 .../specs/utils/createSubscription.spec.js | 0 .../unit/specs/utils/createTaskQueue.spec.js | 0 .../utils/decodeUriComponentSafely.spec.js | 0 .../unit/specs/utils/deduplicateArray.spec.js | 0 .../unit/specs/utils/deepAssign.spec.js | 0 .../unit/specs/utils/defer.spec.js | 0 .../unit/specs/utils/dom/appendNode.spec.js | 0 .../specs/utils/dom/awaitSelector.spec.js | 1 + .../unit/specs/utils/dom/createNode.spec.js | 0 .../specs/utils/dom/isShadowSelector.spec.js | 0 .../specs/utils/dom/matchesSelector.spec.js | 0 .../specs/utils/dom/querySelectorAll.spec.js | 0 .../unit/specs/utils/dom/removeNode.spec.js | 0 .../unit/specs/utils/dom/selectNodes.spec.js | 0 .../utils/dom/selectNodesWithShadow.spec.js | 0 .../unit/specs/utils/event.spec.js | 0 .../unit/specs/utils/filterObject.spec.js | 0 .../unit/specs/utils/flattenArray.spec.js | 0 .../unit/specs/utils/flattenObject.spec.js | 0 .../unit/specs/utils/getApexDomain.spec.js | 0 .../specs/utils/getLastArrayItems.spec.js | 0 .../utils/getNamespacedCookieName.spec.js | 0 .../specs/utils/getNamespacedStorage.spec.js | 0 .../unit/specs/utils/groupBy.spec.js | 0 ...hirdPartyCookiesSupportedByDefault.spec.js | 0 .../injectDoesIdentityCookieExist.spec.js | 0 .../injectFireReferrerHideableImage.spec.js | 0 .../unit/specs/utils/injectGetBrowser.spec.js | 0 .../unit/specs/utils/injectStorage.spec.js | 0 .../unit/specs/utils/intersection.spec.js | 0 .../unit/specs/utils/isBlankString.spec.js | 0 .../unit/specs/utils/isBoolean.spec.js | 0 .../unit/specs/utils/isEmptyObject.spec.js | 0 .../unit/specs/utils/isFunction.spec.js | 0 .../unit/specs/utils/isInteger.spec.js | 0 .../utils/isNamespacedCookieName.spec.js | 0 .../unit/specs/utils/isNil.spec.js | 0 .../unit/specs/utils/isNonEmptyArray.spec.js | 0 .../unit/specs/utils/isNonEmptyString.spec.js | 0 .../unit/specs/utils/isNumber.spec.js | 0 .../unit/specs/utils/isObject.spec.js | 0 .../unit/specs/utils/isString.spec.js | 0 .../unit/specs/utils/isUnique.spec.js | 0 .../unit/specs/utils/isValidRegExp.spec.js | 0 {vtest => test}/unit/specs/utils/lazy.spec.js | 0 .../unit/specs/utils/networkErrors.spec.js | 0 {vtest => test}/unit/specs/utils/noop.spec.js | 0 .../unit/specs/utils/parseUrl.spec.js | 0 .../prepareConfigOverridesForEdge.spec.js | 0 .../utils/request/createAddIdentity.spec.js | 0 .../createDataCollectionRequest.spec.js | 0 ...createDataCollectionRequestPayload.spec.js | 0 ...eGetAssuranceValidationTokenParams.spec.js | 0 .../utils/request/createHasIdentity.spec.js | 0 .../utils/request/createRequestParams.spec.js | 0 .../utils/sanitizeOrgIdForCookieName.spec.js | 0 .../unit/specs/utils/stackError.spec.js | 0 .../unit/specs/utils/stringToBoolean.spec.js | 0 .../unit/specs/utils/toArray.spec.js | 0 .../unit/specs/utils/toError.spec.js | 0 .../unit/specs/utils/toISOStringLocal.spec.js | 0 .../unit/specs/utils/toInteger.spec.js | 0 .../specs/utils/updateErrorMessage.spec.js | 0 .../utils/validateConfigOverride.spec.js | 0 .../specs/utils/validateIdentityMap.spec.js | 0 .../validation/anythingValidator.spec.js | 0 .../utils/validation/booleanValidator.spec.js | 0 .../validation/callbackValidator.spec.js | 0 .../validation/createAnyOfValidator.spec.js | 0 .../validation/createArrayOfValidator.spec.js | 0 .../validation/createDefaultValidator.spec.js | 0 .../createDeprecatedValidator.spec.js | 0 .../validation/createLiteralValidator.spec.js | 0 .../createMapOfValuesValidator.spec.js | 0 .../validation/createMaximumValidator.spec.js | 0 .../validation/createMinimumValidator.spec.js | 0 .../createNoUnknownFieldsValidator.spec.js | 0 .../createNonEmptyValidator.spec.js | 0 .../createObjectOfValidator.spec.js | 0 .../validation/createRenamedValidator.spec.js | 0 .../createUniqueItemsValidator.spec.js | 0 .../validation/createUniqueValidator.spec.js | 0 .../utils/validation/domainValidator.spec.js | 0 .../utils/validation/enumOfValidator.spec.js | 0 .../utils/validation/integerValidator.spec.js | 0 .../validation/matchesRegexpValidator.spec.js | 0 .../utils/validation/numberValidator.spec.js | 0 .../utils/validation/regexpValidator.spec.js | 0 .../validation/requiredValidator.spec.js | 0 .../utils/validation/stringValidator.spec.js | 0 .../unit/specs/utils/validation/utils.spec.js | 0 vitest.config.js | 2 +- .../specs/utils/dom/awaitSelector.spec.js | 52 ------------------- 349 files changed, 5 insertions(+), 57 deletions(-) rename {vtest => test}/unit/constants/uuidV4Regex.js (100%) rename {vtest => test}/unit/helpers/assertFunctionCallOrder.js (100%) rename {vtest => test}/unit/helpers/cleanUpDomChanges.js (100%) rename {vtest => test}/unit/helpers/createDecoratePropositionForTest.js (100%) rename {vtest => test}/unit/helpers/createMockProposition.js (100%) rename {vtest => test}/unit/helpers/createResponse.js (100%) rename {vtest => test}/unit/helpers/describeRequest.js (100%) rename {vtest => test}/unit/helpers/describeRequestPayload.js (100%) rename {vtest => test}/unit/helpers/describeValidation.js (100%) rename {vtest => test}/unit/helpers/flushPromiseChains.js (100%) rename {vtest => test}/unit/helpers/pause.js (100%) rename {vtest => test}/unit/helpers/removeAllCookies.js (100%) rename {vtest => test}/unit/helpers/testConfigValidators.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/attachClickActivityCollector.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/configValidators.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/createClickActivityStorage.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/createClickedElementProperties.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/createGetClickedElementProperties.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/createInjectClickedElementProperties.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/createRecallAndInjectClickedElementProperties.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/createStorePageViewProperties.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/getLinkName.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/getLinkRegion.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/activityMapExtensionEnabled.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/createTransientStorage.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/determineLinkType.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/dom/elementHasClickHandler.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/dom/extractDomain.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/dom/findClickableElement.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/dom/isButtonSubmitElement.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/dom/isDownloadLink.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/dom/isExitLink.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/dom/isInputSubmitElement.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/dom/isSupportedAnchorElement.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/dom/isSupportedTextNode.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/hasPageName.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/isDifferentDomains.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/trimQueryFromUrl.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/truncateWhiteSpace.spec.js (100%) rename {vtest => test}/unit/specs/components/ActivityCollector/utils/urlStartsWithScheme.spec.js (100%) rename {vtest => test}/unit/specs/components/Audiences/injectProcessDestinations.spec.js (100%) rename {vtest => test}/unit/specs/components/Audiences/injectProcessResponse.spec.js (100%) rename {vtest => test}/unit/specs/components/Consent/computeConsentHash.spec.js (100%) rename {vtest => test}/unit/specs/components/Consent/configValidators.spec.js (100%) rename {vtest => test}/unit/specs/components/Consent/createComponent.spec.js (100%) rename {vtest => test}/unit/specs/components/Consent/createConsentHashStore.spec.js (100%) rename {vtest => test}/unit/specs/components/Consent/createConsentRequest.spec.js (100%) rename {vtest => test}/unit/specs/components/Consent/createConsentRequestPayload.spec.js (100%) rename {vtest => test}/unit/specs/components/Consent/createStoredConsent.spec.js (100%) rename {vtest => test}/unit/specs/components/Consent/injectSendSetConsentRequest.spec.js (100%) rename {vtest => test}/unit/specs/components/Consent/parseConsentCookie.spec.js (100%) rename {vtest => test}/unit/specs/components/Consent/validateSetConsentOptions.spec.js (100%) rename {vtest => test}/unit/specs/components/Context/createComponent.spec.js (100%) rename {vtest => test}/unit/specs/components/Context/implementationDetails.spec.js (100%) rename {vtest => test}/unit/specs/components/Context/injectDevice.spec.js (100%) rename {vtest => test}/unit/specs/components/Context/injectEnvironment.spec.js (100%) rename {vtest => test}/unit/specs/components/Context/injectHighEntropyUserAgentHints.spec.js (100%) rename {vtest => test}/unit/specs/components/Context/injectPlaceContext.spec.js (100%) rename {vtest => test}/unit/specs/components/Context/injectTimestamp.spec.js (100%) rename {vtest => test}/unit/specs/components/Context/injectWeb.spec.js (100%) rename {vtest => test}/unit/specs/components/DataCollector/index.spec.js (100%) rename {vtest => test}/unit/specs/components/DataCollector/validateApplyResponse.spec.js (100%) rename {vtest => test}/unit/specs/components/DataCollector/validateUserEventOptions.spec.js (100%) rename {vtest => test}/unit/specs/components/EventMerge/createComponent.spec.js (100%) rename {vtest => test}/unit/specs/components/EventMerge/createEventMergeId.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/addEcidToPayload.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/configValidators.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/createComponent.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/createLegacyIdentity.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/getIdentity/createGetIdentity.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/getIdentity/createGetIdentityOptionsValidator.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/getIdentity/createIdentityRequest.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/getIdentity/createIdentityRequestPayload.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/getNamespacesFromResponse.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/injectAddEcidQueryToPayload.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/injectAddLegacyEcidToPayload.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/injectAddQueryStringIdentityToPayload.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/injectAwaitIdentityCookie.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/injectEnsureSingleIdentity.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/injectHandleResponseForIdSyncs.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/injectProcessIdSyncs.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/injectSetDomainForInitialIdentityPayload.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/visitorService/awaitVisitorOptIn.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/visitorService/getVisitor.spec.js (100%) rename {vtest => test}/unit/specs/components/Identity/visitorService/injectGetEcidFromVisitor.spec.js (100%) rename {vtest => test}/unit/specs/components/LibraryInfo/index.spec.js (100%) rename {vtest => test}/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js (100%) rename {vtest => test}/unit/specs/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.spec.js (100%) rename {vtest => test}/unit/specs/components/MediaAnalyticsBridge/createMediaHelper.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createActionsProvider.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createApplyPropositions.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createClickStorage.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createComponent.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createFetchDataHandler.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createGetPageLocation.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createHandleConsentFlicker.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createInteractionStorage.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createNotificationHandler.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createOnClickHandler.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createOnDecisionHandler.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createPersonalizationDetails.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createPreprocessors.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createSetTargetMigration.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createViewCacheManager.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/createViewChangeHandler.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/addNonceToInlineStyleElements.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/clicks/collectInteractions.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/collectInteractions.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/createPreprocess.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/customCode.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/createFragment.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/getAttribute.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/getChildNodes.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/getChildren.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/getElementById.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/getFirstChild.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/getNextSibling.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/getNonce.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/getParent.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/helperForEq.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/insertAfter.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/removeAttribute.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/setAttribute.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/setStyle.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/dom/util.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/images.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/move.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/remapCustomCodeOffers.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/remove.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/resize.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/scripts.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/setHtml.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/setStyles.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/dom-actions/setText.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/flicker/index.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/handlers/createDecorateProposition.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/handlers/createProcessInAppMessage.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/handlers/createProcessRedirect.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/handlers/injectCreateProposition.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/handlers/processDefaultContent.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/in-app-message-actions/utils.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/responsesMock/eventResponses.js (100%) rename {vtest => test}/unit/specs/components/Personalization/topLevel/buildAlloy.js (100%) rename {vtest => test}/unit/specs/components/Personalization/topLevel/buildMocks.js (100%) rename {vtest => test}/unit/specs/components/Personalization/topLevel/cartViewDecisions.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/topLevel/mergedMetricDecisions.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/topLevel/pageWideScopeDecisionsWithoutDomActionSchemaItems.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/topLevel/productsViewDecisions.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/topLevel/redirectPageWideScopeDecision.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/topLevel/resetMocks.js (100%) rename {vtest => test}/unit/specs/components/Personalization/topLevel/scopesFoo1Foo2Decisions.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/utils/addRenderAttemptedToDecisions.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/utils/createAsyncArray.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/utils/isAuthoringModeEnabled.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/utils/metaUtils.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/utils/surfaceUtils.spec.js (100%) rename {vtest => test}/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/contextTestUtils.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/createApplyResponse.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/createConsequenceAdapter.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/createContextProvider.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/createDecisionHistory.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/createDecisionProvider.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/createEvaluableRulesetPayload.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/createEvaluateRulesetsCommand.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/createEventRegistry.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/createOnResponseHandler.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/createSubscribeRulesetItems.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/decisioningContext.browser.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/decisioningContext.page.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/decisioningContext.referringPage.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/decisioningContext.sdkVersion.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/decisioningContext.timestamp.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/decisioningContext.window.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/index.spec.js (100%) rename {vtest => test}/unit/specs/components/RulesEngine/utils.spec.js (100%) rename {vtest => test}/unit/specs/components/StreamingMedia/configValidators.spec.js (100%) rename {vtest => test}/unit/specs/components/StreamingMedia/createMediaEventManager.spec.js (100%) rename {vtest => test}/unit/specs/components/StreamingMedia/createMediaRequest.spec.js (100%) rename {vtest => test}/unit/specs/components/StreamingMedia/createMediaResponseHandler.spec.js (100%) rename {vtest => test}/unit/specs/components/StreamingMedia/createMediaSessionCacheManager.spec.js (100%) rename {vtest => test}/unit/specs/components/StreamingMedia/createStreamingMediaComponent.spec.js (100%) rename {vtest => test}/unit/specs/components/StreamingMedia/createTrackMediaEvent.spec.js (100%) rename {vtest => test}/unit/specs/components/StreamingMedia/createTrackMediaSession.spec.js (100%) rename {vtest => test}/unit/specs/components/StreamingMedia/validateMediaEventOptions.spec.js (100%) rename {vtest => test}/unit/specs/components/StreamingMedia/validateMediaSessionOptions.spec.js (100%) rename {vtest => test}/unit/specs/core/buildAndValidateConfig.spec.js (100%) rename {vtest => test}/unit/specs/core/componentCreators.spec.js (100%) rename {vtest => test}/unit/specs/core/config/createConfig.spec.js (100%) rename {vtest => test}/unit/specs/core/config/createCoreConfigs.spec.js (100%) rename {vtest => test}/unit/specs/core/consent/createConsent.spec.js (100%) rename {vtest => test}/unit/specs/core/consent/createConsentStateMachine.spec.js (100%) rename {vtest => test}/unit/specs/core/createComponentRegistry.spec.js (100%) rename {vtest => test}/unit/specs/core/createCookieTransfer.spec.js (100%) rename {vtest => test}/unit/specs/core/createEvent.spec.js (100%) rename {vtest => test}/unit/specs/core/createEventManager.spec.js (100%) rename {vtest => test}/unit/specs/core/createInstanceFunction.spec.js (100%) rename {vtest => test}/unit/specs/core/createLifecycle.spec.js (100%) rename {vtest => test}/unit/specs/core/createLogController.spec.js (100%) rename {vtest => test}/unit/specs/core/createLogger.spec.js (100%) rename {vtest => test}/unit/specs/core/edgeNetwork/handleRequestFailure.spec.js (100%) rename {vtest => test}/unit/specs/core/edgeNetwork/injectApplyResponse.spec.js (100%) rename {vtest => test}/unit/specs/core/edgeNetwork/injectExtractEdgeInfo.spec.js (100%) rename {vtest => test}/unit/specs/core/edgeNetwork/injectGetLocationHint.spec.js (100%) rename {vtest => test}/unit/specs/core/edgeNetwork/injectProcessWarningsAndErrors.spec.js (100%) rename {vtest => test}/unit/specs/core/edgeNetwork/injectSendEdgeNetworkRequest.spec.js (100%) rename {vtest => test}/unit/specs/core/edgeNetwork/mergeLifecycleResponses.spec.js (100%) rename {vtest => test}/unit/specs/core/initializeComponents.spec.js (100%) rename {vtest => test}/unit/specs/core/injectCreateResponse.spec.js (100%) rename {vtest => test}/unit/specs/core/injectExecuteCommand.spec.js (100%) rename {vtest => test}/unit/specs/core/injectHandleError.spec.js (100%) rename {vtest => test}/unit/specs/core/injectShouldTransferCookie.spec.js (100%) rename {vtest => test}/unit/specs/core/network/getRequestRetryDelay.spec.js (100%) rename {vtest => test}/unit/specs/core/network/injectSendNetworkRequest.spec.js (100%) rename {vtest => test}/unit/specs/core/network/isRequestRetryable.spec.js (100%) rename {vtest => test}/unit/specs/core/network/requestMethods/injectSendBeaconRequest.spec.js (100%) rename {vtest => test}/unit/specs/core/network/requestMethods/injectSendFetchRequest.spec.js (100%) rename {vtest => test}/unit/specs/core/requiredComponentCreators.spec.js (100%) rename {vtest => test}/unit/specs/core/validateCommandOptions.spec.js (100%) rename {vtest => test}/unit/specs/utils/assignConcatArrayValues.spec.js (100%) rename {vtest => test}/unit/specs/utils/clone.spec.js (100%) rename {vtest => test}/unit/specs/utils/crc32.spec.js (100%) rename {vtest => test}/unit/specs/utils/createCallbackAggregator.spec.js (100%) rename {vtest => test}/unit/specs/utils/createCollect.spec.js (100%) rename {vtest => test}/unit/specs/utils/createLoggingCookieJar.spec.js (100%) rename {vtest => test}/unit/specs/utils/createMerger.spec.js (100%) rename {vtest => test}/unit/specs/utils/createSubscription.spec.js (100%) rename {vtest => test}/unit/specs/utils/createTaskQueue.spec.js (100%) rename {vtest => test}/unit/specs/utils/decodeUriComponentSafely.spec.js (100%) rename {vtest => test}/unit/specs/utils/deduplicateArray.spec.js (100%) rename {vtest => test}/unit/specs/utils/deepAssign.spec.js (100%) rename {vtest => test}/unit/specs/utils/defer.spec.js (100%) rename {vtest => test}/unit/specs/utils/dom/appendNode.spec.js (100%) rename {vtest => test}/unit/specs/utils/dom/createNode.spec.js (100%) rename {vtest => test}/unit/specs/utils/dom/isShadowSelector.spec.js (100%) rename {vtest => test}/unit/specs/utils/dom/matchesSelector.spec.js (100%) rename {vtest => test}/unit/specs/utils/dom/querySelectorAll.spec.js (100%) rename {vtest => test}/unit/specs/utils/dom/removeNode.spec.js (100%) rename {vtest => test}/unit/specs/utils/dom/selectNodes.spec.js (100%) rename {vtest => test}/unit/specs/utils/dom/selectNodesWithShadow.spec.js (100%) rename {vtest => test}/unit/specs/utils/event.spec.js (100%) rename {vtest => test}/unit/specs/utils/filterObject.spec.js (100%) rename {vtest => test}/unit/specs/utils/flattenArray.spec.js (100%) rename {vtest => test}/unit/specs/utils/flattenObject.spec.js (100%) rename {vtest => test}/unit/specs/utils/getApexDomain.spec.js (100%) rename {vtest => test}/unit/specs/utils/getLastArrayItems.spec.js (100%) rename {vtest => test}/unit/specs/utils/getNamespacedCookieName.spec.js (100%) rename {vtest => test}/unit/specs/utils/getNamespacedStorage.spec.js (100%) rename {vtest => test}/unit/specs/utils/groupBy.spec.js (100%) rename {vtest => test}/unit/specs/utils/injectAreThirdPartyCookiesSupportedByDefault.spec.js (100%) rename {vtest => test}/unit/specs/utils/injectDoesIdentityCookieExist.spec.js (100%) rename {vtest => test}/unit/specs/utils/injectFireReferrerHideableImage.spec.js (100%) rename {vtest => test}/unit/specs/utils/injectGetBrowser.spec.js (100%) rename {vtest => test}/unit/specs/utils/injectStorage.spec.js (100%) rename {vtest => test}/unit/specs/utils/intersection.spec.js (100%) rename {vtest => test}/unit/specs/utils/isBlankString.spec.js (100%) rename {vtest => test}/unit/specs/utils/isBoolean.spec.js (100%) rename {vtest => test}/unit/specs/utils/isEmptyObject.spec.js (100%) rename {vtest => test}/unit/specs/utils/isFunction.spec.js (100%) rename {vtest => test}/unit/specs/utils/isInteger.spec.js (100%) rename {vtest => test}/unit/specs/utils/isNamespacedCookieName.spec.js (100%) rename {vtest => test}/unit/specs/utils/isNil.spec.js (100%) rename {vtest => test}/unit/specs/utils/isNonEmptyArray.spec.js (100%) rename {vtest => test}/unit/specs/utils/isNonEmptyString.spec.js (100%) rename {vtest => test}/unit/specs/utils/isNumber.spec.js (100%) rename {vtest => test}/unit/specs/utils/isObject.spec.js (100%) rename {vtest => test}/unit/specs/utils/isString.spec.js (100%) rename {vtest => test}/unit/specs/utils/isUnique.spec.js (100%) rename {vtest => test}/unit/specs/utils/isValidRegExp.spec.js (100%) rename {vtest => test}/unit/specs/utils/lazy.spec.js (100%) rename {vtest => test}/unit/specs/utils/networkErrors.spec.js (100%) rename {vtest => test}/unit/specs/utils/noop.spec.js (100%) rename {vtest => test}/unit/specs/utils/parseUrl.spec.js (100%) rename {vtest => test}/unit/specs/utils/prepareConfigOverridesForEdge.spec.js (100%) rename {vtest => test}/unit/specs/utils/request/createAddIdentity.spec.js (100%) rename {vtest => test}/unit/specs/utils/request/createDataCollectionRequest.spec.js (100%) rename {vtest => test}/unit/specs/utils/request/createDataCollectionRequestPayload.spec.js (100%) rename {vtest => test}/unit/specs/utils/request/createGetAssuranceValidationTokenParams.spec.js (100%) rename {vtest => test}/unit/specs/utils/request/createHasIdentity.spec.js (100%) rename {vtest => test}/unit/specs/utils/request/createRequestParams.spec.js (100%) rename {vtest => test}/unit/specs/utils/sanitizeOrgIdForCookieName.spec.js (100%) rename {vtest => test}/unit/specs/utils/stackError.spec.js (100%) rename {vtest => test}/unit/specs/utils/stringToBoolean.spec.js (100%) rename {vtest => test}/unit/specs/utils/toArray.spec.js (100%) rename {vtest => test}/unit/specs/utils/toError.spec.js (100%) rename {vtest => test}/unit/specs/utils/toISOStringLocal.spec.js (100%) rename {vtest => test}/unit/specs/utils/toInteger.spec.js (100%) rename {vtest => test}/unit/specs/utils/updateErrorMessage.spec.js (100%) rename {vtest => test}/unit/specs/utils/validateConfigOverride.spec.js (100%) rename {vtest => test}/unit/specs/utils/validateIdentityMap.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/anythingValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/booleanValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/callbackValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createAnyOfValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createArrayOfValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createDefaultValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createDeprecatedValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createLiteralValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createMapOfValuesValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createMaximumValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createMinimumValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createNoUnknownFieldsValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createNonEmptyValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createObjectOfValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createRenamedValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createUniqueItemsValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/createUniqueValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/domainValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/enumOfValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/integerValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/matchesRegexpValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/numberValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/regexpValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/requiredValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/stringValidator.spec.js (100%) rename {vtest => test}/unit/specs/utils/validation/utils.spec.js (100%) delete mode 100644 vtest/unit/specs/utils/dom/awaitSelector.spec.js diff --git a/.husky/pre-commit b/.husky/pre-commit index d7ea2d80f..5e0776cc7 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,3 @@ -npm run checkthattestfilesexist STAGED_ONLY=true npm run add-license npx lint-staged npm run test:unit diff --git a/eslint.config.js b/eslint.config.js index 35e16ea03..90e339e5c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -130,7 +130,7 @@ export default [ }, }, { - files: ["{test,vtest}/**/*.{cjs,js}"], + files: ["test/**/*.{cjs,js}"], rules: { "import/extensions": [ "error", diff --git a/package.json b/package.json index 6d3172cb5..567a921bf 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ }, "scripts": { "clean": "rimraf dist distTest libEs5 libEs6", - "lint": "eslint --cache --fix \"*.{js,cjs,mjs,jsx}\" \"{src,test,vtest,scripts}/**/*.{js,cjs,mjs,jsx}\"", - "format": "prettier --write \"*.{html,js,cjs,mjs,jsx}\" \"{sandbox,src,test,scripts,vtest}/**/*.{html,js,cjs,mjs,jsx}\"", + "lint": "eslint --cache --fix \"*.{js,cjs,mjs,jsx}\" \"{src,test,scripts}/**/*.{js,cjs,mjs,jsx}\"", + "format": "prettier --write \"*.{html,js,cjs,mjs,jsx}\" \"{sandbox,src,test,scripts}/**/*.{html,js,cjs,mjs,jsx}\"", "test": "npm run test:unit && npm run test:scripts", "test:unit": "vitest run", "test:unit:debug": "vitest --no-file-parallelism --browser.headless=false", diff --git a/vtest/unit/constants/uuidV4Regex.js b/test/unit/constants/uuidV4Regex.js similarity index 100% rename from vtest/unit/constants/uuidV4Regex.js rename to test/unit/constants/uuidV4Regex.js diff --git a/vtest/unit/helpers/assertFunctionCallOrder.js b/test/unit/helpers/assertFunctionCallOrder.js similarity index 100% rename from vtest/unit/helpers/assertFunctionCallOrder.js rename to test/unit/helpers/assertFunctionCallOrder.js diff --git a/vtest/unit/helpers/cleanUpDomChanges.js b/test/unit/helpers/cleanUpDomChanges.js similarity index 100% rename from vtest/unit/helpers/cleanUpDomChanges.js rename to test/unit/helpers/cleanUpDomChanges.js diff --git a/vtest/unit/helpers/createDecoratePropositionForTest.js b/test/unit/helpers/createDecoratePropositionForTest.js similarity index 100% rename from vtest/unit/helpers/createDecoratePropositionForTest.js rename to test/unit/helpers/createDecoratePropositionForTest.js diff --git a/vtest/unit/helpers/createMockProposition.js b/test/unit/helpers/createMockProposition.js similarity index 100% rename from vtest/unit/helpers/createMockProposition.js rename to test/unit/helpers/createMockProposition.js diff --git a/vtest/unit/helpers/createResponse.js b/test/unit/helpers/createResponse.js similarity index 100% rename from vtest/unit/helpers/createResponse.js rename to test/unit/helpers/createResponse.js diff --git a/vtest/unit/helpers/describeRequest.js b/test/unit/helpers/describeRequest.js similarity index 100% rename from vtest/unit/helpers/describeRequest.js rename to test/unit/helpers/describeRequest.js diff --git a/vtest/unit/helpers/describeRequestPayload.js b/test/unit/helpers/describeRequestPayload.js similarity index 100% rename from vtest/unit/helpers/describeRequestPayload.js rename to test/unit/helpers/describeRequestPayload.js diff --git a/vtest/unit/helpers/describeValidation.js b/test/unit/helpers/describeValidation.js similarity index 100% rename from vtest/unit/helpers/describeValidation.js rename to test/unit/helpers/describeValidation.js diff --git a/vtest/unit/helpers/flushPromiseChains.js b/test/unit/helpers/flushPromiseChains.js similarity index 100% rename from vtest/unit/helpers/flushPromiseChains.js rename to test/unit/helpers/flushPromiseChains.js diff --git a/vtest/unit/helpers/pause.js b/test/unit/helpers/pause.js similarity index 100% rename from vtest/unit/helpers/pause.js rename to test/unit/helpers/pause.js diff --git a/vtest/unit/helpers/removeAllCookies.js b/test/unit/helpers/removeAllCookies.js similarity index 100% rename from vtest/unit/helpers/removeAllCookies.js rename to test/unit/helpers/removeAllCookies.js diff --git a/vtest/unit/helpers/testConfigValidators.js b/test/unit/helpers/testConfigValidators.js similarity index 100% rename from vtest/unit/helpers/testConfigValidators.js rename to test/unit/helpers/testConfigValidators.js diff --git a/vtest/unit/specs/components/ActivityCollector/attachClickActivityCollector.spec.js b/test/unit/specs/components/ActivityCollector/attachClickActivityCollector.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/attachClickActivityCollector.spec.js rename to test/unit/specs/components/ActivityCollector/attachClickActivityCollector.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/configValidators.spec.js b/test/unit/specs/components/ActivityCollector/configValidators.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/configValidators.spec.js rename to test/unit/specs/components/ActivityCollector/configValidators.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/createClickActivityStorage.spec.js b/test/unit/specs/components/ActivityCollector/createClickActivityStorage.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/createClickActivityStorage.spec.js rename to test/unit/specs/components/ActivityCollector/createClickActivityStorage.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/createClickedElementProperties.spec.js b/test/unit/specs/components/ActivityCollector/createClickedElementProperties.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/createClickedElementProperties.spec.js rename to test/unit/specs/components/ActivityCollector/createClickedElementProperties.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/createGetClickedElementProperties.spec.js b/test/unit/specs/components/ActivityCollector/createGetClickedElementProperties.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/createGetClickedElementProperties.spec.js rename to test/unit/specs/components/ActivityCollector/createGetClickedElementProperties.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/createInjectClickedElementProperties.spec.js b/test/unit/specs/components/ActivityCollector/createInjectClickedElementProperties.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/createInjectClickedElementProperties.spec.js rename to test/unit/specs/components/ActivityCollector/createInjectClickedElementProperties.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/createRecallAndInjectClickedElementProperties.spec.js b/test/unit/specs/components/ActivityCollector/createRecallAndInjectClickedElementProperties.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/createRecallAndInjectClickedElementProperties.spec.js rename to test/unit/specs/components/ActivityCollector/createRecallAndInjectClickedElementProperties.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/createStorePageViewProperties.spec.js b/test/unit/specs/components/ActivityCollector/createStorePageViewProperties.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/createStorePageViewProperties.spec.js rename to test/unit/specs/components/ActivityCollector/createStorePageViewProperties.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/getLinkName.spec.js b/test/unit/specs/components/ActivityCollector/getLinkName.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/getLinkName.spec.js rename to test/unit/specs/components/ActivityCollector/getLinkName.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/getLinkRegion.spec.js b/test/unit/specs/components/ActivityCollector/getLinkRegion.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/getLinkRegion.spec.js rename to test/unit/specs/components/ActivityCollector/getLinkRegion.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/activityMapExtensionEnabled.spec.js b/test/unit/specs/components/ActivityCollector/utils/activityMapExtensionEnabled.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/activityMapExtensionEnabled.spec.js rename to test/unit/specs/components/ActivityCollector/utils/activityMapExtensionEnabled.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/createTransientStorage.spec.js b/test/unit/specs/components/ActivityCollector/utils/createTransientStorage.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/createTransientStorage.spec.js rename to test/unit/specs/components/ActivityCollector/utils/createTransientStorage.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/determineLinkType.spec.js b/test/unit/specs/components/ActivityCollector/utils/determineLinkType.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/determineLinkType.spec.js rename to test/unit/specs/components/ActivityCollector/utils/determineLinkType.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/elementHasClickHandler.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/elementHasClickHandler.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/dom/elementHasClickHandler.spec.js rename to test/unit/specs/components/ActivityCollector/utils/dom/elementHasClickHandler.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/extractDomain.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/extractDomain.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/dom/extractDomain.spec.js rename to test/unit/specs/components/ActivityCollector/utils/dom/extractDomain.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/findClickableElement.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/findClickableElement.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/dom/findClickableElement.spec.js rename to test/unit/specs/components/ActivityCollector/utils/dom/findClickableElement.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.spec.js rename to test/unit/specs/components/ActivityCollector/utils/dom/getAbsoluteUrlFromAnchorElement.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/isButtonSubmitElement.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/isButtonSubmitElement.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/dom/isButtonSubmitElement.spec.js rename to test/unit/specs/components/ActivityCollector/utils/dom/isButtonSubmitElement.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/isDownloadLink.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/isDownloadLink.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/dom/isDownloadLink.spec.js rename to test/unit/specs/components/ActivityCollector/utils/dom/isDownloadLink.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/isExitLink.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/isExitLink.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/dom/isExitLink.spec.js rename to test/unit/specs/components/ActivityCollector/utils/dom/isExitLink.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/isInputSubmitElement.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/isInputSubmitElement.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/dom/isInputSubmitElement.spec.js rename to test/unit/specs/components/ActivityCollector/utils/dom/isInputSubmitElement.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/isSupportedAnchorElement.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/isSupportedAnchorElement.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/dom/isSupportedAnchorElement.spec.js rename to test/unit/specs/components/ActivityCollector/utils/dom/isSupportedAnchorElement.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/dom/isSupportedTextNode.spec.js b/test/unit/specs/components/ActivityCollector/utils/dom/isSupportedTextNode.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/dom/isSupportedTextNode.spec.js rename to test/unit/specs/components/ActivityCollector/utils/dom/isSupportedTextNode.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/hasPageName.spec.js b/test/unit/specs/components/ActivityCollector/utils/hasPageName.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/hasPageName.spec.js rename to test/unit/specs/components/ActivityCollector/utils/hasPageName.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/isDifferentDomains.spec.js b/test/unit/specs/components/ActivityCollector/utils/isDifferentDomains.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/isDifferentDomains.spec.js rename to test/unit/specs/components/ActivityCollector/utils/isDifferentDomains.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/trimQueryFromUrl.spec.js b/test/unit/specs/components/ActivityCollector/utils/trimQueryFromUrl.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/trimQueryFromUrl.spec.js rename to test/unit/specs/components/ActivityCollector/utils/trimQueryFromUrl.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/truncateWhiteSpace.spec.js b/test/unit/specs/components/ActivityCollector/utils/truncateWhiteSpace.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/truncateWhiteSpace.spec.js rename to test/unit/specs/components/ActivityCollector/utils/truncateWhiteSpace.spec.js diff --git a/vtest/unit/specs/components/ActivityCollector/utils/urlStartsWithScheme.spec.js b/test/unit/specs/components/ActivityCollector/utils/urlStartsWithScheme.spec.js similarity index 100% rename from vtest/unit/specs/components/ActivityCollector/utils/urlStartsWithScheme.spec.js rename to test/unit/specs/components/ActivityCollector/utils/urlStartsWithScheme.spec.js diff --git a/vtest/unit/specs/components/Audiences/injectProcessDestinations.spec.js b/test/unit/specs/components/Audiences/injectProcessDestinations.spec.js similarity index 100% rename from vtest/unit/specs/components/Audiences/injectProcessDestinations.spec.js rename to test/unit/specs/components/Audiences/injectProcessDestinations.spec.js diff --git a/vtest/unit/specs/components/Audiences/injectProcessResponse.spec.js b/test/unit/specs/components/Audiences/injectProcessResponse.spec.js similarity index 100% rename from vtest/unit/specs/components/Audiences/injectProcessResponse.spec.js rename to test/unit/specs/components/Audiences/injectProcessResponse.spec.js diff --git a/vtest/unit/specs/components/Consent/computeConsentHash.spec.js b/test/unit/specs/components/Consent/computeConsentHash.spec.js similarity index 100% rename from vtest/unit/specs/components/Consent/computeConsentHash.spec.js rename to test/unit/specs/components/Consent/computeConsentHash.spec.js diff --git a/vtest/unit/specs/components/Consent/configValidators.spec.js b/test/unit/specs/components/Consent/configValidators.spec.js similarity index 100% rename from vtest/unit/specs/components/Consent/configValidators.spec.js rename to test/unit/specs/components/Consent/configValidators.spec.js diff --git a/vtest/unit/specs/components/Consent/createComponent.spec.js b/test/unit/specs/components/Consent/createComponent.spec.js similarity index 100% rename from vtest/unit/specs/components/Consent/createComponent.spec.js rename to test/unit/specs/components/Consent/createComponent.spec.js diff --git a/vtest/unit/specs/components/Consent/createConsentHashStore.spec.js b/test/unit/specs/components/Consent/createConsentHashStore.spec.js similarity index 100% rename from vtest/unit/specs/components/Consent/createConsentHashStore.spec.js rename to test/unit/specs/components/Consent/createConsentHashStore.spec.js diff --git a/vtest/unit/specs/components/Consent/createConsentRequest.spec.js b/test/unit/specs/components/Consent/createConsentRequest.spec.js similarity index 100% rename from vtest/unit/specs/components/Consent/createConsentRequest.spec.js rename to test/unit/specs/components/Consent/createConsentRequest.spec.js diff --git a/vtest/unit/specs/components/Consent/createConsentRequestPayload.spec.js b/test/unit/specs/components/Consent/createConsentRequestPayload.spec.js similarity index 100% rename from vtest/unit/specs/components/Consent/createConsentRequestPayload.spec.js rename to test/unit/specs/components/Consent/createConsentRequestPayload.spec.js diff --git a/vtest/unit/specs/components/Consent/createStoredConsent.spec.js b/test/unit/specs/components/Consent/createStoredConsent.spec.js similarity index 100% rename from vtest/unit/specs/components/Consent/createStoredConsent.spec.js rename to test/unit/specs/components/Consent/createStoredConsent.spec.js diff --git a/vtest/unit/specs/components/Consent/injectSendSetConsentRequest.spec.js b/test/unit/specs/components/Consent/injectSendSetConsentRequest.spec.js similarity index 100% rename from vtest/unit/specs/components/Consent/injectSendSetConsentRequest.spec.js rename to test/unit/specs/components/Consent/injectSendSetConsentRequest.spec.js diff --git a/vtest/unit/specs/components/Consent/parseConsentCookie.spec.js b/test/unit/specs/components/Consent/parseConsentCookie.spec.js similarity index 100% rename from vtest/unit/specs/components/Consent/parseConsentCookie.spec.js rename to test/unit/specs/components/Consent/parseConsentCookie.spec.js diff --git a/vtest/unit/specs/components/Consent/validateSetConsentOptions.spec.js b/test/unit/specs/components/Consent/validateSetConsentOptions.spec.js similarity index 100% rename from vtest/unit/specs/components/Consent/validateSetConsentOptions.spec.js rename to test/unit/specs/components/Consent/validateSetConsentOptions.spec.js diff --git a/vtest/unit/specs/components/Context/createComponent.spec.js b/test/unit/specs/components/Context/createComponent.spec.js similarity index 100% rename from vtest/unit/specs/components/Context/createComponent.spec.js rename to test/unit/specs/components/Context/createComponent.spec.js diff --git a/vtest/unit/specs/components/Context/implementationDetails.spec.js b/test/unit/specs/components/Context/implementationDetails.spec.js similarity index 100% rename from vtest/unit/specs/components/Context/implementationDetails.spec.js rename to test/unit/specs/components/Context/implementationDetails.spec.js diff --git a/vtest/unit/specs/components/Context/injectDevice.spec.js b/test/unit/specs/components/Context/injectDevice.spec.js similarity index 100% rename from vtest/unit/specs/components/Context/injectDevice.spec.js rename to test/unit/specs/components/Context/injectDevice.spec.js diff --git a/vtest/unit/specs/components/Context/injectEnvironment.spec.js b/test/unit/specs/components/Context/injectEnvironment.spec.js similarity index 100% rename from vtest/unit/specs/components/Context/injectEnvironment.spec.js rename to test/unit/specs/components/Context/injectEnvironment.spec.js diff --git a/vtest/unit/specs/components/Context/injectHighEntropyUserAgentHints.spec.js b/test/unit/specs/components/Context/injectHighEntropyUserAgentHints.spec.js similarity index 100% rename from vtest/unit/specs/components/Context/injectHighEntropyUserAgentHints.spec.js rename to test/unit/specs/components/Context/injectHighEntropyUserAgentHints.spec.js diff --git a/vtest/unit/specs/components/Context/injectPlaceContext.spec.js b/test/unit/specs/components/Context/injectPlaceContext.spec.js similarity index 100% rename from vtest/unit/specs/components/Context/injectPlaceContext.spec.js rename to test/unit/specs/components/Context/injectPlaceContext.spec.js diff --git a/vtest/unit/specs/components/Context/injectTimestamp.spec.js b/test/unit/specs/components/Context/injectTimestamp.spec.js similarity index 100% rename from vtest/unit/specs/components/Context/injectTimestamp.spec.js rename to test/unit/specs/components/Context/injectTimestamp.spec.js diff --git a/vtest/unit/specs/components/Context/injectWeb.spec.js b/test/unit/specs/components/Context/injectWeb.spec.js similarity index 100% rename from vtest/unit/specs/components/Context/injectWeb.spec.js rename to test/unit/specs/components/Context/injectWeb.spec.js diff --git a/vtest/unit/specs/components/DataCollector/index.spec.js b/test/unit/specs/components/DataCollector/index.spec.js similarity index 100% rename from vtest/unit/specs/components/DataCollector/index.spec.js rename to test/unit/specs/components/DataCollector/index.spec.js diff --git a/vtest/unit/specs/components/DataCollector/validateApplyResponse.spec.js b/test/unit/specs/components/DataCollector/validateApplyResponse.spec.js similarity index 100% rename from vtest/unit/specs/components/DataCollector/validateApplyResponse.spec.js rename to test/unit/specs/components/DataCollector/validateApplyResponse.spec.js diff --git a/vtest/unit/specs/components/DataCollector/validateUserEventOptions.spec.js b/test/unit/specs/components/DataCollector/validateUserEventOptions.spec.js similarity index 100% rename from vtest/unit/specs/components/DataCollector/validateUserEventOptions.spec.js rename to test/unit/specs/components/DataCollector/validateUserEventOptions.spec.js diff --git a/vtest/unit/specs/components/EventMerge/createComponent.spec.js b/test/unit/specs/components/EventMerge/createComponent.spec.js similarity index 100% rename from vtest/unit/specs/components/EventMerge/createComponent.spec.js rename to test/unit/specs/components/EventMerge/createComponent.spec.js diff --git a/vtest/unit/specs/components/EventMerge/createEventMergeId.spec.js b/test/unit/specs/components/EventMerge/createEventMergeId.spec.js similarity index 100% rename from vtest/unit/specs/components/EventMerge/createEventMergeId.spec.js rename to test/unit/specs/components/EventMerge/createEventMergeId.spec.js diff --git a/vtest/unit/specs/components/Identity/addEcidToPayload.spec.js b/test/unit/specs/components/Identity/addEcidToPayload.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/addEcidToPayload.spec.js rename to test/unit/specs/components/Identity/addEcidToPayload.spec.js diff --git a/vtest/unit/specs/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.spec.js b/test/unit/specs/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.spec.js rename to test/unit/specs/components/Identity/appendIdentityToUrl/appendIdentityToUrlOptionsValidator.spec.js diff --git a/vtest/unit/specs/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.spec.js b/test/unit/specs/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.spec.js rename to test/unit/specs/components/Identity/appendIdentityToUrl/injectAppendIdentityToUrl.spec.js diff --git a/vtest/unit/specs/components/Identity/configValidators.spec.js b/test/unit/specs/components/Identity/configValidators.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/configValidators.spec.js rename to test/unit/specs/components/Identity/configValidators.spec.js diff --git a/vtest/unit/specs/components/Identity/createComponent.spec.js b/test/unit/specs/components/Identity/createComponent.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/createComponent.spec.js rename to test/unit/specs/components/Identity/createComponent.spec.js diff --git a/vtest/unit/specs/components/Identity/createLegacyIdentity.spec.js b/test/unit/specs/components/Identity/createLegacyIdentity.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/createLegacyIdentity.spec.js rename to test/unit/specs/components/Identity/createLegacyIdentity.spec.js diff --git a/vtest/unit/specs/components/Identity/getIdentity/createGetIdentity.spec.js b/test/unit/specs/components/Identity/getIdentity/createGetIdentity.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/getIdentity/createGetIdentity.spec.js rename to test/unit/specs/components/Identity/getIdentity/createGetIdentity.spec.js diff --git a/vtest/unit/specs/components/Identity/getIdentity/createGetIdentityOptionsValidator.spec.js b/test/unit/specs/components/Identity/getIdentity/createGetIdentityOptionsValidator.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/getIdentity/createGetIdentityOptionsValidator.spec.js rename to test/unit/specs/components/Identity/getIdentity/createGetIdentityOptionsValidator.spec.js diff --git a/vtest/unit/specs/components/Identity/getIdentity/createIdentityRequest.spec.js b/test/unit/specs/components/Identity/getIdentity/createIdentityRequest.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/getIdentity/createIdentityRequest.spec.js rename to test/unit/specs/components/Identity/getIdentity/createIdentityRequest.spec.js diff --git a/vtest/unit/specs/components/Identity/getIdentity/createIdentityRequestPayload.spec.js b/test/unit/specs/components/Identity/getIdentity/createIdentityRequestPayload.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/getIdentity/createIdentityRequestPayload.spec.js rename to test/unit/specs/components/Identity/getIdentity/createIdentityRequestPayload.spec.js diff --git a/vtest/unit/specs/components/Identity/getNamespacesFromResponse.spec.js b/test/unit/specs/components/Identity/getNamespacesFromResponse.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/getNamespacesFromResponse.spec.js rename to test/unit/specs/components/Identity/getNamespacesFromResponse.spec.js diff --git a/vtest/unit/specs/components/Identity/injectAddEcidQueryToPayload.spec.js b/test/unit/specs/components/Identity/injectAddEcidQueryToPayload.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/injectAddEcidQueryToPayload.spec.js rename to test/unit/specs/components/Identity/injectAddEcidQueryToPayload.spec.js diff --git a/vtest/unit/specs/components/Identity/injectAddLegacyEcidToPayload.spec.js b/test/unit/specs/components/Identity/injectAddLegacyEcidToPayload.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/injectAddLegacyEcidToPayload.spec.js rename to test/unit/specs/components/Identity/injectAddLegacyEcidToPayload.spec.js diff --git a/vtest/unit/specs/components/Identity/injectAddQueryStringIdentityToPayload.spec.js b/test/unit/specs/components/Identity/injectAddQueryStringIdentityToPayload.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/injectAddQueryStringIdentityToPayload.spec.js rename to test/unit/specs/components/Identity/injectAddQueryStringIdentityToPayload.spec.js diff --git a/vtest/unit/specs/components/Identity/injectAwaitIdentityCookie.spec.js b/test/unit/specs/components/Identity/injectAwaitIdentityCookie.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/injectAwaitIdentityCookie.spec.js rename to test/unit/specs/components/Identity/injectAwaitIdentityCookie.spec.js diff --git a/vtest/unit/specs/components/Identity/injectEnsureSingleIdentity.spec.js b/test/unit/specs/components/Identity/injectEnsureSingleIdentity.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/injectEnsureSingleIdentity.spec.js rename to test/unit/specs/components/Identity/injectEnsureSingleIdentity.spec.js diff --git a/vtest/unit/specs/components/Identity/injectHandleResponseForIdSyncs.spec.js b/test/unit/specs/components/Identity/injectHandleResponseForIdSyncs.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/injectHandleResponseForIdSyncs.spec.js rename to test/unit/specs/components/Identity/injectHandleResponseForIdSyncs.spec.js diff --git a/vtest/unit/specs/components/Identity/injectProcessIdSyncs.spec.js b/test/unit/specs/components/Identity/injectProcessIdSyncs.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/injectProcessIdSyncs.spec.js rename to test/unit/specs/components/Identity/injectProcessIdSyncs.spec.js diff --git a/vtest/unit/specs/components/Identity/injectSetDomainForInitialIdentityPayload.spec.js b/test/unit/specs/components/Identity/injectSetDomainForInitialIdentityPayload.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/injectSetDomainForInitialIdentityPayload.spec.js rename to test/unit/specs/components/Identity/injectSetDomainForInitialIdentityPayload.spec.js diff --git a/vtest/unit/specs/components/Identity/visitorService/awaitVisitorOptIn.spec.js b/test/unit/specs/components/Identity/visitorService/awaitVisitorOptIn.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/visitorService/awaitVisitorOptIn.spec.js rename to test/unit/specs/components/Identity/visitorService/awaitVisitorOptIn.spec.js diff --git a/vtest/unit/specs/components/Identity/visitorService/getVisitor.spec.js b/test/unit/specs/components/Identity/visitorService/getVisitor.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/visitorService/getVisitor.spec.js rename to test/unit/specs/components/Identity/visitorService/getVisitor.spec.js diff --git a/vtest/unit/specs/components/Identity/visitorService/injectGetEcidFromVisitor.spec.js b/test/unit/specs/components/Identity/visitorService/injectGetEcidFromVisitor.spec.js similarity index 100% rename from vtest/unit/specs/components/Identity/visitorService/injectGetEcidFromVisitor.spec.js rename to test/unit/specs/components/Identity/visitorService/injectGetEcidFromVisitor.spec.js diff --git a/vtest/unit/specs/components/LibraryInfo/index.spec.js b/test/unit/specs/components/LibraryInfo/index.spec.js similarity index 100% rename from vtest/unit/specs/components/LibraryInfo/index.spec.js rename to test/unit/specs/components/LibraryInfo/index.spec.js diff --git a/vtest/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js b/test/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js similarity index 100% rename from vtest/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js rename to test/unit/specs/components/MediaAnalyticsBridge/createGetInstance.spec.js diff --git a/vtest/unit/specs/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.spec.js b/test/unit/specs/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.spec.js similarity index 100% rename from vtest/unit/specs/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.spec.js rename to test/unit/specs/components/MediaAnalyticsBridge/createMediaAnalyticsBridgeComponent.spec.js diff --git a/vtest/unit/specs/components/MediaAnalyticsBridge/createMediaHelper.spec.js b/test/unit/specs/components/MediaAnalyticsBridge/createMediaHelper.spec.js similarity index 100% rename from vtest/unit/specs/components/MediaAnalyticsBridge/createMediaHelper.spec.js rename to test/unit/specs/components/MediaAnalyticsBridge/createMediaHelper.spec.js diff --git a/vtest/unit/specs/components/Personalization/createActionsProvider.spec.js b/test/unit/specs/components/Personalization/createActionsProvider.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createActionsProvider.spec.js rename to test/unit/specs/components/Personalization/createActionsProvider.spec.js diff --git a/vtest/unit/specs/components/Personalization/createApplyPropositions.spec.js b/test/unit/specs/components/Personalization/createApplyPropositions.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createApplyPropositions.spec.js rename to test/unit/specs/components/Personalization/createApplyPropositions.spec.js diff --git a/vtest/unit/specs/components/Personalization/createClickStorage.spec.js b/test/unit/specs/components/Personalization/createClickStorage.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createClickStorage.spec.js rename to test/unit/specs/components/Personalization/createClickStorage.spec.js diff --git a/vtest/unit/specs/components/Personalization/createComponent.spec.js b/test/unit/specs/components/Personalization/createComponent.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createComponent.spec.js rename to test/unit/specs/components/Personalization/createComponent.spec.js diff --git a/vtest/unit/specs/components/Personalization/createFetchDataHandler.spec.js b/test/unit/specs/components/Personalization/createFetchDataHandler.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createFetchDataHandler.spec.js rename to test/unit/specs/components/Personalization/createFetchDataHandler.spec.js diff --git a/vtest/unit/specs/components/Personalization/createGetPageLocation.spec.js b/test/unit/specs/components/Personalization/createGetPageLocation.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createGetPageLocation.spec.js rename to test/unit/specs/components/Personalization/createGetPageLocation.spec.js diff --git a/vtest/unit/specs/components/Personalization/createHandleConsentFlicker.spec.js b/test/unit/specs/components/Personalization/createHandleConsentFlicker.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createHandleConsentFlicker.spec.js rename to test/unit/specs/components/Personalization/createHandleConsentFlicker.spec.js diff --git a/vtest/unit/specs/components/Personalization/createInteractionStorage.spec.js b/test/unit/specs/components/Personalization/createInteractionStorage.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createInteractionStorage.spec.js rename to test/unit/specs/components/Personalization/createInteractionStorage.spec.js diff --git a/vtest/unit/specs/components/Personalization/createNotificationHandler.spec.js b/test/unit/specs/components/Personalization/createNotificationHandler.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createNotificationHandler.spec.js rename to test/unit/specs/components/Personalization/createNotificationHandler.spec.js diff --git a/vtest/unit/specs/components/Personalization/createOnClickHandler.spec.js b/test/unit/specs/components/Personalization/createOnClickHandler.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createOnClickHandler.spec.js rename to test/unit/specs/components/Personalization/createOnClickHandler.spec.js diff --git a/vtest/unit/specs/components/Personalization/createOnDecisionHandler.spec.js b/test/unit/specs/components/Personalization/createOnDecisionHandler.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createOnDecisionHandler.spec.js rename to test/unit/specs/components/Personalization/createOnDecisionHandler.spec.js diff --git a/vtest/unit/specs/components/Personalization/createPersonalizationDetails.spec.js b/test/unit/specs/components/Personalization/createPersonalizationDetails.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createPersonalizationDetails.spec.js rename to test/unit/specs/components/Personalization/createPersonalizationDetails.spec.js diff --git a/vtest/unit/specs/components/Personalization/createPreprocessors.spec.js b/test/unit/specs/components/Personalization/createPreprocessors.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createPreprocessors.spec.js rename to test/unit/specs/components/Personalization/createPreprocessors.spec.js diff --git a/vtest/unit/specs/components/Personalization/createSetTargetMigration.spec.js b/test/unit/specs/components/Personalization/createSetTargetMigration.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createSetTargetMigration.spec.js rename to test/unit/specs/components/Personalization/createSetTargetMigration.spec.js diff --git a/vtest/unit/specs/components/Personalization/createViewCacheManager.spec.js b/test/unit/specs/components/Personalization/createViewCacheManager.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createViewCacheManager.spec.js rename to test/unit/specs/components/Personalization/createViewCacheManager.spec.js diff --git a/vtest/unit/specs/components/Personalization/createViewChangeHandler.spec.js b/test/unit/specs/components/Personalization/createViewChangeHandler.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/createViewChangeHandler.spec.js rename to test/unit/specs/components/Personalization/createViewChangeHandler.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/addNonceToInlineStyleElements.spec.js b/test/unit/specs/components/Personalization/dom-actions/addNonceToInlineStyleElements.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/addNonceToInlineStyleElements.spec.js rename to test/unit/specs/components/Personalization/dom-actions/addNonceToInlineStyleElements.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js b/test/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js rename to test/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js b/test/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js rename to test/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/clicks/collectInteractions.spec.js b/test/unit/specs/components/Personalization/dom-actions/clicks/collectInteractions.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/clicks/collectInteractions.spec.js rename to test/unit/specs/components/Personalization/dom-actions/clicks/collectInteractions.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/collectInteractions.spec.js b/test/unit/specs/components/Personalization/dom-actions/collectInteractions.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/collectInteractions.spec.js rename to test/unit/specs/components/Personalization/dom-actions/collectInteractions.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/createPreprocess.spec.js b/test/unit/specs/components/Personalization/dom-actions/createPreprocess.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/createPreprocess.spec.js rename to test/unit/specs/components/Personalization/dom-actions/createPreprocess.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js b/test/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js rename to test/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/customCode.spec.js b/test/unit/specs/components/Personalization/dom-actions/customCode.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/customCode.spec.js rename to test/unit/specs/components/Personalization/dom-actions/customCode.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/createFragment.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/createFragment.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/createFragment.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/createFragment.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getAttribute.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getAttribute.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/getAttribute.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/getAttribute.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getChildNodes.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getChildNodes.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/getChildNodes.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/getChildNodes.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getChildren.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getChildren.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/getChildren.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/getChildren.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getElementById.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getElementById.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/getElementById.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/getElementById.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getFirstChild.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getFirstChild.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/getFirstChild.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/getFirstChild.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getNextSibling.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getNextSibling.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/getNextSibling.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/getNextSibling.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getNonce.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getNonce.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/getNonce.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/getNonce.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/getParent.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/getParent.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/getParent.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/getParent.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/helperForEq.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/helperForEq.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/helperForEq.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/helperForEq.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/insertAfter.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/insertAfter.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/insertAfter.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/insertAfter.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/matchesSelectorWithEq.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/removeAttribute.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/removeAttribute.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/removeAttribute.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/removeAttribute.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/selectNodesWithEq.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/setAttribute.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/setAttribute.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/setAttribute.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/setAttribute.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/setStyle.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/setStyle.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/setStyle.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/setStyle.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/dom/util.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/util.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/dom/util.spec.js rename to test/unit/specs/components/Personalization/dom-actions/dom/util.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/images.spec.js b/test/unit/specs/components/Personalization/dom-actions/images.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/images.spec.js rename to test/unit/specs/components/Personalization/dom-actions/images.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js b/test/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js rename to test/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js b/test/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js rename to test/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js b/test/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js rename to test/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/move.spec.js b/test/unit/specs/components/Personalization/dom-actions/move.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/move.spec.js rename to test/unit/specs/components/Personalization/dom-actions/move.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js b/test/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js rename to test/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js b/test/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js rename to test/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/remapCustomCodeOffers.spec.js b/test/unit/specs/components/Personalization/dom-actions/remapCustomCodeOffers.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/remapCustomCodeOffers.spec.js rename to test/unit/specs/components/Personalization/dom-actions/remapCustomCodeOffers.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/remove.spec.js b/test/unit/specs/components/Personalization/dom-actions/remove.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/remove.spec.js rename to test/unit/specs/components/Personalization/dom-actions/remove.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js b/test/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js rename to test/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/resize.spec.js b/test/unit/specs/components/Personalization/dom-actions/resize.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/resize.spec.js rename to test/unit/specs/components/Personalization/dom-actions/resize.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/scripts.spec.js b/test/unit/specs/components/Personalization/dom-actions/scripts.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/scripts.spec.js rename to test/unit/specs/components/Personalization/dom-actions/scripts.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js b/test/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js rename to test/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/setHtml.spec.js b/test/unit/specs/components/Personalization/dom-actions/setHtml.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/setHtml.spec.js rename to test/unit/specs/components/Personalization/dom-actions/setHtml.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js b/test/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js rename to test/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/setStyles.spec.js b/test/unit/specs/components/Personalization/dom-actions/setStyles.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/setStyles.spec.js rename to test/unit/specs/components/Personalization/dom-actions/setStyles.spec.js diff --git a/vtest/unit/specs/components/Personalization/dom-actions/setText.spec.js b/test/unit/specs/components/Personalization/dom-actions/setText.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/dom-actions/setText.spec.js rename to test/unit/specs/components/Personalization/dom-actions/setText.spec.js diff --git a/vtest/unit/specs/components/Personalization/flicker/index.spec.js b/test/unit/specs/components/Personalization/flicker/index.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/flicker/index.spec.js rename to test/unit/specs/components/Personalization/flicker/index.spec.js diff --git a/vtest/unit/specs/components/Personalization/handlers/createDecorateProposition.spec.js b/test/unit/specs/components/Personalization/handlers/createDecorateProposition.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/handlers/createDecorateProposition.spec.js rename to test/unit/specs/components/Personalization/handlers/createDecorateProposition.spec.js diff --git a/vtest/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js b/test/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js rename to test/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js diff --git a/vtest/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js b/test/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js rename to test/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js diff --git a/vtest/unit/specs/components/Personalization/handlers/createProcessInAppMessage.spec.js b/test/unit/specs/components/Personalization/handlers/createProcessInAppMessage.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/handlers/createProcessInAppMessage.spec.js rename to test/unit/specs/components/Personalization/handlers/createProcessInAppMessage.spec.js diff --git a/vtest/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js b/test/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js rename to test/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js diff --git a/vtest/unit/specs/components/Personalization/handlers/createProcessRedirect.spec.js b/test/unit/specs/components/Personalization/handlers/createProcessRedirect.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/handlers/createProcessRedirect.spec.js rename to test/unit/specs/components/Personalization/handlers/createProcessRedirect.spec.js diff --git a/vtest/unit/specs/components/Personalization/handlers/injectCreateProposition.spec.js b/test/unit/specs/components/Personalization/handlers/injectCreateProposition.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/handlers/injectCreateProposition.spec.js rename to test/unit/specs/components/Personalization/handlers/injectCreateProposition.spec.js diff --git a/vtest/unit/specs/components/Personalization/handlers/processDefaultContent.spec.js b/test/unit/specs/components/Personalization/handlers/processDefaultContent.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/handlers/processDefaultContent.spec.js rename to test/unit/specs/components/Personalization/handlers/processDefaultContent.spec.js diff --git a/vtest/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js b/test/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js rename to test/unit/specs/components/Personalization/in-app-message-actions/actions/displayIframeContent.spec.js diff --git a/vtest/unit/specs/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.spec.js b/test/unit/specs/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.spec.js rename to test/unit/specs/components/Personalization/in-app-message-actions/initInAppMessageActionsModules.spec.js diff --git a/vtest/unit/specs/components/Personalization/in-app-message-actions/utils.spec.js b/test/unit/specs/components/Personalization/in-app-message-actions/utils.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/in-app-message-actions/utils.spec.js rename to test/unit/specs/components/Personalization/in-app-message-actions/utils.spec.js diff --git a/vtest/unit/specs/components/Personalization/responsesMock/eventResponses.js b/test/unit/specs/components/Personalization/responsesMock/eventResponses.js similarity index 100% rename from vtest/unit/specs/components/Personalization/responsesMock/eventResponses.js rename to test/unit/specs/components/Personalization/responsesMock/eventResponses.js diff --git a/vtest/unit/specs/components/Personalization/topLevel/buildAlloy.js b/test/unit/specs/components/Personalization/topLevel/buildAlloy.js similarity index 100% rename from vtest/unit/specs/components/Personalization/topLevel/buildAlloy.js rename to test/unit/specs/components/Personalization/topLevel/buildAlloy.js diff --git a/vtest/unit/specs/components/Personalization/topLevel/buildMocks.js b/test/unit/specs/components/Personalization/topLevel/buildMocks.js similarity index 100% rename from vtest/unit/specs/components/Personalization/topLevel/buildMocks.js rename to test/unit/specs/components/Personalization/topLevel/buildMocks.js diff --git a/vtest/unit/specs/components/Personalization/topLevel/cartViewDecisions.spec.js b/test/unit/specs/components/Personalization/topLevel/cartViewDecisions.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/topLevel/cartViewDecisions.spec.js rename to test/unit/specs/components/Personalization/topLevel/cartViewDecisions.spec.js diff --git a/vtest/unit/specs/components/Personalization/topLevel/mergedMetricDecisions.spec.js b/test/unit/specs/components/Personalization/topLevel/mergedMetricDecisions.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/topLevel/mergedMetricDecisions.spec.js rename to test/unit/specs/components/Personalization/topLevel/mergedMetricDecisions.spec.js diff --git a/vtest/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js b/test/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js rename to test/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js diff --git a/vtest/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js b/test/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js rename to test/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js diff --git a/vtest/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js b/test/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js rename to test/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js diff --git a/vtest/unit/specs/components/Personalization/topLevel/pageWideScopeDecisionsWithoutDomActionSchemaItems.spec.js b/test/unit/specs/components/Personalization/topLevel/pageWideScopeDecisionsWithoutDomActionSchemaItems.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/topLevel/pageWideScopeDecisionsWithoutDomActionSchemaItems.spec.js rename to test/unit/specs/components/Personalization/topLevel/pageWideScopeDecisionsWithoutDomActionSchemaItems.spec.js diff --git a/vtest/unit/specs/components/Personalization/topLevel/productsViewDecisions.spec.js b/test/unit/specs/components/Personalization/topLevel/productsViewDecisions.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/topLevel/productsViewDecisions.spec.js rename to test/unit/specs/components/Personalization/topLevel/productsViewDecisions.spec.js diff --git a/vtest/unit/specs/components/Personalization/topLevel/redirectPageWideScopeDecision.spec.js b/test/unit/specs/components/Personalization/topLevel/redirectPageWideScopeDecision.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/topLevel/redirectPageWideScopeDecision.spec.js rename to test/unit/specs/components/Personalization/topLevel/redirectPageWideScopeDecision.spec.js diff --git a/vtest/unit/specs/components/Personalization/topLevel/resetMocks.js b/test/unit/specs/components/Personalization/topLevel/resetMocks.js similarity index 100% rename from vtest/unit/specs/components/Personalization/topLevel/resetMocks.js rename to test/unit/specs/components/Personalization/topLevel/resetMocks.js diff --git a/vtest/unit/specs/components/Personalization/topLevel/scopesFoo1Foo2Decisions.spec.js b/test/unit/specs/components/Personalization/topLevel/scopesFoo1Foo2Decisions.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/topLevel/scopesFoo1Foo2Decisions.spec.js rename to test/unit/specs/components/Personalization/topLevel/scopesFoo1Foo2Decisions.spec.js diff --git a/vtest/unit/specs/components/Personalization/utils/addRenderAttemptedToDecisions.spec.js b/test/unit/specs/components/Personalization/utils/addRenderAttemptedToDecisions.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/utils/addRenderAttemptedToDecisions.spec.js rename to test/unit/specs/components/Personalization/utils/addRenderAttemptedToDecisions.spec.js diff --git a/vtest/unit/specs/components/Personalization/utils/createAsyncArray.spec.js b/test/unit/specs/components/Personalization/utils/createAsyncArray.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/utils/createAsyncArray.spec.js rename to test/unit/specs/components/Personalization/utils/createAsyncArray.spec.js diff --git a/vtest/unit/specs/components/Personalization/utils/isAuthoringModeEnabled.spec.js b/test/unit/specs/components/Personalization/utils/isAuthoringModeEnabled.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/utils/isAuthoringModeEnabled.spec.js rename to test/unit/specs/components/Personalization/utils/isAuthoringModeEnabled.spec.js diff --git a/vtest/unit/specs/components/Personalization/utils/metaUtils.spec.js b/test/unit/specs/components/Personalization/utils/metaUtils.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/utils/metaUtils.spec.js rename to test/unit/specs/components/Personalization/utils/metaUtils.spec.js diff --git a/vtest/unit/specs/components/Personalization/utils/surfaceUtils.spec.js b/test/unit/specs/components/Personalization/utils/surfaceUtils.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/utils/surfaceUtils.spec.js rename to test/unit/specs/components/Personalization/utils/surfaceUtils.spec.js diff --git a/vtest/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js b/test/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js similarity index 100% rename from vtest/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js rename to test/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.spec.js b/test/unit/specs/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.spec.js rename to test/unit/specs/components/RulesEngine/consequenceAdapters/inAppMessageConsequenceAdapter.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.spec.js b/test/unit/specs/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.spec.js rename to test/unit/specs/components/RulesEngine/consequenceAdapters/schemaTypeConsequenceAdapter.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/contextTestUtils.js b/test/unit/specs/components/RulesEngine/contextTestUtils.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/contextTestUtils.js rename to test/unit/specs/components/RulesEngine/contextTestUtils.js diff --git a/vtest/unit/specs/components/RulesEngine/createApplyResponse.spec.js b/test/unit/specs/components/RulesEngine/createApplyResponse.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/createApplyResponse.spec.js rename to test/unit/specs/components/RulesEngine/createApplyResponse.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/createConsequenceAdapter.spec.js b/test/unit/specs/components/RulesEngine/createConsequenceAdapter.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/createConsequenceAdapter.spec.js rename to test/unit/specs/components/RulesEngine/createConsequenceAdapter.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/createContextProvider.spec.js b/test/unit/specs/components/RulesEngine/createContextProvider.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/createContextProvider.spec.js rename to test/unit/specs/components/RulesEngine/createContextProvider.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/createDecisionHistory.spec.js b/test/unit/specs/components/RulesEngine/createDecisionHistory.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/createDecisionHistory.spec.js rename to test/unit/specs/components/RulesEngine/createDecisionHistory.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/createDecisionProvider.spec.js b/test/unit/specs/components/RulesEngine/createDecisionProvider.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/createDecisionProvider.spec.js rename to test/unit/specs/components/RulesEngine/createDecisionProvider.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/createEvaluableRulesetPayload.spec.js b/test/unit/specs/components/RulesEngine/createEvaluableRulesetPayload.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/createEvaluableRulesetPayload.spec.js rename to test/unit/specs/components/RulesEngine/createEvaluableRulesetPayload.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/createEvaluateRulesetsCommand.spec.js b/test/unit/specs/components/RulesEngine/createEvaluateRulesetsCommand.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/createEvaluateRulesetsCommand.spec.js rename to test/unit/specs/components/RulesEngine/createEvaluateRulesetsCommand.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/createEventRegistry.spec.js b/test/unit/specs/components/RulesEngine/createEventRegistry.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/createEventRegistry.spec.js rename to test/unit/specs/components/RulesEngine/createEventRegistry.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/createOnResponseHandler.spec.js b/test/unit/specs/components/RulesEngine/createOnResponseHandler.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/createOnResponseHandler.spec.js rename to test/unit/specs/components/RulesEngine/createOnResponseHandler.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/createSubscribeRulesetItems.spec.js b/test/unit/specs/components/RulesEngine/createSubscribeRulesetItems.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/createSubscribeRulesetItems.spec.js rename to test/unit/specs/components/RulesEngine/createSubscribeRulesetItems.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/decisioningContext.browser.spec.js b/test/unit/specs/components/RulesEngine/decisioningContext.browser.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/decisioningContext.browser.spec.js rename to test/unit/specs/components/RulesEngine/decisioningContext.browser.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/decisioningContext.page.spec.js b/test/unit/specs/components/RulesEngine/decisioningContext.page.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/decisioningContext.page.spec.js rename to test/unit/specs/components/RulesEngine/decisioningContext.page.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/decisioningContext.referringPage.spec.js b/test/unit/specs/components/RulesEngine/decisioningContext.referringPage.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/decisioningContext.referringPage.spec.js rename to test/unit/specs/components/RulesEngine/decisioningContext.referringPage.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/decisioningContext.sdkVersion.spec.js b/test/unit/specs/components/RulesEngine/decisioningContext.sdkVersion.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/decisioningContext.sdkVersion.spec.js rename to test/unit/specs/components/RulesEngine/decisioningContext.sdkVersion.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/decisioningContext.timestamp.spec.js b/test/unit/specs/components/RulesEngine/decisioningContext.timestamp.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/decisioningContext.timestamp.spec.js rename to test/unit/specs/components/RulesEngine/decisioningContext.timestamp.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/decisioningContext.window.spec.js b/test/unit/specs/components/RulesEngine/decisioningContext.window.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/decisioningContext.window.spec.js rename to test/unit/specs/components/RulesEngine/decisioningContext.window.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/index.spec.js b/test/unit/specs/components/RulesEngine/index.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/index.spec.js rename to test/unit/specs/components/RulesEngine/index.spec.js diff --git a/vtest/unit/specs/components/RulesEngine/utils.spec.js b/test/unit/specs/components/RulesEngine/utils.spec.js similarity index 100% rename from vtest/unit/specs/components/RulesEngine/utils.spec.js rename to test/unit/specs/components/RulesEngine/utils.spec.js diff --git a/vtest/unit/specs/components/StreamingMedia/configValidators.spec.js b/test/unit/specs/components/StreamingMedia/configValidators.spec.js similarity index 100% rename from vtest/unit/specs/components/StreamingMedia/configValidators.spec.js rename to test/unit/specs/components/StreamingMedia/configValidators.spec.js diff --git a/vtest/unit/specs/components/StreamingMedia/createMediaEventManager.spec.js b/test/unit/specs/components/StreamingMedia/createMediaEventManager.spec.js similarity index 100% rename from vtest/unit/specs/components/StreamingMedia/createMediaEventManager.spec.js rename to test/unit/specs/components/StreamingMedia/createMediaEventManager.spec.js diff --git a/vtest/unit/specs/components/StreamingMedia/createMediaRequest.spec.js b/test/unit/specs/components/StreamingMedia/createMediaRequest.spec.js similarity index 100% rename from vtest/unit/specs/components/StreamingMedia/createMediaRequest.spec.js rename to test/unit/specs/components/StreamingMedia/createMediaRequest.spec.js diff --git a/vtest/unit/specs/components/StreamingMedia/createMediaResponseHandler.spec.js b/test/unit/specs/components/StreamingMedia/createMediaResponseHandler.spec.js similarity index 100% rename from vtest/unit/specs/components/StreamingMedia/createMediaResponseHandler.spec.js rename to test/unit/specs/components/StreamingMedia/createMediaResponseHandler.spec.js diff --git a/vtest/unit/specs/components/StreamingMedia/createMediaSessionCacheManager.spec.js b/test/unit/specs/components/StreamingMedia/createMediaSessionCacheManager.spec.js similarity index 100% rename from vtest/unit/specs/components/StreamingMedia/createMediaSessionCacheManager.spec.js rename to test/unit/specs/components/StreamingMedia/createMediaSessionCacheManager.spec.js diff --git a/vtest/unit/specs/components/StreamingMedia/createStreamingMediaComponent.spec.js b/test/unit/specs/components/StreamingMedia/createStreamingMediaComponent.spec.js similarity index 100% rename from vtest/unit/specs/components/StreamingMedia/createStreamingMediaComponent.spec.js rename to test/unit/specs/components/StreamingMedia/createStreamingMediaComponent.spec.js diff --git a/vtest/unit/specs/components/StreamingMedia/createTrackMediaEvent.spec.js b/test/unit/specs/components/StreamingMedia/createTrackMediaEvent.spec.js similarity index 100% rename from vtest/unit/specs/components/StreamingMedia/createTrackMediaEvent.spec.js rename to test/unit/specs/components/StreamingMedia/createTrackMediaEvent.spec.js diff --git a/vtest/unit/specs/components/StreamingMedia/createTrackMediaSession.spec.js b/test/unit/specs/components/StreamingMedia/createTrackMediaSession.spec.js similarity index 100% rename from vtest/unit/specs/components/StreamingMedia/createTrackMediaSession.spec.js rename to test/unit/specs/components/StreamingMedia/createTrackMediaSession.spec.js diff --git a/vtest/unit/specs/components/StreamingMedia/validateMediaEventOptions.spec.js b/test/unit/specs/components/StreamingMedia/validateMediaEventOptions.spec.js similarity index 100% rename from vtest/unit/specs/components/StreamingMedia/validateMediaEventOptions.spec.js rename to test/unit/specs/components/StreamingMedia/validateMediaEventOptions.spec.js diff --git a/vtest/unit/specs/components/StreamingMedia/validateMediaSessionOptions.spec.js b/test/unit/specs/components/StreamingMedia/validateMediaSessionOptions.spec.js similarity index 100% rename from vtest/unit/specs/components/StreamingMedia/validateMediaSessionOptions.spec.js rename to test/unit/specs/components/StreamingMedia/validateMediaSessionOptions.spec.js diff --git a/vtest/unit/specs/core/buildAndValidateConfig.spec.js b/test/unit/specs/core/buildAndValidateConfig.spec.js similarity index 100% rename from vtest/unit/specs/core/buildAndValidateConfig.spec.js rename to test/unit/specs/core/buildAndValidateConfig.spec.js diff --git a/vtest/unit/specs/core/componentCreators.spec.js b/test/unit/specs/core/componentCreators.spec.js similarity index 100% rename from vtest/unit/specs/core/componentCreators.spec.js rename to test/unit/specs/core/componentCreators.spec.js diff --git a/vtest/unit/specs/core/config/createConfig.spec.js b/test/unit/specs/core/config/createConfig.spec.js similarity index 100% rename from vtest/unit/specs/core/config/createConfig.spec.js rename to test/unit/specs/core/config/createConfig.spec.js diff --git a/vtest/unit/specs/core/config/createCoreConfigs.spec.js b/test/unit/specs/core/config/createCoreConfigs.spec.js similarity index 100% rename from vtest/unit/specs/core/config/createCoreConfigs.spec.js rename to test/unit/specs/core/config/createCoreConfigs.spec.js diff --git a/vtest/unit/specs/core/consent/createConsent.spec.js b/test/unit/specs/core/consent/createConsent.spec.js similarity index 100% rename from vtest/unit/specs/core/consent/createConsent.spec.js rename to test/unit/specs/core/consent/createConsent.spec.js diff --git a/vtest/unit/specs/core/consent/createConsentStateMachine.spec.js b/test/unit/specs/core/consent/createConsentStateMachine.spec.js similarity index 100% rename from vtest/unit/specs/core/consent/createConsentStateMachine.spec.js rename to test/unit/specs/core/consent/createConsentStateMachine.spec.js diff --git a/vtest/unit/specs/core/createComponentRegistry.spec.js b/test/unit/specs/core/createComponentRegistry.spec.js similarity index 100% rename from vtest/unit/specs/core/createComponentRegistry.spec.js rename to test/unit/specs/core/createComponentRegistry.spec.js diff --git a/vtest/unit/specs/core/createCookieTransfer.spec.js b/test/unit/specs/core/createCookieTransfer.spec.js similarity index 100% rename from vtest/unit/specs/core/createCookieTransfer.spec.js rename to test/unit/specs/core/createCookieTransfer.spec.js diff --git a/vtest/unit/specs/core/createEvent.spec.js b/test/unit/specs/core/createEvent.spec.js similarity index 100% rename from vtest/unit/specs/core/createEvent.spec.js rename to test/unit/specs/core/createEvent.spec.js diff --git a/vtest/unit/specs/core/createEventManager.spec.js b/test/unit/specs/core/createEventManager.spec.js similarity index 100% rename from vtest/unit/specs/core/createEventManager.spec.js rename to test/unit/specs/core/createEventManager.spec.js diff --git a/vtest/unit/specs/core/createInstanceFunction.spec.js b/test/unit/specs/core/createInstanceFunction.spec.js similarity index 100% rename from vtest/unit/specs/core/createInstanceFunction.spec.js rename to test/unit/specs/core/createInstanceFunction.spec.js diff --git a/vtest/unit/specs/core/createLifecycle.spec.js b/test/unit/specs/core/createLifecycle.spec.js similarity index 100% rename from vtest/unit/specs/core/createLifecycle.spec.js rename to test/unit/specs/core/createLifecycle.spec.js diff --git a/vtest/unit/specs/core/createLogController.spec.js b/test/unit/specs/core/createLogController.spec.js similarity index 100% rename from vtest/unit/specs/core/createLogController.spec.js rename to test/unit/specs/core/createLogController.spec.js diff --git a/vtest/unit/specs/core/createLogger.spec.js b/test/unit/specs/core/createLogger.spec.js similarity index 100% rename from vtest/unit/specs/core/createLogger.spec.js rename to test/unit/specs/core/createLogger.spec.js diff --git a/vtest/unit/specs/core/edgeNetwork/handleRequestFailure.spec.js b/test/unit/specs/core/edgeNetwork/handleRequestFailure.spec.js similarity index 100% rename from vtest/unit/specs/core/edgeNetwork/handleRequestFailure.spec.js rename to test/unit/specs/core/edgeNetwork/handleRequestFailure.spec.js diff --git a/vtest/unit/specs/core/edgeNetwork/injectApplyResponse.spec.js b/test/unit/specs/core/edgeNetwork/injectApplyResponse.spec.js similarity index 100% rename from vtest/unit/specs/core/edgeNetwork/injectApplyResponse.spec.js rename to test/unit/specs/core/edgeNetwork/injectApplyResponse.spec.js diff --git a/vtest/unit/specs/core/edgeNetwork/injectExtractEdgeInfo.spec.js b/test/unit/specs/core/edgeNetwork/injectExtractEdgeInfo.spec.js similarity index 100% rename from vtest/unit/specs/core/edgeNetwork/injectExtractEdgeInfo.spec.js rename to test/unit/specs/core/edgeNetwork/injectExtractEdgeInfo.spec.js diff --git a/vtest/unit/specs/core/edgeNetwork/injectGetLocationHint.spec.js b/test/unit/specs/core/edgeNetwork/injectGetLocationHint.spec.js similarity index 100% rename from vtest/unit/specs/core/edgeNetwork/injectGetLocationHint.spec.js rename to test/unit/specs/core/edgeNetwork/injectGetLocationHint.spec.js diff --git a/vtest/unit/specs/core/edgeNetwork/injectProcessWarningsAndErrors.spec.js b/test/unit/specs/core/edgeNetwork/injectProcessWarningsAndErrors.spec.js similarity index 100% rename from vtest/unit/specs/core/edgeNetwork/injectProcessWarningsAndErrors.spec.js rename to test/unit/specs/core/edgeNetwork/injectProcessWarningsAndErrors.spec.js diff --git a/vtest/unit/specs/core/edgeNetwork/injectSendEdgeNetworkRequest.spec.js b/test/unit/specs/core/edgeNetwork/injectSendEdgeNetworkRequest.spec.js similarity index 100% rename from vtest/unit/specs/core/edgeNetwork/injectSendEdgeNetworkRequest.spec.js rename to test/unit/specs/core/edgeNetwork/injectSendEdgeNetworkRequest.spec.js diff --git a/vtest/unit/specs/core/edgeNetwork/mergeLifecycleResponses.spec.js b/test/unit/specs/core/edgeNetwork/mergeLifecycleResponses.spec.js similarity index 100% rename from vtest/unit/specs/core/edgeNetwork/mergeLifecycleResponses.spec.js rename to test/unit/specs/core/edgeNetwork/mergeLifecycleResponses.spec.js diff --git a/vtest/unit/specs/core/initializeComponents.spec.js b/test/unit/specs/core/initializeComponents.spec.js similarity index 100% rename from vtest/unit/specs/core/initializeComponents.spec.js rename to test/unit/specs/core/initializeComponents.spec.js diff --git a/vtest/unit/specs/core/injectCreateResponse.spec.js b/test/unit/specs/core/injectCreateResponse.spec.js similarity index 100% rename from vtest/unit/specs/core/injectCreateResponse.spec.js rename to test/unit/specs/core/injectCreateResponse.spec.js diff --git a/vtest/unit/specs/core/injectExecuteCommand.spec.js b/test/unit/specs/core/injectExecuteCommand.spec.js similarity index 100% rename from vtest/unit/specs/core/injectExecuteCommand.spec.js rename to test/unit/specs/core/injectExecuteCommand.spec.js diff --git a/vtest/unit/specs/core/injectHandleError.spec.js b/test/unit/specs/core/injectHandleError.spec.js similarity index 100% rename from vtest/unit/specs/core/injectHandleError.spec.js rename to test/unit/specs/core/injectHandleError.spec.js diff --git a/vtest/unit/specs/core/injectShouldTransferCookie.spec.js b/test/unit/specs/core/injectShouldTransferCookie.spec.js similarity index 100% rename from vtest/unit/specs/core/injectShouldTransferCookie.spec.js rename to test/unit/specs/core/injectShouldTransferCookie.spec.js diff --git a/vtest/unit/specs/core/network/getRequestRetryDelay.spec.js b/test/unit/specs/core/network/getRequestRetryDelay.spec.js similarity index 100% rename from vtest/unit/specs/core/network/getRequestRetryDelay.spec.js rename to test/unit/specs/core/network/getRequestRetryDelay.spec.js diff --git a/vtest/unit/specs/core/network/injectSendNetworkRequest.spec.js b/test/unit/specs/core/network/injectSendNetworkRequest.spec.js similarity index 100% rename from vtest/unit/specs/core/network/injectSendNetworkRequest.spec.js rename to test/unit/specs/core/network/injectSendNetworkRequest.spec.js diff --git a/vtest/unit/specs/core/network/isRequestRetryable.spec.js b/test/unit/specs/core/network/isRequestRetryable.spec.js similarity index 100% rename from vtest/unit/specs/core/network/isRequestRetryable.spec.js rename to test/unit/specs/core/network/isRequestRetryable.spec.js diff --git a/vtest/unit/specs/core/network/requestMethods/injectSendBeaconRequest.spec.js b/test/unit/specs/core/network/requestMethods/injectSendBeaconRequest.spec.js similarity index 100% rename from vtest/unit/specs/core/network/requestMethods/injectSendBeaconRequest.spec.js rename to test/unit/specs/core/network/requestMethods/injectSendBeaconRequest.spec.js diff --git a/vtest/unit/specs/core/network/requestMethods/injectSendFetchRequest.spec.js b/test/unit/specs/core/network/requestMethods/injectSendFetchRequest.spec.js similarity index 100% rename from vtest/unit/specs/core/network/requestMethods/injectSendFetchRequest.spec.js rename to test/unit/specs/core/network/requestMethods/injectSendFetchRequest.spec.js diff --git a/vtest/unit/specs/core/requiredComponentCreators.spec.js b/test/unit/specs/core/requiredComponentCreators.spec.js similarity index 100% rename from vtest/unit/specs/core/requiredComponentCreators.spec.js rename to test/unit/specs/core/requiredComponentCreators.spec.js diff --git a/vtest/unit/specs/core/validateCommandOptions.spec.js b/test/unit/specs/core/validateCommandOptions.spec.js similarity index 100% rename from vtest/unit/specs/core/validateCommandOptions.spec.js rename to test/unit/specs/core/validateCommandOptions.spec.js diff --git a/vtest/unit/specs/utils/assignConcatArrayValues.spec.js b/test/unit/specs/utils/assignConcatArrayValues.spec.js similarity index 100% rename from vtest/unit/specs/utils/assignConcatArrayValues.spec.js rename to test/unit/specs/utils/assignConcatArrayValues.spec.js diff --git a/vtest/unit/specs/utils/clone.spec.js b/test/unit/specs/utils/clone.spec.js similarity index 100% rename from vtest/unit/specs/utils/clone.spec.js rename to test/unit/specs/utils/clone.spec.js diff --git a/vtest/unit/specs/utils/crc32.spec.js b/test/unit/specs/utils/crc32.spec.js similarity index 100% rename from vtest/unit/specs/utils/crc32.spec.js rename to test/unit/specs/utils/crc32.spec.js diff --git a/vtest/unit/specs/utils/createCallbackAggregator.spec.js b/test/unit/specs/utils/createCallbackAggregator.spec.js similarity index 100% rename from vtest/unit/specs/utils/createCallbackAggregator.spec.js rename to test/unit/specs/utils/createCallbackAggregator.spec.js diff --git a/vtest/unit/specs/utils/createCollect.spec.js b/test/unit/specs/utils/createCollect.spec.js similarity index 100% rename from vtest/unit/specs/utils/createCollect.spec.js rename to test/unit/specs/utils/createCollect.spec.js diff --git a/vtest/unit/specs/utils/createLoggingCookieJar.spec.js b/test/unit/specs/utils/createLoggingCookieJar.spec.js similarity index 100% rename from vtest/unit/specs/utils/createLoggingCookieJar.spec.js rename to test/unit/specs/utils/createLoggingCookieJar.spec.js diff --git a/vtest/unit/specs/utils/createMerger.spec.js b/test/unit/specs/utils/createMerger.spec.js similarity index 100% rename from vtest/unit/specs/utils/createMerger.spec.js rename to test/unit/specs/utils/createMerger.spec.js diff --git a/vtest/unit/specs/utils/createSubscription.spec.js b/test/unit/specs/utils/createSubscription.spec.js similarity index 100% rename from vtest/unit/specs/utils/createSubscription.spec.js rename to test/unit/specs/utils/createSubscription.spec.js diff --git a/vtest/unit/specs/utils/createTaskQueue.spec.js b/test/unit/specs/utils/createTaskQueue.spec.js similarity index 100% rename from vtest/unit/specs/utils/createTaskQueue.spec.js rename to test/unit/specs/utils/createTaskQueue.spec.js diff --git a/vtest/unit/specs/utils/decodeUriComponentSafely.spec.js b/test/unit/specs/utils/decodeUriComponentSafely.spec.js similarity index 100% rename from vtest/unit/specs/utils/decodeUriComponentSafely.spec.js rename to test/unit/specs/utils/decodeUriComponentSafely.spec.js diff --git a/vtest/unit/specs/utils/deduplicateArray.spec.js b/test/unit/specs/utils/deduplicateArray.spec.js similarity index 100% rename from vtest/unit/specs/utils/deduplicateArray.spec.js rename to test/unit/specs/utils/deduplicateArray.spec.js diff --git a/vtest/unit/specs/utils/deepAssign.spec.js b/test/unit/specs/utils/deepAssign.spec.js similarity index 100% rename from vtest/unit/specs/utils/deepAssign.spec.js rename to test/unit/specs/utils/deepAssign.spec.js diff --git a/vtest/unit/specs/utils/defer.spec.js b/test/unit/specs/utils/defer.spec.js similarity index 100% rename from vtest/unit/specs/utils/defer.spec.js rename to test/unit/specs/utils/defer.spec.js diff --git a/vtest/unit/specs/utils/dom/appendNode.spec.js b/test/unit/specs/utils/dom/appendNode.spec.js similarity index 100% rename from vtest/unit/specs/utils/dom/appendNode.spec.js rename to test/unit/specs/utils/dom/appendNode.spec.js diff --git a/test/unit/specs/utils/dom/awaitSelector.spec.js b/test/unit/specs/utils/dom/awaitSelector.spec.js index 48133a028..657b941ee 100644 --- a/test/unit/specs/utils/dom/awaitSelector.spec.js +++ b/test/unit/specs/utils/dom/awaitSelector.spec.js @@ -10,6 +10,7 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ +import { afterAll, describe, it, expect } from "vitest"; import awaitSelector from "../../../../../src/utils/dom/awaitSelector.js"; import selectNodes from "../../../../../src/utils/dom/selectNodes.js"; import { diff --git a/vtest/unit/specs/utils/dom/createNode.spec.js b/test/unit/specs/utils/dom/createNode.spec.js similarity index 100% rename from vtest/unit/specs/utils/dom/createNode.spec.js rename to test/unit/specs/utils/dom/createNode.spec.js diff --git a/vtest/unit/specs/utils/dom/isShadowSelector.spec.js b/test/unit/specs/utils/dom/isShadowSelector.spec.js similarity index 100% rename from vtest/unit/specs/utils/dom/isShadowSelector.spec.js rename to test/unit/specs/utils/dom/isShadowSelector.spec.js diff --git a/vtest/unit/specs/utils/dom/matchesSelector.spec.js b/test/unit/specs/utils/dom/matchesSelector.spec.js similarity index 100% rename from vtest/unit/specs/utils/dom/matchesSelector.spec.js rename to test/unit/specs/utils/dom/matchesSelector.spec.js diff --git a/vtest/unit/specs/utils/dom/querySelectorAll.spec.js b/test/unit/specs/utils/dom/querySelectorAll.spec.js similarity index 100% rename from vtest/unit/specs/utils/dom/querySelectorAll.spec.js rename to test/unit/specs/utils/dom/querySelectorAll.spec.js diff --git a/vtest/unit/specs/utils/dom/removeNode.spec.js b/test/unit/specs/utils/dom/removeNode.spec.js similarity index 100% rename from vtest/unit/specs/utils/dom/removeNode.spec.js rename to test/unit/specs/utils/dom/removeNode.spec.js diff --git a/vtest/unit/specs/utils/dom/selectNodes.spec.js b/test/unit/specs/utils/dom/selectNodes.spec.js similarity index 100% rename from vtest/unit/specs/utils/dom/selectNodes.spec.js rename to test/unit/specs/utils/dom/selectNodes.spec.js diff --git a/vtest/unit/specs/utils/dom/selectNodesWithShadow.spec.js b/test/unit/specs/utils/dom/selectNodesWithShadow.spec.js similarity index 100% rename from vtest/unit/specs/utils/dom/selectNodesWithShadow.spec.js rename to test/unit/specs/utils/dom/selectNodesWithShadow.spec.js diff --git a/vtest/unit/specs/utils/event.spec.js b/test/unit/specs/utils/event.spec.js similarity index 100% rename from vtest/unit/specs/utils/event.spec.js rename to test/unit/specs/utils/event.spec.js diff --git a/vtest/unit/specs/utils/filterObject.spec.js b/test/unit/specs/utils/filterObject.spec.js similarity index 100% rename from vtest/unit/specs/utils/filterObject.spec.js rename to test/unit/specs/utils/filterObject.spec.js diff --git a/vtest/unit/specs/utils/flattenArray.spec.js b/test/unit/specs/utils/flattenArray.spec.js similarity index 100% rename from vtest/unit/specs/utils/flattenArray.spec.js rename to test/unit/specs/utils/flattenArray.spec.js diff --git a/vtest/unit/specs/utils/flattenObject.spec.js b/test/unit/specs/utils/flattenObject.spec.js similarity index 100% rename from vtest/unit/specs/utils/flattenObject.spec.js rename to test/unit/specs/utils/flattenObject.spec.js diff --git a/vtest/unit/specs/utils/getApexDomain.spec.js b/test/unit/specs/utils/getApexDomain.spec.js similarity index 100% rename from vtest/unit/specs/utils/getApexDomain.spec.js rename to test/unit/specs/utils/getApexDomain.spec.js diff --git a/vtest/unit/specs/utils/getLastArrayItems.spec.js b/test/unit/specs/utils/getLastArrayItems.spec.js similarity index 100% rename from vtest/unit/specs/utils/getLastArrayItems.spec.js rename to test/unit/specs/utils/getLastArrayItems.spec.js diff --git a/vtest/unit/specs/utils/getNamespacedCookieName.spec.js b/test/unit/specs/utils/getNamespacedCookieName.spec.js similarity index 100% rename from vtest/unit/specs/utils/getNamespacedCookieName.spec.js rename to test/unit/specs/utils/getNamespacedCookieName.spec.js diff --git a/vtest/unit/specs/utils/getNamespacedStorage.spec.js b/test/unit/specs/utils/getNamespacedStorage.spec.js similarity index 100% rename from vtest/unit/specs/utils/getNamespacedStorage.spec.js rename to test/unit/specs/utils/getNamespacedStorage.spec.js diff --git a/vtest/unit/specs/utils/groupBy.spec.js b/test/unit/specs/utils/groupBy.spec.js similarity index 100% rename from vtest/unit/specs/utils/groupBy.spec.js rename to test/unit/specs/utils/groupBy.spec.js diff --git a/vtest/unit/specs/utils/injectAreThirdPartyCookiesSupportedByDefault.spec.js b/test/unit/specs/utils/injectAreThirdPartyCookiesSupportedByDefault.spec.js similarity index 100% rename from vtest/unit/specs/utils/injectAreThirdPartyCookiesSupportedByDefault.spec.js rename to test/unit/specs/utils/injectAreThirdPartyCookiesSupportedByDefault.spec.js diff --git a/vtest/unit/specs/utils/injectDoesIdentityCookieExist.spec.js b/test/unit/specs/utils/injectDoesIdentityCookieExist.spec.js similarity index 100% rename from vtest/unit/specs/utils/injectDoesIdentityCookieExist.spec.js rename to test/unit/specs/utils/injectDoesIdentityCookieExist.spec.js diff --git a/vtest/unit/specs/utils/injectFireReferrerHideableImage.spec.js b/test/unit/specs/utils/injectFireReferrerHideableImage.spec.js similarity index 100% rename from vtest/unit/specs/utils/injectFireReferrerHideableImage.spec.js rename to test/unit/specs/utils/injectFireReferrerHideableImage.spec.js diff --git a/vtest/unit/specs/utils/injectGetBrowser.spec.js b/test/unit/specs/utils/injectGetBrowser.spec.js similarity index 100% rename from vtest/unit/specs/utils/injectGetBrowser.spec.js rename to test/unit/specs/utils/injectGetBrowser.spec.js diff --git a/vtest/unit/specs/utils/injectStorage.spec.js b/test/unit/specs/utils/injectStorage.spec.js similarity index 100% rename from vtest/unit/specs/utils/injectStorage.spec.js rename to test/unit/specs/utils/injectStorage.spec.js diff --git a/vtest/unit/specs/utils/intersection.spec.js b/test/unit/specs/utils/intersection.spec.js similarity index 100% rename from vtest/unit/specs/utils/intersection.spec.js rename to test/unit/specs/utils/intersection.spec.js diff --git a/vtest/unit/specs/utils/isBlankString.spec.js b/test/unit/specs/utils/isBlankString.spec.js similarity index 100% rename from vtest/unit/specs/utils/isBlankString.spec.js rename to test/unit/specs/utils/isBlankString.spec.js diff --git a/vtest/unit/specs/utils/isBoolean.spec.js b/test/unit/specs/utils/isBoolean.spec.js similarity index 100% rename from vtest/unit/specs/utils/isBoolean.spec.js rename to test/unit/specs/utils/isBoolean.spec.js diff --git a/vtest/unit/specs/utils/isEmptyObject.spec.js b/test/unit/specs/utils/isEmptyObject.spec.js similarity index 100% rename from vtest/unit/specs/utils/isEmptyObject.spec.js rename to test/unit/specs/utils/isEmptyObject.spec.js diff --git a/vtest/unit/specs/utils/isFunction.spec.js b/test/unit/specs/utils/isFunction.spec.js similarity index 100% rename from vtest/unit/specs/utils/isFunction.spec.js rename to test/unit/specs/utils/isFunction.spec.js diff --git a/vtest/unit/specs/utils/isInteger.spec.js b/test/unit/specs/utils/isInteger.spec.js similarity index 100% rename from vtest/unit/specs/utils/isInteger.spec.js rename to test/unit/specs/utils/isInteger.spec.js diff --git a/vtest/unit/specs/utils/isNamespacedCookieName.spec.js b/test/unit/specs/utils/isNamespacedCookieName.spec.js similarity index 100% rename from vtest/unit/specs/utils/isNamespacedCookieName.spec.js rename to test/unit/specs/utils/isNamespacedCookieName.spec.js diff --git a/vtest/unit/specs/utils/isNil.spec.js b/test/unit/specs/utils/isNil.spec.js similarity index 100% rename from vtest/unit/specs/utils/isNil.spec.js rename to test/unit/specs/utils/isNil.spec.js diff --git a/vtest/unit/specs/utils/isNonEmptyArray.spec.js b/test/unit/specs/utils/isNonEmptyArray.spec.js similarity index 100% rename from vtest/unit/specs/utils/isNonEmptyArray.spec.js rename to test/unit/specs/utils/isNonEmptyArray.spec.js diff --git a/vtest/unit/specs/utils/isNonEmptyString.spec.js b/test/unit/specs/utils/isNonEmptyString.spec.js similarity index 100% rename from vtest/unit/specs/utils/isNonEmptyString.spec.js rename to test/unit/specs/utils/isNonEmptyString.spec.js diff --git a/vtest/unit/specs/utils/isNumber.spec.js b/test/unit/specs/utils/isNumber.spec.js similarity index 100% rename from vtest/unit/specs/utils/isNumber.spec.js rename to test/unit/specs/utils/isNumber.spec.js diff --git a/vtest/unit/specs/utils/isObject.spec.js b/test/unit/specs/utils/isObject.spec.js similarity index 100% rename from vtest/unit/specs/utils/isObject.spec.js rename to test/unit/specs/utils/isObject.spec.js diff --git a/vtest/unit/specs/utils/isString.spec.js b/test/unit/specs/utils/isString.spec.js similarity index 100% rename from vtest/unit/specs/utils/isString.spec.js rename to test/unit/specs/utils/isString.spec.js diff --git a/vtest/unit/specs/utils/isUnique.spec.js b/test/unit/specs/utils/isUnique.spec.js similarity index 100% rename from vtest/unit/specs/utils/isUnique.spec.js rename to test/unit/specs/utils/isUnique.spec.js diff --git a/vtest/unit/specs/utils/isValidRegExp.spec.js b/test/unit/specs/utils/isValidRegExp.spec.js similarity index 100% rename from vtest/unit/specs/utils/isValidRegExp.spec.js rename to test/unit/specs/utils/isValidRegExp.spec.js diff --git a/vtest/unit/specs/utils/lazy.spec.js b/test/unit/specs/utils/lazy.spec.js similarity index 100% rename from vtest/unit/specs/utils/lazy.spec.js rename to test/unit/specs/utils/lazy.spec.js diff --git a/vtest/unit/specs/utils/networkErrors.spec.js b/test/unit/specs/utils/networkErrors.spec.js similarity index 100% rename from vtest/unit/specs/utils/networkErrors.spec.js rename to test/unit/specs/utils/networkErrors.spec.js diff --git a/vtest/unit/specs/utils/noop.spec.js b/test/unit/specs/utils/noop.spec.js similarity index 100% rename from vtest/unit/specs/utils/noop.spec.js rename to test/unit/specs/utils/noop.spec.js diff --git a/vtest/unit/specs/utils/parseUrl.spec.js b/test/unit/specs/utils/parseUrl.spec.js similarity index 100% rename from vtest/unit/specs/utils/parseUrl.spec.js rename to test/unit/specs/utils/parseUrl.spec.js diff --git a/vtest/unit/specs/utils/prepareConfigOverridesForEdge.spec.js b/test/unit/specs/utils/prepareConfigOverridesForEdge.spec.js similarity index 100% rename from vtest/unit/specs/utils/prepareConfigOverridesForEdge.spec.js rename to test/unit/specs/utils/prepareConfigOverridesForEdge.spec.js diff --git a/vtest/unit/specs/utils/request/createAddIdentity.spec.js b/test/unit/specs/utils/request/createAddIdentity.spec.js similarity index 100% rename from vtest/unit/specs/utils/request/createAddIdentity.spec.js rename to test/unit/specs/utils/request/createAddIdentity.spec.js diff --git a/vtest/unit/specs/utils/request/createDataCollectionRequest.spec.js b/test/unit/specs/utils/request/createDataCollectionRequest.spec.js similarity index 100% rename from vtest/unit/specs/utils/request/createDataCollectionRequest.spec.js rename to test/unit/specs/utils/request/createDataCollectionRequest.spec.js diff --git a/vtest/unit/specs/utils/request/createDataCollectionRequestPayload.spec.js b/test/unit/specs/utils/request/createDataCollectionRequestPayload.spec.js similarity index 100% rename from vtest/unit/specs/utils/request/createDataCollectionRequestPayload.spec.js rename to test/unit/specs/utils/request/createDataCollectionRequestPayload.spec.js diff --git a/vtest/unit/specs/utils/request/createGetAssuranceValidationTokenParams.spec.js b/test/unit/specs/utils/request/createGetAssuranceValidationTokenParams.spec.js similarity index 100% rename from vtest/unit/specs/utils/request/createGetAssuranceValidationTokenParams.spec.js rename to test/unit/specs/utils/request/createGetAssuranceValidationTokenParams.spec.js diff --git a/vtest/unit/specs/utils/request/createHasIdentity.spec.js b/test/unit/specs/utils/request/createHasIdentity.spec.js similarity index 100% rename from vtest/unit/specs/utils/request/createHasIdentity.spec.js rename to test/unit/specs/utils/request/createHasIdentity.spec.js diff --git a/vtest/unit/specs/utils/request/createRequestParams.spec.js b/test/unit/specs/utils/request/createRequestParams.spec.js similarity index 100% rename from vtest/unit/specs/utils/request/createRequestParams.spec.js rename to test/unit/specs/utils/request/createRequestParams.spec.js diff --git a/vtest/unit/specs/utils/sanitizeOrgIdForCookieName.spec.js b/test/unit/specs/utils/sanitizeOrgIdForCookieName.spec.js similarity index 100% rename from vtest/unit/specs/utils/sanitizeOrgIdForCookieName.spec.js rename to test/unit/specs/utils/sanitizeOrgIdForCookieName.spec.js diff --git a/vtest/unit/specs/utils/stackError.spec.js b/test/unit/specs/utils/stackError.spec.js similarity index 100% rename from vtest/unit/specs/utils/stackError.spec.js rename to test/unit/specs/utils/stackError.spec.js diff --git a/vtest/unit/specs/utils/stringToBoolean.spec.js b/test/unit/specs/utils/stringToBoolean.spec.js similarity index 100% rename from vtest/unit/specs/utils/stringToBoolean.spec.js rename to test/unit/specs/utils/stringToBoolean.spec.js diff --git a/vtest/unit/specs/utils/toArray.spec.js b/test/unit/specs/utils/toArray.spec.js similarity index 100% rename from vtest/unit/specs/utils/toArray.spec.js rename to test/unit/specs/utils/toArray.spec.js diff --git a/vtest/unit/specs/utils/toError.spec.js b/test/unit/specs/utils/toError.spec.js similarity index 100% rename from vtest/unit/specs/utils/toError.spec.js rename to test/unit/specs/utils/toError.spec.js diff --git a/vtest/unit/specs/utils/toISOStringLocal.spec.js b/test/unit/specs/utils/toISOStringLocal.spec.js similarity index 100% rename from vtest/unit/specs/utils/toISOStringLocal.spec.js rename to test/unit/specs/utils/toISOStringLocal.spec.js diff --git a/vtest/unit/specs/utils/toInteger.spec.js b/test/unit/specs/utils/toInteger.spec.js similarity index 100% rename from vtest/unit/specs/utils/toInteger.spec.js rename to test/unit/specs/utils/toInteger.spec.js diff --git a/vtest/unit/specs/utils/updateErrorMessage.spec.js b/test/unit/specs/utils/updateErrorMessage.spec.js similarity index 100% rename from vtest/unit/specs/utils/updateErrorMessage.spec.js rename to test/unit/specs/utils/updateErrorMessage.spec.js diff --git a/vtest/unit/specs/utils/validateConfigOverride.spec.js b/test/unit/specs/utils/validateConfigOverride.spec.js similarity index 100% rename from vtest/unit/specs/utils/validateConfigOverride.spec.js rename to test/unit/specs/utils/validateConfigOverride.spec.js diff --git a/vtest/unit/specs/utils/validateIdentityMap.spec.js b/test/unit/specs/utils/validateIdentityMap.spec.js similarity index 100% rename from vtest/unit/specs/utils/validateIdentityMap.spec.js rename to test/unit/specs/utils/validateIdentityMap.spec.js diff --git a/vtest/unit/specs/utils/validation/anythingValidator.spec.js b/test/unit/specs/utils/validation/anythingValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/anythingValidator.spec.js rename to test/unit/specs/utils/validation/anythingValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/booleanValidator.spec.js b/test/unit/specs/utils/validation/booleanValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/booleanValidator.spec.js rename to test/unit/specs/utils/validation/booleanValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/callbackValidator.spec.js b/test/unit/specs/utils/validation/callbackValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/callbackValidator.spec.js rename to test/unit/specs/utils/validation/callbackValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createAnyOfValidator.spec.js b/test/unit/specs/utils/validation/createAnyOfValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createAnyOfValidator.spec.js rename to test/unit/specs/utils/validation/createAnyOfValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createArrayOfValidator.spec.js b/test/unit/specs/utils/validation/createArrayOfValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createArrayOfValidator.spec.js rename to test/unit/specs/utils/validation/createArrayOfValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createDefaultValidator.spec.js b/test/unit/specs/utils/validation/createDefaultValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createDefaultValidator.spec.js rename to test/unit/specs/utils/validation/createDefaultValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createDeprecatedValidator.spec.js b/test/unit/specs/utils/validation/createDeprecatedValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createDeprecatedValidator.spec.js rename to test/unit/specs/utils/validation/createDeprecatedValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createLiteralValidator.spec.js b/test/unit/specs/utils/validation/createLiteralValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createLiteralValidator.spec.js rename to test/unit/specs/utils/validation/createLiteralValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createMapOfValuesValidator.spec.js b/test/unit/specs/utils/validation/createMapOfValuesValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createMapOfValuesValidator.spec.js rename to test/unit/specs/utils/validation/createMapOfValuesValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createMaximumValidator.spec.js b/test/unit/specs/utils/validation/createMaximumValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createMaximumValidator.spec.js rename to test/unit/specs/utils/validation/createMaximumValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createMinimumValidator.spec.js b/test/unit/specs/utils/validation/createMinimumValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createMinimumValidator.spec.js rename to test/unit/specs/utils/validation/createMinimumValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createNoUnknownFieldsValidator.spec.js b/test/unit/specs/utils/validation/createNoUnknownFieldsValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createNoUnknownFieldsValidator.spec.js rename to test/unit/specs/utils/validation/createNoUnknownFieldsValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createNonEmptyValidator.spec.js b/test/unit/specs/utils/validation/createNonEmptyValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createNonEmptyValidator.spec.js rename to test/unit/specs/utils/validation/createNonEmptyValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createObjectOfValidator.spec.js b/test/unit/specs/utils/validation/createObjectOfValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createObjectOfValidator.spec.js rename to test/unit/specs/utils/validation/createObjectOfValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createRenamedValidator.spec.js b/test/unit/specs/utils/validation/createRenamedValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createRenamedValidator.spec.js rename to test/unit/specs/utils/validation/createRenamedValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createUniqueItemsValidator.spec.js b/test/unit/specs/utils/validation/createUniqueItemsValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createUniqueItemsValidator.spec.js rename to test/unit/specs/utils/validation/createUniqueItemsValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/createUniqueValidator.spec.js b/test/unit/specs/utils/validation/createUniqueValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/createUniqueValidator.spec.js rename to test/unit/specs/utils/validation/createUniqueValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/domainValidator.spec.js b/test/unit/specs/utils/validation/domainValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/domainValidator.spec.js rename to test/unit/specs/utils/validation/domainValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/enumOfValidator.spec.js b/test/unit/specs/utils/validation/enumOfValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/enumOfValidator.spec.js rename to test/unit/specs/utils/validation/enumOfValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/integerValidator.spec.js b/test/unit/specs/utils/validation/integerValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/integerValidator.spec.js rename to test/unit/specs/utils/validation/integerValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/matchesRegexpValidator.spec.js b/test/unit/specs/utils/validation/matchesRegexpValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/matchesRegexpValidator.spec.js rename to test/unit/specs/utils/validation/matchesRegexpValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/numberValidator.spec.js b/test/unit/specs/utils/validation/numberValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/numberValidator.spec.js rename to test/unit/specs/utils/validation/numberValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/regexpValidator.spec.js b/test/unit/specs/utils/validation/regexpValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/regexpValidator.spec.js rename to test/unit/specs/utils/validation/regexpValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/requiredValidator.spec.js b/test/unit/specs/utils/validation/requiredValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/requiredValidator.spec.js rename to test/unit/specs/utils/validation/requiredValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/stringValidator.spec.js b/test/unit/specs/utils/validation/stringValidator.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/stringValidator.spec.js rename to test/unit/specs/utils/validation/stringValidator.spec.js diff --git a/vtest/unit/specs/utils/validation/utils.spec.js b/test/unit/specs/utils/validation/utils.spec.js similarity index 100% rename from vtest/unit/specs/utils/validation/utils.spec.js rename to test/unit/specs/utils/validation/utils.spec.js diff --git a/vitest.config.js b/vitest.config.js index cd31557ab..82fbf45ce 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -4,7 +4,7 @@ import { defineProject } from "vitest/config"; export default defineProject({ test: { name: "unit-tests", - include: ["vtest/**/*.{test,spec}.?(c|m)[jt]s?(x)"], + include: ["test/unit/**/*.{test,spec}.?(c|m)[jt]s?(x)"], isolate: false, browser: { provider: "playwright", diff --git a/vtest/unit/specs/utils/dom/awaitSelector.spec.js b/vtest/unit/specs/utils/dom/awaitSelector.spec.js deleted file mode 100644 index a6a108192..000000000 --- a/vtest/unit/specs/utils/dom/awaitSelector.spec.js +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2019 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ - -import { afterAll, describe, it, expect } from "vitest"; -import awaitSelector from "../../../../../src/utils/dom/awaitSelector.js"; - -describe("awaitSelector", () => { - it("await via requestAnimationFrame", (done) => { - // Create test element - const testElement = document.createElement("div"); - testElement.id = "def"; - - // Immediately append element to document - document.body.appendChild(testElement); - - // Now wait for selector - awaitSelector("#def") - .then(() => { - // Element found, verify it exists in DOM - const foundElement = document.querySelector("#def"); - expect(foundElement).toBeTruthy(); - expect(foundElement.id).toBe("def"); - - // Cleanup - document.body.removeChild(testElement); - }) - .catch((error) => { - // Cleanup on error - if (testElement.parentNode) { - document.body.removeChild(testElement); - } - done.fail(error); - }); - }); - - // Ensure cleanup after all tests - afterAll(() => { - const element = document.querySelector("#def"); - if (element) { - element.parentNode.removeChild(element); - } - }); -}); From 75dba0d54eb02ec26089bed729b54ef762525d93 Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Wed, 8 Jan 2025 14:12:25 -0700 Subject: [PATCH 10/15] Update tests to match latest changes from main. --- .../createActionsProvider.spec.js | 96 ------------------- .../createPreprocessors.spec.js | 41 -------- .../specs/utils/dom/awaitSelector.spec.js | 2 +- test/unit/specs/utils/networkErrors.spec.js | 78 --------------- 4 files changed, 1 insertion(+), 216 deletions(-) delete mode 100644 test/unit/specs/components/Personalization/createActionsProvider.spec.js delete mode 100644 test/unit/specs/components/Personalization/createPreprocessors.spec.js delete mode 100644 test/unit/specs/utils/networkErrors.spec.js diff --git a/test/unit/specs/components/Personalization/createActionsProvider.spec.js b/test/unit/specs/components/Personalization/createActionsProvider.spec.js deleted file mode 100644 index 73d89e71f..000000000 --- a/test/unit/specs/components/Personalization/createActionsProvider.spec.js +++ /dev/null @@ -1,96 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { vi, beforeEach, describe, it, expect } from "vitest"; -import createActionsProvider from "../../../../../src/components/Personalization/createActionsProvider.js"; - -describe("createActionsProvider", () => { - let actionsProvider; - let logger; - beforeEach(() => { - logger = { - warn: vi.fn(), - error: vi.fn(), - info: vi.fn(), - }; - logger.enabled = true; - actionsProvider = createActionsProvider({ - modules: { - something: { - eat: () => Promise.resolve("yum"), - sleep: () => Promise.resolve(), - exercise: () => Promise.resolve(), - }, - }, - preprocessors: { - something: [(action) => action], - superfluous: [ - (action) => action, - (action) => action, - (action) => action, - ], - }, - logger, - }); - }); - it("executes appropriate action", () => { - const actionDetails = { - schema: "something", - type: "eat", - itWorked: true, - }; - actionsProvider.executeAction(actionDetails).then((result) => { - expect(result).toEqual("yum"); - expect(logger.info).toHaveBeenNthCalledWith( - 1, - expect.stringContaining( - `Action ${JSON.stringify(actionDetails)} executed.`, - ), - ); - }); - }); - it("throws error if missing schema", () => { - const actionDetails = { - schema: "hidden-valley", - type: "truckee", - itWorked: true, - }; - actionsProvider.executeAction(actionDetails).catch((error) => { - expect(error.message).toEqual( - `Action "truckee" not found for schema "hidden-valley"`, - ); - expect(logger.warn).toHaveBeenNthCalledWith( - 1, - expect.stringContaining( - `Failed to execute action ${JSON.stringify(actionDetails)}.`, - ), - ); - }); - }); - it("throws error if missing action", () => { - const actionDetails = { - schema: "something", - type: "truckee", - itWorked: true, - }; - actionsProvider.executeAction(actionDetails).catch((error) => { - expect(error.message).toEqual( - `Action "truckee" not found for schema "something"`, - ); - expect(logger.warn).toHaveBeenNthCalledWith( - 1, - expect.stringContaining( - `Failed to execute action ${JSON.stringify(actionDetails)}.`, - ), - ); - }); - }); -}); diff --git a/test/unit/specs/components/Personalization/createPreprocessors.spec.js b/test/unit/specs/components/Personalization/createPreprocessors.spec.js deleted file mode 100644 index e7516b647..000000000 --- a/test/unit/specs/components/Personalization/createPreprocessors.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { describe, it, expect } from "vitest"; -import { DOM_ACTION } from "../../../../../src/constants/schema.js"; -import createPreprocessors from "../../../../../src/components/Personalization/createPreprocessors.js"; - -describe("Personalization::createPreprocessors", () => { - it("has dom-action preprocessors", () => { - const preprocessors = createPreprocessors(); - expect(preprocessors).toEqual({ - [DOM_ACTION]: expect.any(Array), - }); - expect(preprocessors[DOM_ACTION].length).toEqual(2); - preprocessors[DOM_ACTION].forEach((preprocessor) => { - expect(preprocessor).toEqual(expect.any(Function)); - }); - }); - it("is structured correctly", () => { - const preprocessors = createPreprocessors(); - Object.keys(preprocessors).forEach((key) => { - expect(key.startsWith("https://ns.adobe.com/personalization/")).toBe( - true, - ); - }); - Object.values(preprocessors).forEach((list) => { - expect(list instanceof Array).toBe(true); - list.forEach((preprocessor) => { - expect(preprocessor).toEqual(expect.any(Function)); - }); - }); - }); -}); diff --git a/test/unit/specs/utils/dom/awaitSelector.spec.js b/test/unit/specs/utils/dom/awaitSelector.spec.js index 657b941ee..17faee903 100644 --- a/test/unit/specs/utils/dom/awaitSelector.spec.js +++ b/test/unit/specs/utils/dom/awaitSelector.spec.js @@ -10,7 +10,7 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ -import { afterAll, describe, it, expect } from "vitest"; +import { describe, it, expect } from "vitest"; import awaitSelector from "../../../../../src/utils/dom/awaitSelector.js"; import selectNodes from "../../../../../src/utils/dom/selectNodes.js"; import { diff --git a/test/unit/specs/utils/networkErrors.spec.js b/test/unit/specs/utils/networkErrors.spec.js deleted file mode 100644 index b34f1cd39..000000000 --- a/test/unit/specs/utils/networkErrors.spec.js +++ /dev/null @@ -1,78 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { - TYPE_ERROR, - NETWORK_ERROR, - isNetworkError, -} from "../../../../src/utils/networkErrors.js"; - -describe("Network Errors", () => { - describe("isNetworkError", () => { - it("returns true for TypeError", () => { - const error = new Error(); - error.name = TYPE_ERROR; - expect(isNetworkError(error)).toBe(true); - }); - it("returns true for NetworkError", () => { - const error = new Error(); - error.name = NETWORK_ERROR; - expect(isNetworkError(error)).toBe(true); - }); - it("returns true for status 0", () => { - const error = { - status: 0, - }; - expect(isNetworkError(error)).toBe(true); - }); - it("returns false for other errors", () => { - const error = new Error(); - error.name = "SyntaxError"; - expect(isNetworkError(error)).toBe(false); - }); - it("returns false for non-zero status", () => { - const error = { - status: 500, - }; - expect(isNetworkError(error)).toBe(false); - }); - it("returns false for undefined status", () => { - const error = new Error(); - expect(isNetworkError(error)).toBe(false); - }); - }); -}); - -describe("Network Errors", () => { - describe("isNetworkError", () => { - it("returns true for TypeError", () => { - const error = new Error(); - error.name = TYPE_ERROR; - expect(isNetworkError(error)).toBe(true); - }); - it("returns true for NetworkError", () => { - const error = new Error(); - error.name = NETWORK_ERROR; - expect(isNetworkError(error)).toBe(true); - }); - it("returns true for status 0", () => { - const error = { - status: 0, - }; - expect(isNetworkError(error)).toBe(true); - }); - it("returns false for other errors", () => { - const error = new Error(); - error.name = "SyntaxError"; - expect(isNetworkError(error)).toBe(false); - }); - it("returns false for non-zero status", () => { - const error = { - status: 500, - }; - expect(isNetworkError(error)).toBe(false); - }); - it("returns false for undefined status", () => { - const error = new Error(); - expect(isNetworkError(error)).toBe(false); - }); - }); -}); From 7f673a7f9989653b1cc94525b80179c734c997d3 Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Wed, 8 Jan 2025 14:25:49 -0700 Subject: [PATCH 11/15] Remove saucelabs references. --- .github/workflows/dev.yaml | 9 - .github/workflows/publish-to-npm.yaml | 3 - package-lock.json | 733 +------------------------- package.json | 2 - 4 files changed, 19 insertions(+), 728 deletions(-) diff --git a/.github/workflows/dev.yaml b/.github/workflows/dev.yaml index a5992240d..4692b3180 100644 --- a/.github/workflows/dev.yaml +++ b/.github/workflows/dev.yaml @@ -2,14 +2,6 @@ name: Alloy Dev on: pull_request: workflow_dispatch: -env: - SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} - SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} - SAUCE_JOB: "Alloy Dev Workflow" - SAUCE_CAPABILITIES_OVERRIDES_PATH: "sauceLabsCapabilities.json" - EDGE_BASE_PATH: ee-pre-prd - ALLOY_ENV: int - SAUCE_TUNNEL_ID: github-action-tunnel jobs: linting: @@ -55,4 +47,3 @@ jobs: BUILD_NUMBER: $GITHUB_RUN_NUMBER BUILD_ID: $GITHUB_RUN_ID JOB_NUMBER: $GITHUB_JOB - diff --git a/.github/workflows/publish-to-npm.yaml b/.github/workflows/publish-to-npm.yaml index 9d270894e..6fe220c52 100644 --- a/.github/workflows/publish-to-npm.yaml +++ b/.github/workflows/publish-to-npm.yaml @@ -1,8 +1,5 @@ name: Publish To NPM on: workflow_dispatch -env: - SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} - SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} jobs: publish-to-npm: diff --git a/package-lock.json b/package-lock.json index 3ed2ae7e8..dd3afbe27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,9 +75,7 @@ "staged-git-files": "^1.3.0", "start-server-and-test": "^2.0.8", "testcafe": "^3.7.0", - "testcafe-browser-provider-saucelabs": "^3.0.0", "testcafe-reporter-junit": "^3.0.2", - "testcafe-reporter-saucelabs": "^3.5.1", "url-exists-nodejs": "^0.2.4", "url-parse": "^1.5.10", "vitest": "^2.1.4" @@ -3954,30 +3952,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@saucelabs/sauce-json-reporter": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@saucelabs/sauce-json-reporter/-/sauce-json-reporter-4.1.0.tgz", - "integrity": "sha512-UhqXXsaW4yRA/7v10qeCp1B7tIw09fghsNIr2wuU93XhbkqxXmMJTYItqFX66k1JJSEzHsay8Md7sTkUlJfV6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16.13.2" - } - }, - "node_modules/@saucelabs/testcomposer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@saucelabs/testcomposer/-/testcomposer-3.0.1.tgz", - "integrity": "sha512-4Ye6v09vXsxud89YSoQ1Ag6JFUsZUTYYMZtOux/S1sUch2nN9jkhb4Itn6PxmUqwA7vjMNII8qHOPdLByEFQhA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "axios": "^1.7.5", - "form-data": "^4.0.0" - }, - "engines": { - "node": ">=16.13.2" - } - }, "node_modules/@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -4002,32 +3976,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "dev": true, - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@testing-library/dom": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", @@ -4144,19 +4092,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -4170,23 +4105,14 @@ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "license": "MIT" }, - "node_modules/@types/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/minimatch": "^5.1.2", - "@types/node": "*" - } - }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@types/json5": { "version": "0.0.29", @@ -4195,16 +4121,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/lodash": { "version": "4.17.14", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.14.tgz", @@ -4234,16 +4150,6 @@ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "license": "MIT" }, - "node_modules/@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/statuses": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", @@ -4505,160 +4411,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/@wdio/config": { - "version": "7.40.0", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.40.0.tgz", - "integrity": "sha512-ayQELXyxa+k9/2a509F5a1oTsCa/w8D1nDrd+hzm+1mYb4Te2lceWCCzm+atGKkMpvjLH4GvhrEBYLh3rIWk2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/glob": "^8.1.0", - "@wdio/logger": "7.26.0", - "@wdio/types": "7.40.0", - "@wdio/utils": "7.40.0", - "deepmerge": "^4.0.0", - "glob": "^8.0.3" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@wdio/config/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@wdio/config/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "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": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@wdio/config/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@wdio/logger": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.26.0.tgz", - "integrity": "sha512-kQj9s5JudAG9qB+zAAcYGPHVfATl2oqKgqj47yjehOQ1zzG33xmtL1ArFbQKWhDG32y1A8sN6b0pIqBEIwgg8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@wdio/logger/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@wdio/logger/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@wdio/logger/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@wdio/logger/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@wdio/logger/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@wdio/protocols": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.27.0.tgz", - "integrity": "sha512-hT/U22R5i3HhwPjkaKAG0yd59eaOaZB0eibRj2+esCImkb5Y6rg8FirrlYRxIGFVBl0+xZV0jKHzR5+o097nvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/@wdio/repl": { "version": "8.40.3", "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.40.3.tgz", @@ -4674,60 +4426,6 @@ "node": "^16.13 || >=18" } }, - "node_modules/@wdio/types": { - "version": "7.40.0", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.40.0.tgz", - "integrity": "sha512-MWMbU+8uk+JrF7ygP/TJDsaSvFozKauiW6EnG7rxx9+GvU1Q1B3l4UjAc7GDbgLKjwt8T2y5GDRiDoD3UOjVyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "^18.0.0", - "got": "^11.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "typescript": "^4.6.2" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@wdio/types/node_modules/@types/node": { - "version": "18.19.70", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.70.tgz", - "integrity": "sha512-RE+K0+KZoEpDUbGGctnGdkrLFwi1eYKTlIHNl2Um98mUkGsm1u2Ff6Ltd0e8DktTtC98uy7rSj+hO8t/QuLoVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@wdio/types/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@wdio/utils": { - "version": "7.40.0", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.40.0.tgz", - "integrity": "sha512-jLF57xHmz5nnGuM6ZRWjVYa/LQb22CS7yG50dUFa9wJ509mC1HlUzaA01Gjk9TV5jf9vnwE/yZfUMCoecTgG9w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@wdio/logger": "7.26.0", - "@wdio/types": "7.40.0", - "p-iteration": "^1.1.8" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/@zip.js/zip.js": { "version": "2.7.54", "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.54.tgz", @@ -4805,16 +4503,6 @@ "node": ">= 16.0.0" } }, - "node_modules/adm-zip": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", - "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.3.0" - } - }, "node_modules/agent-base": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", @@ -5703,33 +5391,6 @@ "dev": true, "license": "MIT" }, - "node_modules/babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "node_modules/babel-runtime/node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "dev": true, - "hasInstallScript": true, - "license": "MIT" - }, - "node_modules/babel-runtime/node_modules/regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true, - "license": "MIT" - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -6106,51 +5767,6 @@ "node": ">=8" } }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -6657,19 +6273,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -7375,6 +6978,8 @@ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "mimic-response": "^3.1.0" }, @@ -7391,6 +6996,8 @@ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=10" }, @@ -7458,6 +7065,8 @@ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=10" } @@ -7647,16 +7256,6 @@ "minimalistic-assert": "^1.0.0" } }, - "node_modules/desired-capabilities": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/desired-capabilities/-/desired-capabilities-0.1.0.tgz", - "integrity": "sha512-MNcwZi1elX2YXbTUfs1R6A6RD5Ns3loTljRBu6FGNHY3LPFLVHzTg2tNLlZEpWVZdHQ0cVeQRHrCBdrnW/n1wA==", - "dev": true, - "license": "CC0", - "dependencies": { - "extend": "^3.0.0" - } - }, "node_modules/detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -10127,32 +9726,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -10521,20 +10094,6 @@ "dev": true, "license": "MIT" }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, "node_modules/httpntlm": { "version": "1.8.13", "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.8.13.tgz", @@ -11727,19 +11286,6 @@ "node": ">=6" } }, - "node_modules/ky": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/ky/-/ky-0.30.0.tgz", - "integrity": "sha512-X/u76z4JtDVq10u1JA5UQfatPxgPaVDMYTrgHyiTpGN2z4TMEJkIHsoSBBSg9SWZEIXTKsi9kHgiQ9o3Y/4yog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/ky?sponsor=1" - } - }, "node_modules/lazy-ass": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", @@ -12332,6 +11878,8 @@ "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.6.0" }, @@ -12345,7 +11893,9 @@ "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/loupe": { "version": "3.1.2", @@ -12354,16 +11904,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -12580,16 +12120,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -12948,19 +12478,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/npm-run-path": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", @@ -13236,26 +12753,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/p-iteration": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/p-iteration/-/p-iteration-1.1.8.tgz", - "integrity": "sha512-IMFBSDIYcPNnW7uWYGrBqmvTiq7W0uB0fJn6shQZs7dlF3OvrHOre+JT9ikSZ7gZS3vWqclVgoQSvToJrns7uQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -14250,6 +13747,8 @@ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=10" }, @@ -14724,7 +14223,9 @@ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/resolve-cwd": { "version": "1.0.0", @@ -14759,19 +14260,6 @@ "node": ">=4" } }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lowercase-keys": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/resq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/resq/-/resq-1.11.0.tgz", @@ -15153,114 +14641,6 @@ "truncate-utf8-bytes": "^1.0.0" } }, - "node_modules/sauce-connect-launcher": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/sauce-connect-launcher/-/sauce-connect-launcher-1.3.2.tgz", - "integrity": "sha512-wf0coUlidJ7rmeClgVVBh6Kw55/yalZCY/Un5RgjSnTXRAeGqagnTsTYpZaqC4dCtrY4myuYpOAZXCdbO7lHfQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "adm-zip": "~0.4.3", - "async": "^2.1.2", - "https-proxy-agent": "^5.0.0", - "lodash": "^4.16.6", - "rimraf": "^2.5.4" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/sauce-connect-launcher/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/sauce-connect-launcher/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/sauce-connect-launcher/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/sauce-connect-launcher/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/sauce-connect-launcher/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/saucelabs-connector": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/saucelabs-connector/-/saucelabs-connector-2.0.0.tgz", - "integrity": "sha512-fWZ8hmS/xEgmfGA8itOO1eEzIlLGxpw8PpQ0TpSxhm5xA2LUS3NPeR1b/6GhPbjzCjDTmjKoQgsOuKIkHIRI9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "got": "^11.8.2", - "os-family": "^1.0.0", - "read-file-relative": "^1.2.0", - "sauce-connect-launcher": "^1.2.7", - "webdriver": "^7.31.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -16621,23 +16001,6 @@ "node": ">=16.0.0" } }, - "node_modules/testcafe-browser-provider-saucelabs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/testcafe-browser-provider-saucelabs/-/testcafe-browser-provider-saucelabs-3.0.0.tgz", - "integrity": "sha512-CywfkQ8tDTUeEawLRaCgedEjXM6zCKvXhAtYzPjXo6wZ3Xp3i3jbuW8l7/9Gn2gFN0CevFZ131L0/cr1n7//CA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-runtime": "^6.11.6", - "desired-capabilities": "^0.1.0", - "lodash": "^4.14.2", - "pinkie": "^2.0.4", - "saucelabs-connector": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/testcafe-browser-tools": { "version": "2.0.26", "resolved": "https://registry.npmjs.org/testcafe-browser-tools/-/testcafe-browser-tools-2.0.26.tgz", @@ -17259,26 +16622,6 @@ "dev": true, "license": "MIT" }, - "node_modules/testcafe-reporter-saucelabs": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/testcafe-reporter-saucelabs/-/testcafe-reporter-saucelabs-3.6.0.tgz", - "integrity": "sha512-4yHWSuFeZ1vtkIxuoF3D/EQDmG9Iw+SFUXDiOBI33lf1ipnI4XgtpUBn8PM+HKBM5oQxaOOBw9tc57am+gF0kg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@devexpress/callsite-record": "4.1.7", - "@saucelabs/sauce-json-reporter": "4.1.0", - "@saucelabs/testcomposer": "3.0.1", - "axios": "^1.7.5", - "debug": "^4.3.6" - }, - "engines": { - "node": ">=16.13.2" - }, - "peerDependencies": { - "testcafe": ">=3.2.0" - } - }, "node_modules/testcafe-reporter-spec": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/testcafe-reporter-spec/-/testcafe-reporter-spec-2.2.0.tgz", @@ -18749,44 +18092,6 @@ "node": ">= 8" } }, - "node_modules/webdriver": { - "version": "7.40.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.40.0.tgz", - "integrity": "sha512-CKi3cDWgNVE/ibcsBfdtA+pQVeZ4oYlecLlwemulVxJdgr4l5bv+nXuoIhnYeVb6aAI4naK772vmWQ0XuRYhDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "^18.0.0", - "@wdio/config": "7.40.0", - "@wdio/logger": "7.26.0", - "@wdio/protocols": "7.27.0", - "@wdio/types": "7.40.0", - "@wdio/utils": "7.40.0", - "got": "^11.0.2", - "ky": "0.30.0", - "lodash.merge": "^4.6.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/webdriver/node_modules/@types/node": { - "version": "18.19.70", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.70.tgz", - "integrity": "sha512-RE+K0+KZoEpDUbGGctnGdkrLFwi1eYKTlIHNl2Um98mUkGsm1u2Ff6Ltd0e8DktTtC98uy7rSj+hO8t/QuLoVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/webdriver/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true, - "license": "MIT" - }, "node_modules/webdriverio": { "version": "8.41.0", "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.41.0.tgz", diff --git a/package.json b/package.json index 567a921bf..4a9a83f35 100644 --- a/package.json +++ b/package.json @@ -132,9 +132,7 @@ "staged-git-files": "^1.3.0", "start-server-and-test": "^2.0.8", "testcafe": "^3.7.0", - "testcafe-browser-provider-saucelabs": "^3.0.0", "testcafe-reporter-junit": "^3.0.2", - "testcafe-reporter-saucelabs": "^3.5.1", "url-exists-nodejs": "^0.2.4", "url-parse": "^1.5.10", "vitest": "^2.1.4" From f2f73a487f3d18ae1f8b73bbe5b443861a88137e Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Thu, 9 Jan 2025 13:05:19 -0700 Subject: [PATCH 12/15] Remove duplicate batch of tests. --- .../Consent/configValidators.spec.js | 39 ------------------- 1 file changed, 39 deletions(-) diff --git a/test/unit/specs/components/Consent/configValidators.spec.js b/test/unit/specs/components/Consent/configValidators.spec.js index 9ae9642a6..f9f477387 100644 --- a/test/unit/specs/components/Consent/configValidators.spec.js +++ b/test/unit/specs/components/Consent/configValidators.spec.js @@ -51,42 +51,3 @@ describe("defaultConsent", () => { expect(config.defaultConsent).toEqual("out"); }); }); - -describe("defaultConsent", () => { - it("validates defaultConsent=undefined", () => { - const config = configValidators({}); - expect(config.defaultConsent).toEqual("in"); - }); - it("validates defaultConsent={}", () => { - expect(() => { - configValidators({ - defaultConsent: {}, - }); - }).toThrowError(); - }); - it("validates defaultConsent='in'", () => { - const config = configValidators({ - defaultConsent: "in", - }); - expect(config.defaultConsent).toEqual("in"); - }); - it("validates defaultConsent='pending'", () => { - const config = configValidators({ - defaultConsent: "pending", - }); - expect(config.defaultConsent).toEqual("pending"); - }); - it("validates defaultConsent=123", () => { - expect(() => { - configValidators({ - defaultConsent: 123, - }); - }).toThrowError(); - }); - it("validates defaultConsent='out'", () => { - const config = configValidators({ - defaultConsent: "out", - }); - expect(config.defaultConsent).toEqual("out"); - }); -}); From a8e8a47665e44493f992c6048dae55eb1f99ac2f Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Thu, 9 Jan 2025 13:05:42 -0700 Subject: [PATCH 13/15] Fix tests messed by the convert script. --- .../Personalization/dom-actions/dom/isDomElement.spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js b/test/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js index 5ca8b07f5..23bff3ef1 100644 --- a/test/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js +++ b/test/unit/specs/components/Personalization/dom-actions/dom/isDomElement.spec.js @@ -35,9 +35,9 @@ describe("Personalization::DOM::isDomElement", () => { expect(isDomElement(document.getElementById(testElementId))).toBe(true); }); it("validates not a dom element", () => { - expect(false); - expect(false); - expect(false); - expect(false); + expect(isDomElement({}).toBeFalse); + expect(isDomElement([]).toBeFalse); + expect(isDomElement(true).toBeFalse); + expect(isDomElement("something").toBeFalse); }); }); From 465c7277b874291b53ce327283f9fa262a8ca597 Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Thu, 9 Jan 2025 13:06:03 -0700 Subject: [PATCH 14/15] Remove mess. --- .../Personalization/topLevel/mixedPropositions.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js b/test/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js index 755a9ff68..95c0ffb2a 100644 --- a/test/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js +++ b/test/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js @@ -1,4 +1,4 @@ -/* mixedpro +/* Copyright 2023 Adobe. All rights reserved. This file is licensed to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy From 2efd42745cc27cde443bf44173b17f8129ed682f Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Thu, 9 Jan 2025 13:06:21 -0700 Subject: [PATCH 15/15] Remove unnecessary dependencies. --- package-lock.json | 68 ++++++++++++++++++++++++++++++++++++++++------- package.json | 2 -- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index dd3afbe27..9513a720f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,6 @@ "@vitest/browser": "^2.1.8", "@vitest/coverage-v8": "^2.1.8", "bundlesize": "^0.18.2", - "chai": "^5.1.2", "chalk": "^5.3.0", "concurrently": "^9.1.0", "date-fns": "^4.1.0", @@ -61,7 +60,6 @@ "handlebars": "^4.7.8", "happy-dom": "^15.11.1", "husky": "^9.1.6", - "jsdom": "^25.0.1", "lint-staged": "^15.2.10", "playwright": "^1.49.1", "prettier": "^3.3.3", @@ -4509,6 +4507,8 @@ "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 14" } @@ -6812,6 +6812,8 @@ "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "rrweb-cssom": "^0.7.1" }, @@ -6850,6 +6852,8 @@ "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" @@ -6864,6 +6868,8 @@ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=18" } @@ -6970,7 +6976,9 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/decompress-response": { "version": "6.0.0", @@ -10036,6 +10044,8 @@ "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "whatwg-encoding": "^3.1.1" }, @@ -10063,6 +10073,8 @@ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -10135,6 +10147,8 @@ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" @@ -10778,7 +10792,9 @@ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/is-reference": { "version": "1.2.1", @@ -11128,6 +11144,8 @@ "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "cssstyle": "^4.1.0", "data-urls": "^5.0.0", @@ -11169,6 +11187,8 @@ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=18" } @@ -12536,7 +12556,9 @@ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/oauth-sign": { "version": "0.9.0", @@ -14506,7 +14528,9 @@ "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/run-parallel": { "version": "1.2.0", @@ -14647,6 +14671,8 @@ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "xmlchars": "^2.2.0" }, @@ -15687,7 +15713,9 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/synckit": { "version": "0.9.2", @@ -17203,6 +17231,8 @@ "integrity": "sha512-LQIHmHnuzfZgZWAf2HzL83TIIrD8NhhI0DVxqo9/FdOd4ilec+NTNZOlDZf7EwrTNoutccbsHjvWHYXLAtvxjw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tldts-core": "^6.1.71" }, @@ -17215,7 +17245,9 @@ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.71.tgz", "integrity": "sha512-LRbChn2YRpic1KxY+ldL1pGXN/oVvKfCVufwfVzEQdFYNo39uF7AJa/WXdo+gYO7PTvdfkCPCed6Hkvz/kR7jg==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/tmp": { "version": "0.0.33", @@ -17258,6 +17290,8 @@ "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", "dev": true, "license": "BSD-3-Clause", + "optional": true, + "peer": true, "dependencies": { "tldts": "^6.1.32" }, @@ -17271,6 +17305,8 @@ "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "punycode": "^2.3.1" }, @@ -17284,6 +17320,8 @@ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -17945,6 +17983,8 @@ "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "xml-name-validator": "^5.0.0" }, @@ -18671,6 +18711,8 @@ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "iconv-lite": "0.6.3" }, @@ -18684,6 +18726,8 @@ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -18707,6 +18751,8 @@ "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tr46": "^5.0.0", "webidl-conversions": "^7.0.0" @@ -19297,6 +19343,8 @@ "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": ">=18" } @@ -19306,7 +19354,9 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/y18n": { "version": "5.0.8", diff --git a/package.json b/package.json index 4a9a83f35..e44dfd494 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,6 @@ "@vitest/browser": "^2.1.8", "@vitest/coverage-v8": "^2.1.8", "bundlesize": "^0.18.2", - "chai": "^5.1.2", "chalk": "^5.3.0", "concurrently": "^9.1.0", "date-fns": "^4.1.0", @@ -118,7 +117,6 @@ "handlebars": "^4.7.8", "happy-dom": "^15.11.1", "husky": "^9.1.6", - "jsdom": "^25.0.1", "lint-staged": "^15.2.10", "playwright": "^1.49.1", "prettier": "^3.3.3",