diff --git a/jest.config.ts b/jest.config.ts index 55523e8..23460d9 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -13,6 +13,7 @@ export default { moduleNameMapper: { '/guillotine/(.*)': '/src/main/resources/guillotine/$1', '/lib/app-metafields/(.*)': '/src/main/resources/lib/app-metafields/$1', + '/site/(.*)': '/src/main/resources/site/$1', }, preset: 'ts-jest/presets/js-with-babel-legacy', testEnvironment: 'node', diff --git a/package-lock.json b/package-lock.json index 1934edb..eda98db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@enonic-types/lib-node": "^7.14.1", "@enonic-types/lib-portal": "^7.14.1", "@enonic/js-utils": "^1.7.1", + "@enonic/mock-xp": "^1.0.0", "@swc/core": "^1.4.17", "glob": "^10.3.12", "jest": "^29.7.0", @@ -763,6 +764,28 @@ "integrity": "sha512-zPFVfWciEqgxbzna9uBLCCwwilGEMUEmw5bgIT6vuAE357JuxFrx6ZblDBsmYjjAag3FPAO1AnQzlFv3dduW7Q==", "dev": true }, + "node_modules/@enonic/mock-xp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@enonic/mock-xp/-/mock-xp-1.0.0.tgz", + "integrity": "sha512-qK8RKM2Fqt9O3dd3MZQmi88LJrY8zWxvhV3zZsKdWAWCWzIjyvE2m5j5VUnkEZsfCTUixdW8V1LCtSe0KGENmg==", + "dev": true, + "dependencies": { + "@enonic/js-utils": "^1.4.1", + "colors": "^1.4.0", + "fast-uri": "^2.3.0", + "fnv1a": "^1.1.1", + "intersect": "^1.0.1", + "jest": "^29.5.0", + "memfs": "^4.2.0", + "node-forge": "^1.3.1", + "probe-image-size": "^7.2.3", + "q-i": "^2.0.1", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=20.11.1" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", @@ -1598,6 +1621,63 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.1.tgz", + "integrity": "sha512-LnFjVChaGY8cZVMwAIMjvA1XwQjZ/zIXHyh28IyJkyNkzof4Dkm1+KN9UIm3lHhREH4vs7XwZ0NpkZKnwOtEfg==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.0.2.tgz", + "integrity": "sha512-4KMApTgb1Hvjz9Ue7unziJ1xNy3k6d2erp0hz1iXryXsf6LEM3KwN6YrfbqT0vqkUO8Tu+CSnvMia9cWX6YGVw==", + "dev": true, + "dependencies": { + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.0.0", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.1.0.tgz", + "integrity": "sha512-Yz+xITJ3Y/w0DBISwPkBETP5/cITHXscjgQNZIkfrVz1V7/ahJY8vw+T+LZy/KtXgKuUWqu4GALAQ3bhGt9J8A==", + "dev": true, + "dependencies": { + "hyperdyperid": "^1.2.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2727,6 +2807,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "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, + "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", @@ -3130,6 +3219,12 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "node_modules/fast-uri": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.3.0.tgz", + "integrity": "sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==", + "dev": true + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -3173,6 +3268,12 @@ "node": ">=8" } }, + "node_modules/fnv1a": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fnv1a/-/fnv1a-1.1.1.tgz", + "integrity": "sha512-S2HviLR9UyNbt8R+vU6YeQtL8RliPwez9DQEVba5MAvN3Od+RSgKUSL2+qveOMt3owIeBukKoRu2enoOck5uag==", + "dev": true + }, "node_modules/foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -3262,6 +3363,12 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -3451,6 +3558,15 @@ "node": ">=10.17.0" } }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "engines": { + "node": ">=10.18" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -3516,6 +3632,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/intersect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/intersect/-/intersect-1.0.1.tgz", + "integrity": "sha512-qsc720yevCO+4NydrJWgEWKccAQwTOvj2m73O/VBA6iUL2HGZJ9XqBiyraNrBXX/W1IAjdpXdRZk24sq8TzBRg==", + "dev": true + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -3594,12 +3716,42 @@ "node": ">=0.12.0" } }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "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 }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -3618,6 +3770,15 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -4526,6 +4687,12 @@ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -4604,6 +4771,25 @@ "tmpl": "1.0.5" } }, + "node_modules/memfs": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.9.1.tgz", + "integrity": "sha512-36cVYFMaa9HNEYyvkyKCwker8DBmOdjWLrfekE/cHEKJ806fCfKNVhOJNvoyV/CrGSZDtfQPbhn0Zid0gbH0Hw==", + "dev": true, + "dependencies": { + "@jsonjoy.com/json-pack": "^1.0.2", + "@jsonjoy.com/util": "^1.1.0", + "sonic-forest": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">= 4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4706,6 +4892,53 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/needle": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/needle/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==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5026,6 +5259,17 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/probe-image-size": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/probe-image-size/-/probe-image-size-7.2.3.tgz", + "integrity": "sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==", + "dev": true, + "dependencies": { + "lodash.merge": "^4.6.2", + "needle": "^2.5.2", + "stream-parser": "~0.3.1" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -5070,6 +5314,47 @@ } ] }, + "node_modules/q-i": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/q-i/-/q-i-2.0.1.tgz", + "integrity": "sha512-tr7CzPNxkBDBuPzqi/HDUS4uBOppb91akNTeh56TYio8TiIeXp2Yp8ea9NmDu2DmGH35ZjJDq6C3E4SepVZ4bQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "is-plain-object": "^2.0.4", + "stringify-object": "^3.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/q-i/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/q-i/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/q-i/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 + }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -5259,6 +5544,12 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/sax": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", + "dev": true + }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -5322,6 +5613,22 @@ "node": ">=8" } }, + "node_modules/sonic-forest": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sonic-forest/-/sonic-forest-1.0.0.tgz", + "integrity": "sha512-yFO2N4uTUFtgKLw03WWFpN1iEwZySweMsa18XN3Kt0yYrlmVHunC2ZgM+437zDoKISAJHcH3Cg18U7d6tuSgSQ==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -5359,6 +5666,30 @@ "node": ">=10" } }, + "node_modules/stream-parser": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz", + "integrity": "sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==", + "dev": true, + "dependencies": { + "debug": "2" + } + }, + "node_modules/stream-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/stream-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -5401,6 +5732,20 @@ "node": ">=8" } }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -5563,6 +5908,18 @@ "node": ">=0.8" } }, + "node_modules/thingies": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", + "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", + "dev": true, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "tslib": "^2" + } + }, "node_modules/thymeleaf": { "version": "0.22.0", "resolved": "https://registry.npmjs.org/thymeleaf/-/thymeleaf-0.22.0.tgz", @@ -5768,6 +6125,12 @@ } } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/tsup": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.0.2.tgz", @@ -5895,6 +6258,12 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==", + "dev": true + }, "node_modules/universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", diff --git a/package.json b/package.json index 698be90..ee15ec8 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "@enonic-types/lib-node": "^7.14.1", "@enonic-types/lib-portal": "^7.14.1", "@enonic/js-utils": "^1.7.1", + "@enonic/mock-xp": "^1.0.0", "@swc/core": "^1.4.17", "glob": "^10.3.12", "jest": "^29.7.0", diff --git a/src/main/resources/lib/app-metafields/constants.ts b/src/main/resources/lib/app-metafields/constants.ts index 9d0d8f0..2c5c0af 100644 --- a/src/main/resources/lib/app-metafields/constants.ts +++ b/src/main/resources/lib/app-metafields/constants.ts @@ -3,4 +3,5 @@ import {getDashedAppName} from '/lib/app-metafields/xp/getDashedAppName'; export const APP_NAME_PATH = getDashedAppName(); // Outside Guillotine Extension Context export const DEBUG = false; +export const TRACE = false; export const MIXIN_PATH = 'meta-data'; diff --git a/src/main/resources/lib/app-metafields/types/Request.d.ts b/src/main/resources/lib/app-metafields/types/Request.d.ts index a80ab4f..57c8b1e 100644 --- a/src/main/resources/lib/app-metafields/types/Request.d.ts +++ b/src/main/resources/lib/app-metafields/types/Request.d.ts @@ -8,7 +8,8 @@ export declare type Request< pathParams?: Record rawPath?: string remoteAddress?: string - // webSocket?: unknown + repositoryId?: string + webSocket?: boolean } > = { branch: 'draft'|'master' diff --git a/src/main/resources/site/processors/add-metadata.ts b/src/main/resources/site/processors/add-metadata.ts index 6742485..657bad9 100644 --- a/src/main/resources/site/processors/add-metadata.ts +++ b/src/main/resources/site/processors/add-metadata.ts @@ -8,7 +8,7 @@ import { getSite as libPortalGetSite, getSiteConfig as libPortalGetSiteConfig, } from '/lib/xp/portal'; -import {DEBUG} from '/lib/app-metafields/constants'; +import {DEBUG, TRACE} from '/lib/app-metafields/constants'; import {getMergedConfig} from '/lib/app-metafields/xp/getMergedConfig'; import {getFixedHtmlAttrsAsString} from '/lib/app-metafields/processor/getFixedHtmlAttrsAsString'; import {getMetaData} from '/lib/app-metafields/processor/getMetaData' @@ -20,8 +20,14 @@ const XML_MEDIA_TYPES = ['application/xhtml+xml', 'application/xml', 'text/xml'] export const responseProcessor = (req: Request, res: Response) => { + DEBUG && log.debug('add-metadata response processor Request:%s Response:%s', toStr(req), toStr(res)); + const content = getCurrentContent(); + DEBUG && log.debug('add-metadata response processor Content:%s', toStr(content)); + const site = libPortalGetSite(); + DEBUG && log.debug('add-metadata response processor Site:%s', toStr(content)); + const siteConfig = libPortalGetSiteConfig(); DEBUG && log.debug('add-metadata response processor siteConfig:%s', toStr(siteConfig)); @@ -29,6 +35,8 @@ export const responseProcessor = (req: Request, res: Response) => { let titleAdded = false; const isResponseContentTypeHtml = res.contentType.indexOf(HTML_MEDIA_TYPE) > -1; + TRACE && log.debug('add-metadata response processor isResponseContentTypeHtml:%s', isResponseContentTypeHtml); + const isResponseContentTypeXml = XML_MEDIA_TYPES.some(xmlMediaType => res.contentType.indexOf(xmlMediaType) > -1); if ( isResponseContentTypeHtml && res.body && typeof res.body === "string" ) { @@ -43,7 +51,8 @@ export const responseProcessor = (req: Request, res: Response) => { mergedConfig, content, site, - }) || ""; + }) || ''; + TRACE && log.debug('add-metadata response processor titleHtml:%s', titleHtml); res.body = res.body.replace(/()(.*?)(<\/title>)/i, titleHtml); titleAdded = true; } @@ -52,7 +61,7 @@ export const responseProcessor = (req: Request, res: Response) => { if (htmlIndex >= 0 && endHtmlIndex >= 0) { const fixedHtmlTagInnerContent = getFixedHtmlAttrsAsString({ htmlTag: res.body, - isFrontpage: site._path === content._path, + isFrontpage: site && content && site._path === content._path, }); res.body = res.body.substr(0, htmlIndex + 5) + " " + fixedHtmlTagInnerContent + res.body.substr(endHtmlIndex); } diff --git a/test/site/processors/add-metadata.test.ts b/test/site/processors/add-metadata.test.ts new file mode 100644 index 0000000..65397bd --- /dev/null +++ b/test/site/processors/add-metadata.test.ts @@ -0,0 +1,139 @@ +import type { Log } from '@enonic/mock-xp'; +import type { + getContent as libPortalGetContent, + getSite as libPortalGetSite, + getSiteConfig as libPortalGetSiteConfig +} from '@enonic-types/lib-portal'; +import type { + Request, + Response +} from '/lib/app-metafields/types'; + + +import { + // LibContent, + // LibContext, + // LibNode, + // LibPortal, + // App, + // Request as MockRequest, + Server +} from '@enonic/mock-xp'; +import { + describe, + expect, + jest, + test as it, +} from '@jest/globals'; + + +const server = new Server({ + loglevel: 'info' +}); + +// Avoid type errors below. +// eslint-disable-next-line @typescript-eslint/no-namespace +declare module globalThis { + let log: Log +} + +globalThis.log = server.log as Log; + +// const app = new App({ key: 'com.enonic.app.metafields' }); +// const libContent = new LibContent({ server }); +// const libContext = new LibContext({ server }); +// const libNode = new LibNode({ server }); +// const libPortal = new LibPortal({ app, server }); + +jest.mock('/lib/xp/content', () => { + return { + } +}, { virtual: true }); + +jest.mock('/lib/xp/context', () => { + return { + } +}, { virtual: true }); + +jest.mock('/lib/xp/node', () => { + return { + } +}, { virtual: true }); + +jest.mock('/lib/xp/portal', () => { + return { + // assetUrl: jest.fn<typeof assetUrlType>((params) => libPortal.assetUrl(params)), + // getContent: jest.fn<typeof libPortalGetContent>(() => libPortal.getContent()), + getContent: jest.fn<typeof libPortalGetContent>(() => null), + // getSite: jest.fn<typeof libPortalGetSite>(() => libPortal.getSite()), + getSite: jest.fn<typeof libPortalGetSite>(() => null), + getSiteConfig: jest.fn<typeof libPortalGetSiteConfig<{}>>(() => ({ + "canonical": true, + "blockRobots": false, + "twitterUsername": "@enonicHQ", + "seoImage": "f7efd6e4-9eed-45d2-83b6-d088a7bdc685", + "titleBehaviour": true, + "titleSeparator": "-", + "titleFrontpageBehaviour": true, + "disableAppConfig": false, + "seoImageIsPrescaled": false, + "frontpageImage": "f7efd6e4-9eed-45d2-83b6-d088a7bdc685", + "frontpageImageIsPrescaled": false, + "removeTwitterImage": false, + "removeOpenGraphUrl": false, + "removeOpenGraphImage": false, + "fullPath": false, + "headless": false + })), + // imageUrl: jest.fn<typeof imageUrlType>((params) => libPortal.imageUrl(params)), + } +}, { virtual: true }); + +jest.mock('/lib/thymeleaf', () => { + render: jest.fn((view, model) => model) +}, { virtual: true }); + +describe('add-metadata', () => { + it('should not throw when there is no content', () => { + import('/site/processors/add-metadata').then(({responseProcessor}) => { + const request: Request = { + "branch": "draft", + "method": "GET", + "scheme": "http", + "host": "localhost", + "port": 8080, + "path": "/admin/site/inline/dev-portal/draft/developer/search", + // "rawPath": "/admin/site/inline/dev-portal/draft/developer/search", + "url": "http://localhost:8080/admin/site/inline/dev-portal/draft/developer/search?q=test", + // "remoteAddress": "127.0.0.1", + "mode": "inline", + // "webSocket": false, + // "repositoryId": "com.enonic.cms.dev-portal", + // "contextPath": "/admin/site/inline/dev-portal/draft/developer", + // "params": { + // "q": "test" + // }, + } + // libPortal.request = new MockRequest(request); + const response: Response = { + "status": 200, + "contentType": "text/html; charset=utf-8", + "postProcess": true, + "headers": {}, + "pageContributions": { + "headEnd": [ + "<script async src=\"/admin/site/inline/dev-portal/draft/_/asset/com.enonic.site.developer:1714396560884/js/main.min.js\"></script>" + ] + }, + "applyFilters": true, + "body": `<!DOCTYPE html><html lang="en"><head><title>Whatnot` + }; + const expected = { + ...response, + body: ``, + }; + const actual = responseProcessor(request, response); + expect(actual).toStrictEqual(expected); + }); + }); +});