From 8ee7580ff2952da241567d564cd9f5f5f8870073 Mon Sep 17 00:00:00 2001 From: Knah Tsaeb Date: Tue, 10 Dec 2024 16:52:55 +0100 Subject: [PATCH] Remove unwanted file --- .dev/.eslintrc.js | 12 - .dev/.stylelintrc.js | 15 - .docker/.htaccess | 13 - .docker/nginx.conf | 60 - .docker/php-fpm.conf | 16 - .docker/services.d/.s6-svscan/finish | 2 - .docker/services.d/nginx/run | 2 - .docker/services.d/php-fpm/run | 2 - .dockerignore | 64 - .editorconfig | 23 - .gitattributes | 47 - .github/mailmap | 22 - .github/workflows/ci.yml | 106 - .github/workflows/docker-latest.yml | 45 - .github/workflows/docker-pr.yml | 21 - .github/workflows/docker-tags.yml | 43 - Dockerfile | 74 - Makefile | 215 - docker-compose.yml | 62 - package-lock.json | 7486 ----------------- phpunit.xml | 27 - tests/.htaccess | 13 - tests/HistoryTest.php | 206 - tests/LanguagesTest.php | 229 - tests/PluginManagerTest.php | 178 - tests/TestCase.php | 77 - tests/ThumbnailerTest.php | 115 - tests/TimeZoneTest.php | 104 - tests/UtilsTest.php | 463 - tests/api/ApiMiddlewareTest.php | 262 - tests/api/ApiUtilsTest.php | 354 - tests/api/controllers/history/HistoryTest.php | 215 - tests/api/controllers/info/InfoTest.php | 131 - .../api/controllers/links/DeleteLinkTest.php | 153 - tests/api/controllers/links/GetLinkIdTest.php | 147 - tests/api/controllers/links/GetLinksTest.php | 492 -- tests/api/controllers/links/PostLinkTest.php | 289 - tests/api/controllers/links/PutLinkTest.php | 292 - tests/api/controllers/tags/DeleteTagTest.php | 198 - tests/api/controllers/tags/GetTagNameTest.php | 147 - tests/api/controllers/tags/GetTagsTest.php | 227 - tests/api/controllers/tags/PutTagTest.php | 225 - tests/bookmark/BookmarkArrayTest.php | 224 - tests/bookmark/BookmarkFileServiceTest.php | 1273 --- tests/bookmark/BookmarkFilterTest.php | 554 -- tests/bookmark/BookmarkInitializerTest.php | 185 - tests/bookmark/BookmarkTest.php | 468 -- tests/bookmark/LinkUtilsTest.php | 767 -- tests/bookmark/SearchResultTest.php | 125 - tests/bootstrap.php | 45 - tests/config/ConfigJsonTest.php | 121 - tests/config/ConfigManagerTest.php | 197 - tests/config/ConfigPhpTest.php | 100 - tests/config/ConfigPluginTest.php | 133 - tests/container/ContainerBuilderTest.php | 98 - tests/container/ShaarliTestContainer.php | 41 - tests/docker/alpine316/Dockerfile | 36 - tests/docker/debian8/Dockerfile | 35 - tests/docker/debian9/Dockerfile | 36 - tests/docker/ubuntu16/Dockerfile | 36 - tests/feed/CachedPageTest.php | 162 - tests/feed/FeedBuilderTest.php | 304 - .../BookmarkDefaultFormatterTest.php | 319 - .../BookmarkMarkdownExtraFormatterTest.php | 162 - .../BookmarkMarkdownFormatterTest.php | 203 - tests/formatter/BookmarkRawFormatterTest.php | 97 - tests/formatter/FormatterFactoryTest.php | 101 - tests/front/ShaarliAdminMiddlewareTest.php | 101 - tests/front/ShaarliMiddlewareTest.php | 222 - .../admin/ConfigureControllerTest.php | 250 - .../controller/admin/ExportControllerTest.php | 166 - .../admin/FrontAdminControllerMockHelper.php | 56 - .../controller/admin/ImportControllerTest.php | 151 - .../controller/admin/LogoutControllerTest.php | 50 - .../admin/ManageTagControllerTest.php | 409 - .../admin/PasswordControllerTest.php | 203 - .../admin/PluginsControllerTest.php | 209 - .../controller/admin/ServerControllerTest.php | 184 - .../admin/SessionFilterControllerTest.php | 177 - .../admin/ShaareAddControllerTest.php | 97 - .../AddOrDeleteTagTest.php | 380 - .../ChangeVisibilityBookmarkTest.php | 418 - .../DeleteBookmarkTest.php | 427 - .../PinBookmarkTest.php | 145 - .../SharePrivateTest.php | 139 - .../DisplayCreateBatchFormTest.php | 63 - .../DisplayCreateFormTest.php | 367 - .../DisplayEditFormTest.php | 155 - .../SaveBookmarkTest.php | 368 - .../admin/ShaarliAdminControllerTest.php | 184 - .../admin/ThumbnailsControllerTest.php | 157 - .../controller/admin/TokenControllerTest.php | 41 - .../controller/admin/ToolsControllerTest.php | 69 - .../visitor/BookmarkListControllerTest.php | 538 -- .../visitor/DailyControllerTest.php | 726 -- .../visitor/ErrorControllerTest.php | 98 - .../visitor/ErrorNotFoundControllerTest.php | 81 - .../controller/visitor/FeedControllerTest.php | 151 - .../visitor/FrontControllerMockHelper.php | 122 - .../visitor/InstallControllerTest.php | 304 - .../visitor/LoginControllerTest.php | 404 - .../visitor/OpenSearchControllerTest.php | 44 - .../visitor/PictureWallControllerTest.php | 124 - .../PublicSessionFilterControllerTest.php | 122 - .../visitor/ShaarliVisitorControllerTest.php | 249 - .../visitor/TagCloudControllerTest.php | 381 - .../controller/visitor/TagControllerTest.php | 215 - tests/helper/ApplicationUtilsTest.php | 414 - tests/helper/DailyPageHelperTest.php | 344 - tests/helper/FileUtilsTest.php | 197 - tests/http/HttpUtils/ClientIpIdTest.php | 53 - tests/http/HttpUtils/GetHttpUrlTest.php | 66 - .../HttpUtils/GetIpAdressFromProxyTest.php | 58 - tests/http/HttpUtils/IndexUrlTest.php | 139 - .../HttpUtils/IndexUrlTestWithConstant.php | 51 - tests/http/HttpUtils/IsHttpsTest.php | 36 - tests/http/HttpUtils/PageUrlTest.php | 77 - tests/http/HttpUtils/ServerUrlTest.php | 222 - tests/http/MetadataRetrieverTest.php | 161 - tests/http/UrlTest.php | 201 - tests/http/UrlUtils/CleanupUrlTest.php | 110 - tests/http/UrlUtils/GetUrlSchemeTest.php | 33 - tests/http/UrlUtils/UnparseUrlTest.php | 33 - .../http/UrlUtils/WhitelistProtocolsTest.php | 63 - tests/languages/bootstrap.php | 7 - tests/languages/de/UtilsDeTest.php | 135 - tests/languages/en/UtilsEnTest.php | 135 - tests/languages/fr/LanguagesFrTest.php | 201 - tests/languages/fr/UtilsFrTest.php | 135 - tests/legacy/LegacyControllerTest.php | 101 - tests/legacy/LegacyDummyUpdater.php | 75 - tests/legacy/LegacyLinkDBTest.php | 664 -- tests/legacy/LegacyLinkFilterTest.php | 510 -- tests/legacy/LegacyUpdaterTest.php | 885 -- tests/netscape/BookmarkExportTest.php | 215 - tests/netscape/BookmarkImportTest.php | 665 -- tests/netscape/input/empty.htm | 0 .../input/internet_explorer_encoding.htm | 9 - tests/netscape/input/lowercase_doctype.htm | 8 - tests/netscape/input/netscape_basic.htm | 11 - tests/netscape/input/netscape_nested.htm | 31 - tests/netscape/input/no_doctype.htm | 7 - tests/netscape/input/same_date.htm | 11 - tests/plugins/PluginAddlinkTest.php | 63 - tests/plugins/PluginArchiveorgTest.php | 154 - tests/plugins/PluginDefaultColorsTest.php | 219 - tests/plugins/PluginIssoTest.php | 172 - tests/plugins/PluginPlayvideosTest.php | 61 - tests/plugins/PluginPubsubhubbubTest.php | 59 - tests/plugins/PluginQrcodeTest.php | 68 - tests/plugins/PluginReadItLaterTest.php | 419 - tests/plugins/PluginWallabagTest.php | 96 - tests/plugins/WallabagInstanceTest.php | 60 - tests/plugins/test/test.meta | 4 - tests/plugins/test/test.php | 52 - .../test_route_invalid/test_route_invalid.php | 12 - tests/render/PageCacheManagerTest.php | 85 - tests/render/ThemeUtilsTest.php | 55 - tests/security/BanManagerTest.php | 393 - tests/security/LoginManagerTest.php | 374 - tests/security/SessionManagerTest.php | 328 - tests/updater/DummyUpdater.php | 76 - tests/updater/UpdaterTest.php | 232 - tests/utils/CurlUtils.php | 95 - tests/utils/FakeApplicationUtils.php | 21 - tests/utils/FakeBookmarkService.php | 13 - tests/utils/FakeConfigManager.php | 61 - tests/utils/RainTPL.php | 6 - tests/utils/ReferenceHistory.php | 88 - tests/utils/ReferenceLinkDB.php | 293 - tests/utils/ReferenceSessionIdHashes.php | 58 - tests/utils/config/configInvalid.json.php | 5 - tests/utils/config/configJson.json.php | 84 - tests/utils/config/configPhp.php | 15 - tests/utils/config/emptyConfigPhp.php | 1 - tests/utils/config/wt.json | 12 - .../dummy/language/fr/LC_MESSAGES/dummy.mo | Bin 431 -> 0 bytes .../dummy/language/fr/LC_MESSAGES/dummy.po | 16 - tests/utils/languages/fr/LC_MESSAGES/test.mo | Bin 456 -> 0 bytes tests/utils/languages/fr/LC_MESSAGES/test.po | 19 - 180 files changed, 37275 deletions(-) delete mode 100644 .dev/.eslintrc.js delete mode 100644 .dev/.stylelintrc.js delete mode 100644 .docker/.htaccess delete mode 100644 .docker/nginx.conf delete mode 100644 .docker/php-fpm.conf delete mode 100755 .docker/services.d/.s6-svscan/finish delete mode 100755 .docker/services.d/nginx/run delete mode 100755 .docker/services.d/php-fpm/run delete mode 100644 .dockerignore delete mode 100644 .editorconfig delete mode 100644 .gitattributes delete mode 100644 .github/mailmap delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/docker-latest.yml delete mode 100644 .github/workflows/docker-pr.yml delete mode 100644 .github/workflows/docker-tags.yml delete mode 100644 Dockerfile delete mode 100644 Makefile delete mode 100644 docker-compose.yml delete mode 100644 package-lock.json delete mode 100644 phpunit.xml delete mode 100644 tests/.htaccess delete mode 100644 tests/HistoryTest.php delete mode 100644 tests/LanguagesTest.php delete mode 100644 tests/PluginManagerTest.php delete mode 100644 tests/TestCase.php delete mode 100644 tests/ThumbnailerTest.php delete mode 100644 tests/TimeZoneTest.php delete mode 100644 tests/UtilsTest.php delete mode 100644 tests/api/ApiMiddlewareTest.php delete mode 100644 tests/api/ApiUtilsTest.php delete mode 100644 tests/api/controllers/history/HistoryTest.php delete mode 100644 tests/api/controllers/info/InfoTest.php delete mode 100644 tests/api/controllers/links/DeleteLinkTest.php delete mode 100644 tests/api/controllers/links/GetLinkIdTest.php delete mode 100644 tests/api/controllers/links/GetLinksTest.php delete mode 100644 tests/api/controllers/links/PostLinkTest.php delete mode 100644 tests/api/controllers/links/PutLinkTest.php delete mode 100644 tests/api/controllers/tags/DeleteTagTest.php delete mode 100644 tests/api/controllers/tags/GetTagNameTest.php delete mode 100644 tests/api/controllers/tags/GetTagsTest.php delete mode 100644 tests/api/controllers/tags/PutTagTest.php delete mode 100644 tests/bookmark/BookmarkArrayTest.php delete mode 100644 tests/bookmark/BookmarkFileServiceTest.php delete mode 100644 tests/bookmark/BookmarkFilterTest.php delete mode 100644 tests/bookmark/BookmarkInitializerTest.php delete mode 100644 tests/bookmark/BookmarkTest.php delete mode 100644 tests/bookmark/LinkUtilsTest.php delete mode 100644 tests/bookmark/SearchResultTest.php delete mode 100644 tests/bootstrap.php delete mode 100644 tests/config/ConfigJsonTest.php delete mode 100644 tests/config/ConfigManagerTest.php delete mode 100644 tests/config/ConfigPhpTest.php delete mode 100644 tests/config/ConfigPluginTest.php delete mode 100644 tests/container/ContainerBuilderTest.php delete mode 100644 tests/container/ShaarliTestContainer.php delete mode 100644 tests/docker/alpine316/Dockerfile delete mode 100644 tests/docker/debian8/Dockerfile delete mode 100644 tests/docker/debian9/Dockerfile delete mode 100644 tests/docker/ubuntu16/Dockerfile delete mode 100644 tests/feed/CachedPageTest.php delete mode 100644 tests/feed/FeedBuilderTest.php delete mode 100644 tests/formatter/BookmarkDefaultFormatterTest.php delete mode 100644 tests/formatter/BookmarkMarkdownExtraFormatterTest.php delete mode 100644 tests/formatter/BookmarkMarkdownFormatterTest.php delete mode 100644 tests/formatter/BookmarkRawFormatterTest.php delete mode 100644 tests/formatter/FormatterFactoryTest.php delete mode 100644 tests/front/ShaarliAdminMiddlewareTest.php delete mode 100644 tests/front/ShaarliMiddlewareTest.php delete mode 100644 tests/front/controller/admin/ConfigureControllerTest.php delete mode 100644 tests/front/controller/admin/ExportControllerTest.php delete mode 100644 tests/front/controller/admin/FrontAdminControllerMockHelper.php delete mode 100644 tests/front/controller/admin/ImportControllerTest.php delete mode 100644 tests/front/controller/admin/LogoutControllerTest.php delete mode 100644 tests/front/controller/admin/ManageTagControllerTest.php delete mode 100644 tests/front/controller/admin/PasswordControllerTest.php delete mode 100644 tests/front/controller/admin/PluginsControllerTest.php delete mode 100644 tests/front/controller/admin/ServerControllerTest.php delete mode 100644 tests/front/controller/admin/SessionFilterControllerTest.php delete mode 100644 tests/front/controller/admin/ShaareAddControllerTest.php delete mode 100644 tests/front/controller/admin/ShaareManageControllerTest/AddOrDeleteTagTest.php delete mode 100644 tests/front/controller/admin/ShaareManageControllerTest/ChangeVisibilityBookmarkTest.php delete mode 100644 tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php delete mode 100644 tests/front/controller/admin/ShaareManageControllerTest/PinBookmarkTest.php delete mode 100644 tests/front/controller/admin/ShaareManageControllerTest/SharePrivateTest.php delete mode 100644 tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateBatchFormTest.php delete mode 100644 tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateFormTest.php delete mode 100644 tests/front/controller/admin/ShaarePublishControllerTest/DisplayEditFormTest.php delete mode 100644 tests/front/controller/admin/ShaarePublishControllerTest/SaveBookmarkTest.php delete mode 100644 tests/front/controller/admin/ShaarliAdminControllerTest.php delete mode 100644 tests/front/controller/admin/ThumbnailsControllerTest.php delete mode 100644 tests/front/controller/admin/TokenControllerTest.php delete mode 100644 tests/front/controller/admin/ToolsControllerTest.php delete mode 100644 tests/front/controller/visitor/BookmarkListControllerTest.php delete mode 100644 tests/front/controller/visitor/DailyControllerTest.php delete mode 100644 tests/front/controller/visitor/ErrorControllerTest.php delete mode 100644 tests/front/controller/visitor/ErrorNotFoundControllerTest.php delete mode 100644 tests/front/controller/visitor/FeedControllerTest.php delete mode 100644 tests/front/controller/visitor/FrontControllerMockHelper.php delete mode 100644 tests/front/controller/visitor/InstallControllerTest.php delete mode 100644 tests/front/controller/visitor/LoginControllerTest.php delete mode 100644 tests/front/controller/visitor/OpenSearchControllerTest.php delete mode 100644 tests/front/controller/visitor/PictureWallControllerTest.php delete mode 100644 tests/front/controller/visitor/PublicSessionFilterControllerTest.php delete mode 100644 tests/front/controller/visitor/ShaarliVisitorControllerTest.php delete mode 100644 tests/front/controller/visitor/TagCloudControllerTest.php delete mode 100644 tests/front/controller/visitor/TagControllerTest.php delete mode 100644 tests/helper/ApplicationUtilsTest.php delete mode 100644 tests/helper/DailyPageHelperTest.php delete mode 100644 tests/helper/FileUtilsTest.php delete mode 100644 tests/http/HttpUtils/ClientIpIdTest.php delete mode 100644 tests/http/HttpUtils/GetHttpUrlTest.php delete mode 100644 tests/http/HttpUtils/GetIpAdressFromProxyTest.php delete mode 100644 tests/http/HttpUtils/IndexUrlTest.php delete mode 100644 tests/http/HttpUtils/IndexUrlTestWithConstant.php delete mode 100644 tests/http/HttpUtils/IsHttpsTest.php delete mode 100644 tests/http/HttpUtils/PageUrlTest.php delete mode 100644 tests/http/HttpUtils/ServerUrlTest.php delete mode 100644 tests/http/MetadataRetrieverTest.php delete mode 100644 tests/http/UrlTest.php delete mode 100644 tests/http/UrlUtils/CleanupUrlTest.php delete mode 100644 tests/http/UrlUtils/GetUrlSchemeTest.php delete mode 100644 tests/http/UrlUtils/UnparseUrlTest.php delete mode 100644 tests/http/UrlUtils/WhitelistProtocolsTest.php delete mode 100644 tests/languages/bootstrap.php delete mode 100644 tests/languages/de/UtilsDeTest.php delete mode 100644 tests/languages/en/UtilsEnTest.php delete mode 100644 tests/languages/fr/LanguagesFrTest.php delete mode 100644 tests/languages/fr/UtilsFrTest.php delete mode 100644 tests/legacy/LegacyControllerTest.php delete mode 100644 tests/legacy/LegacyDummyUpdater.php delete mode 100644 tests/legacy/LegacyLinkDBTest.php delete mode 100644 tests/legacy/LegacyLinkFilterTest.php delete mode 100644 tests/legacy/LegacyUpdaterTest.php delete mode 100644 tests/netscape/BookmarkExportTest.php delete mode 100644 tests/netscape/BookmarkImportTest.php delete mode 100644 tests/netscape/input/empty.htm delete mode 100644 tests/netscape/input/internet_explorer_encoding.htm delete mode 100644 tests/netscape/input/lowercase_doctype.htm delete mode 100644 tests/netscape/input/netscape_basic.htm delete mode 100644 tests/netscape/input/netscape_nested.htm delete mode 100644 tests/netscape/input/no_doctype.htm delete mode 100644 tests/netscape/input/same_date.htm delete mode 100644 tests/plugins/PluginAddlinkTest.php delete mode 100644 tests/plugins/PluginArchiveorgTest.php delete mode 100644 tests/plugins/PluginDefaultColorsTest.php delete mode 100644 tests/plugins/PluginIssoTest.php delete mode 100644 tests/plugins/PluginPlayvideosTest.php delete mode 100644 tests/plugins/PluginPubsubhubbubTest.php delete mode 100644 tests/plugins/PluginQrcodeTest.php delete mode 100644 tests/plugins/PluginReadItLaterTest.php delete mode 100644 tests/plugins/PluginWallabagTest.php delete mode 100644 tests/plugins/WallabagInstanceTest.php delete mode 100644 tests/plugins/test/test.meta delete mode 100644 tests/plugins/test/test.php delete mode 100644 tests/plugins/test_route_invalid/test_route_invalid.php delete mode 100644 tests/render/PageCacheManagerTest.php delete mode 100644 tests/render/ThemeUtilsTest.php delete mode 100644 tests/security/BanManagerTest.php delete mode 100644 tests/security/LoginManagerTest.php delete mode 100644 tests/security/SessionManagerTest.php delete mode 100644 tests/updater/DummyUpdater.php delete mode 100644 tests/updater/UpdaterTest.php delete mode 100644 tests/utils/CurlUtils.php delete mode 100644 tests/utils/FakeApplicationUtils.php delete mode 100644 tests/utils/FakeBookmarkService.php delete mode 100644 tests/utils/FakeConfigManager.php delete mode 100644 tests/utils/RainTPL.php delete mode 100644 tests/utils/ReferenceHistory.php delete mode 100644 tests/utils/ReferenceLinkDB.php delete mode 100644 tests/utils/ReferenceSessionIdHashes.php delete mode 100644 tests/utils/config/configInvalid.json.php delete mode 100644 tests/utils/config/configJson.json.php delete mode 100644 tests/utils/config/configPhp.php delete mode 100644 tests/utils/config/emptyConfigPhp.php delete mode 100644 tests/utils/config/wt.json delete mode 100644 tests/utils/customtpl/dummy/language/fr/LC_MESSAGES/dummy.mo delete mode 100644 tests/utils/customtpl/dummy/language/fr/LC_MESSAGES/dummy.po delete mode 100644 tests/utils/languages/fr/LC_MESSAGES/test.mo delete mode 100644 tests/utils/languages/fr/LC_MESSAGES/test.po diff --git a/.dev/.eslintrc.js b/.dev/.eslintrc.js deleted file mode 100644 index 151b785b..00000000 --- a/.dev/.eslintrc.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - "extends": "airbnb-base", - "env": { - "browser": true, - }, - "rules": { - "no-param-reassign": 0, // manipulate DOM style properties - "no-restricted-globals": 0, // currently Shaarli uses alert/confirm, could be be improved later - "no-alert": 0, // currently Shaarli uses alert/confirm, could be be improved later - "no-cond-assign": [2, "except-parens"], // assignment in while loops is readable and avoid assignment duplication - } -}; diff --git a/.dev/.stylelintrc.js b/.dev/.stylelintrc.js deleted file mode 100644 index a754e33b..00000000 --- a/.dev/.stylelintrc.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - extends: 'stylelint-config-standard', - plugins: [ - "stylelint-scss" - ], - rules: { - "indentation": [2], - "number-leading-zero": null, - // Replace CSS @ with SASS ones - "at-rule-no-unknown": null, - "scss/at-rule-no-unknown": true, - // not compatible with SASS apparently - "no-descending-specificity": null - }, -} diff --git a/.docker/.htaccess b/.docker/.htaccess deleted file mode 100644 index f601c1ee..00000000 --- a/.docker/.htaccess +++ /dev/null @@ -1,13 +0,0 @@ - - = 2.4> - Require all denied - - - Allow from none - Deny from all - - - - - Require all denied - diff --git a/.docker/nginx.conf b/.docker/nginx.conf deleted file mode 100644 index ca4789ce..00000000 --- a/.docker/nginx.conf +++ /dev/null @@ -1,60 +0,0 @@ -user nginx nginx; -daemon off; -worker_processes 4; -pid /var/run/nginx.pid; - -events { - worker_connections 768; -} - -http { - include mime.types; - default_type application/octet-stream; - keepalive_timeout 20; - - client_max_body_size 10m; - - index index.html index.php; - - server { - listen 80; - listen [::]:80; - root /var/www/shaarli; - - access_log /var/log/nginx/shaarli.access.log; - error_log /var/log/nginx/shaarli.error.log; - - location ~* \.(?:ico|css|js|gif|jpe?g|png|ttf|oet|woff2?)$ { - # cache static assets - expires max; - add_header Pragma public; - add_header Cache-Control "public, must-revalidate, proxy-revalidate"; - } - - location = /favicon.ico { - # serve the Shaarli favicon from its custom location - alias /var/www/shaarli/images/favicon.ico; - } - - location /doc/html/ { - default_type "text/html"; - try_files $uri $uri/ $uri.html =404; - } - - location / { - # Slim - rewrite URLs & do NOT serve static files through this location - try_files _ /index.php$is_args$args; - } - - location ~ index\.php$ { - # Slim - split URL path into (script_filename, path_info) - try_files $uri =404; - fastcgi_split_path_info ^(index.php)(/.+)$; - - # filter and proxy PHP requests to PHP-FPM - fastcgi_pass unix:/var/run/php-fpm.sock; - fastcgi_index index.php; - include fastcgi.conf; - } - } -} diff --git a/.docker/php-fpm.conf b/.docker/php-fpm.conf deleted file mode 100644 index 0843c164..00000000 --- a/.docker/php-fpm.conf +++ /dev/null @@ -1,16 +0,0 @@ -[global] -daemonize = no - -[www] -user = nginx -group = nginx -listen.owner = nginx -listen.group = nginx -catch_workers_output = yes -listen = /var/run/php-fpm.sock -pm = dynamic -pm.max_children = 20 -pm.start_servers = 1 -pm.min_spare_servers = 1 -pm.max_spare_servers = 3 -pm.max_requests = 2048 diff --git a/.docker/services.d/.s6-svscan/finish b/.docker/services.d/.s6-svscan/finish deleted file mode 100755 index 1dadeeaf..00000000 --- a/.docker/services.d/.s6-svscan/finish +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -/bin/true diff --git a/.docker/services.d/nginx/run b/.docker/services.d/nginx/run deleted file mode 100755 index 21e7b0d6..00000000 --- a/.docker/services.d/nginx/run +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/execlineb -P -nginx diff --git a/.docker/services.d/php-fpm/run b/.docker/services.d/php-fpm/run deleted file mode 100755 index e46138db..00000000 --- a/.docker/services.d/php-fpm/run +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/execlineb -P -php-fpm8 -F diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 1fba241e..00000000 --- a/.dockerignore +++ /dev/null @@ -1,64 +0,0 @@ -# Docker-ignore -.dev -.git -.github -.gitattributes -.gitignore -tests - -# Docker related resources are not needed inside the container -.dockerignore -Dockerfile -Dockerfile.armhf - -# Docker Compose resources -docker-compose.yml - -# Shaarli runtime resources -cache/* -data/* -pagecache/* -tmp/* - -# Shaarli's docs are created during the build -doc/html/ - -# Eclipse project files -.settings -.buildpath -.project - -# Raintpl generated pages -*.rtpl.php - -# 3rd-party dependencies -vendor/ - -# Release archives -*.tar.gz -*.zip -inc/languages/*/LC_MESSAGES/shaarli.mo - -# Development and test resources -coverage -doxygen -sandbox -phpmd.html - -# User plugin configuration -plugins/*/config.php - -# 3rd party themes -tpl/* -!tpl/default -!tpl/vintage - -# Front end -node_modules -tpl/default/js -tpl/default/css -tpl/default/fonts -tpl/default/img -tpl/vintage/js -tpl/vintage/css -tpl/vintage/img diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index c2ab80eb..00000000 --- a/.editorconfig +++ /dev/null @@ -1,23 +0,0 @@ -# EditorConfig: http://EditorConfig.org - -root = true - -[*] -charset = utf-8 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true -indent_style = space -indent_size = 4 - -[*.{htaccess,html,scss,js,json,xml,yml}] -indent_size = 2 - -[*.php] -max_line_length = 120 - -[Dockerfile] -max_line_length = 80 - -[Makefile] -indent_style = tab diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 5552e13f..00000000 --- a/.gitattributes +++ /dev/null @@ -1,47 +0,0 @@ -# Set default behavior -* text=auto eol=lf - -# Ensure sources are processed -*.conf text -*.css text -*.html text diff=html -*.js text -*.md text -*.php text diff=php -Dockerfile text - -# Do not alter images nor minified scripts nor fonts -*.ico binary -*.jpg binary -*.png binary -*.svg binary -*.otf binary -*.eot binary -*.woff binary -*.woff2 binary -*.ttf binary -*.min.css binary -*.min.js binary -*.mo binary - -# Exclude from Git archives -.editorconfig export-ignore -.dev export-ignore -.gitattributes export-ignore -.github export-ignore -.gitignore export-ignore -.travis.yml export-ignore -doc/**/*.json export-ignore -doc/**/*.md export-ignore -.docker/ export-ignore -.dockerignore export-ignore -docker-compose.* export-ignore -Dockerfile* export-ignore -Doxyfile export-ignore -Makefile export-ignore -node_modules/ export-ignore -doc/conf.py export-ignore -doc/requirements.txt export-ignore -doc/html/.doctrees/ export-ignore -phpunit.xml export-ignore -tests/ export-ignore diff --git a/.github/mailmap b/.github/mailmap deleted file mode 100644 index 15a25e43..00000000 --- a/.github/mailmap +++ /dev/null @@ -1,22 +0,0 @@ -ArthurHoaro -ArthurHoaro Arthur -Florian Eula feula -Florian Eula -Immánuel Fodor -Immánuel Fodor Immánuel! <21174107+immanuelfodor@users.noreply.github.com> -kalvn -kalvn -Neros -Nicolas Danelon nicolasm -Nicolas Danelon -Nicolas Danelon -Nicolas Danelon -Sébastien Sauvage -Sébastien NOBILI -Timo Van Neerden -Timo Van Neerden lehollandaisvolant -VirtualTam -VirtualTam -VirtualTam -Willi Eggeling -Willi Eggeling diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 378b9512..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,106 +0,0 @@ -name: Shaarli CI -on: [push, pull_request] -jobs: - php: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - php-versions: ['7.4', '8.0', '8.1', '8.2'] - name: PHP ${{ matrix.php-versions }} - steps: - - name: Set locales - run: | - sudo locale-gen de_DE.utf8 && \ - sudo locale-gen en_US.utf8 && \ - sudo locale-gen fr_FR.utf8 && \ - sudo dpkg-reconfigure --frontend=noninteractive locales - - - name: Install Gettext - run: sudo apt-get install gettext - - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - extensions: gd, xml, curl, mbstring, intl, gettext - tools: composer:v2 - - - name: Check PHP version - run: php -v - - - name: Setup Composer from PHP version + update - run: composer config --unset platform && composer config platform.php ${{ matrix.php-versions }} - - - name: Update dependencies for PHP 8.x - if: ${{ matrix.php-versions == '8.0' || matrix.php-versions == '8.1' }} - run: | - composer update && \ - composer remove --dev phpunit/phpunit && \ - composer require --dev phpunit/php-text-template ^2.0 && \ - composer require --dev phpunit/phpunit ^9.0 - - - name: Update dependencies for PHP 7.x - if: ${{ matrix.php-versions != '8.0' && matrix.php-versions != '8.1' }} - run: composer update - - - name: Clean up - run: make clean - - - name: Check permissions - run: make check_permissions - - - name: Run PHPCS - run: make code_sniffer - - - name: Run tests - run: make all_tests - - node: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: '14.x' - - - name: Yarn install - run: yarnpkg install - - - name: Verify successful frontend builds - run: yarnpkg run build - - - name: JS static analysis - run: make eslint - - - name: Linter for SASS syntax - run: make sasslint - - python: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - - name: Build documentation - run: make htmldoc - - trivy-repo: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Run trivy scanner on repository (non-blocking) - run: make test_trivy_repo TRIVY_EXIT_CODE=0 diff --git a/.github/workflows/docker-latest.yml b/.github/workflows/docker-latest.yml deleted file mode 100644 index ad885b6a..00000000 --- a/.github/workflows/docker-latest.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Build/push Docker image (master/latest) -on: - push: - branches: [ master ] -jobs: - docker-build: - runs-on: ubuntu-latest - steps: - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Login to GitHub Container Registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Checkout - uses: actions/checkout@v3 - - - name: Set shaarli version to the latest commit hash - run: sed -i "s/dev/$(git rev-parse --short HEAD)/" shaarli_version.php - - - name: Build and push - id: docker_build - uses: docker/build-push-action@v4 - with: - context: . - push: true - platforms: linux/amd64,linux/arm64,linux/arm/v7 - tags: | - ${{ secrets.DOCKER_IMAGE }}:latest - ghcr.io/${{ secrets.DOCKER_IMAGE }}:latest - - name: Image digest - run: echo ${{ steps.docker_build.outputs.digest }} - - name: Run trivy scanner on latest docker image - run: make test_trivy_docker TRIVY_TARGET_DOCKER_IMAGE=ghcr.io/${{ secrets.DOCKER_IMAGE }}:latest diff --git a/.github/workflows/docker-pr.yml b/.github/workflows/docker-pr.yml deleted file mode 100644 index 16adede9..00000000 --- a/.github/workflows/docker-pr.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Build Docker image (Pull Request) -on: - pull_request: - branches: [ master ] - -jobs: - docker-build: - runs-on: ubuntu-latest - steps: - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Build Docker image - id: docker_build - uses: docker/build-push-action@v2 - with: - push: false - tags: shaarli/shaarli:pr-${{ github.event.number }} - - name: Image digest - run: echo ${{ steps.docker_build.outputs.digest }} diff --git a/.github/workflows/docker-tags.yml b/.github/workflows/docker-tags.yml deleted file mode 100644 index b9412f52..00000000 --- a/.github/workflows/docker-tags.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Build/push Docker image (tags/releases) -on: - push: - tags: - - "v*.*.*" - branches: - - "v*.*" - - release -jobs: - docker-build: - runs-on: ubuntu-latest - steps: - - name: Get the tag name - run: echo "REF=${GITHUB_REF##*/}" >> $GITHUB_ENV - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Login to GitHub Container Registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push - id: docker_build - uses: docker/build-push-action@v3 - with: - push: true - platforms: linux/amd64,linux/arm/v7 - tags: | - ${{ secrets.DOCKER_IMAGE }}:${{ env.REF }} - ghcr.io/${{ secrets.DOCKER_IMAGE }}:${{ env.REF }} - - name: Image digest - run: echo ${{ steps.docker_build.outputs.digest }} diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 8e995a6a..00000000 --- a/Dockerfile +++ /dev/null @@ -1,74 +0,0 @@ -# Stage 1: -# - Copy Shaarli sources -# - Build documentation -FROM python:3-alpine as docs -ADD . /usr/src/app/shaarli -RUN cd /usr/src/app/shaarli \ - && apk add --no-cache gcc musl-dev make bash \ - && make htmldoc - -# Stage 2: -# - Resolve PHP dependencies with Composer -FROM composer:latest as composer -COPY --from=docs /usr/src/app/shaarli /app/shaarli -RUN cd shaarli \ - && composer --prefer-dist --no-dev install - -# Stage 3: -# - Frontend dependencies -FROM node:12-alpine as node -COPY --from=composer /app/shaarli shaarli -RUN cd shaarli \ - && yarnpkg install \ - && yarnpkg run build \ - && rm -rf node_modules - -# Stage 4: -# - Shaarli image -FROM alpine:3.16.7 -LABEL maintainer="Shaarli Community" - -RUN apk --update --no-cache add \ - ca-certificates \ - nginx \ - php8 \ - php8-ctype \ - php8-curl \ - php8-fpm \ - php8-gd \ - php8-gettext \ - php8-iconv \ - php8-intl \ - php8-json \ - php8-ldap \ - php8-mbstring \ - php8-openssl \ - php8-session \ - php8-xml \ - php8-simplexml \ - php8-zlib \ - s6 - -COPY .docker/nginx.conf /etc/nginx/nginx.conf -COPY .docker/php-fpm.conf /etc/php8/php-fpm.conf -COPY .docker/services.d /etc/services.d - -RUN rm -rf /etc/php8/php-fpm.d/www.conf \ - && sed -i 's/post_max_size.*/post_max_size = 10M/' /etc/php8/php.ini \ - && sed -i 's/upload_max_filesize.*/upload_max_filesize = 10M/' /etc/php8/php.ini - - -WORKDIR /var/www -COPY --from=node /shaarli shaarli - -RUN chown -R nginx:nginx . \ - && ln -sf /dev/stdout /var/log/nginx/shaarli.access.log \ - && ln -sf /dev/stderr /var/log/nginx/shaarli.error.log - -VOLUME /var/www/shaarli/cache -VOLUME /var/www/shaarli/data - -EXPOSE 80 - -ENTRYPOINT ["/bin/s6-svscan", "/etc/services.d"] -CMD [] diff --git a/Makefile b/Makefile deleted file mode 100644 index 8f8d335b..00000000 --- a/Makefile +++ /dev/null @@ -1,215 +0,0 @@ -# The personal, minimalist, super fast, database-free, bookmarking service. -# Makefile for PHP code analysis & testing, documentation and release generation - -BIN = vendor/bin - -all: check_permissions test - -## -# Docker test adapter -# -# Shaarli sources and vendored libraries are copied from a shared volume -# to a user-owned directory to enable running tests as a non-root user. -## -docker_%: - rsync -az /shaarli/ ~/shaarli/ - cd ~/shaarli && make $* - -## -# PHP_CodeSniffer -# Detects PHP syntax errors -# Documentation (usage, output formatting): -# - http://pear.php.net/manual/en/package.php.php-codesniffer.usage.php -# - http://pear.php.net/manual/en/package.php.php-codesniffer.reporting.php -## -PHPCS := $(BIN)/phpcs - -# Use GNU Tar where available -ifneq (, $(shell which gtar)) -TAR := gtar -else -TAR := tar -endif - -code_sniffer: - @$(PHPCS) - -### - errors by Git author -code_sniffer_blame: - @$(PHPCS) --report-gitblame - -### - all errors/warnings -code_sniffer_full: - @$(PHPCS) --report-full --report-width=200 - -### - errors grouped by kind -code_sniffer_source: - @$(PHPCS) --report-source || exit 0 - -## -# Checks source file & script permissions -## -check_permissions: - @echo "----------------------" - @echo "Check file permissions" - @echo "----------------------" - @for file in `git ls-files | grep -v docker`; do \ - if [ -x $$file ]; then \ - errors=true; \ - echo "$${file} is executable"; \ - fi \ - done; [ -z $$errors ] || false - -## -# PHPUnit -# Runs unitary and functional tests -# Generates an HTML coverage report if Xdebug is enabled -# -# See phpunit.xml for configuration -# https://phpunit.de/manual/current/en/appendixes.configuration.html -## -test: translate - @echo "-------" - @echo "PHPUNIT" - @echo "-------" - @mkdir -p sandbox coverage - @$(BIN)/phpunit --coverage-php coverage/main.cov --bootstrap tests/bootstrap.php --testsuite unit-tests - -locale_test_%: - @UT_LOCALE=$*.utf8 \ - $(BIN)/phpunit \ - --coverage-php coverage/$(firstword $(subst _, ,$*)).cov \ - --bootstrap tests/languages/bootstrap.php \ - --testsuite language-$(firstword $(subst _, ,$*)) - -all_tests: test locale_test_de_DE locale_test_en_US locale_test_fr_FR - @# --The current version is not compatible with PHP 7.2 - @#$(BIN)/phpcov merge --html coverage coverage - @# --text doesn't work with phpunit 4.* (v5 requires PHP 5.6) - @#$(BIN)/phpcov merge --text coverage/txt coverage - -### download 3rd-party PHP libraries, including dev dependencies -composer_dependencies_dev: clean - composer install --prefer-dist - -## -# Custom release archive generation -# -# For each tagged revision, GitHub provides tar and zip archives that correspond -# to the output of git-archive -# -# These targets produce similar archives, featuring 3rd-party dependencies -# to ease deployment on shared hosting. -## -ARCHIVE_VERSION := shaarli-$$(git describe)-full -ARCHIVE_PREFIX=Shaarli/ - -release_archive: release_tar release_zip - -### download 3rd-party PHP libraries -composer_dependencies: clean - composer install --no-dev --prefer-dist - find vendor/ -name ".git" -type d -exec rm -rf {} + - -### download 3rd-party frontend libraries -frontend_dependencies: - yarnpkg install - -### Build frontend dependencies -build_frontend: frontend_dependencies - yarnpkg run build - -### generate a release tarball and include 3rd-party dependencies and translations -release_tar: composer_dependencies htmldoc translate build_frontend - git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).tar HEAD - $(TAR) rvf $(ARCHIVE_VERSION).tar --transform "s|^vendor|$(ARCHIVE_PREFIX)vendor|" vendor/ - $(TAR) rvf $(ARCHIVE_VERSION).tar --transform "s|^doc/html|$(ARCHIVE_PREFIX)doc/html|" doc/html/ - $(TAR) rvf $(ARCHIVE_VERSION).tar --transform "s|^tpl|$(ARCHIVE_PREFIX)tpl|" tpl/ - gzip $(ARCHIVE_VERSION).tar - -### generate a release zip and include 3rd-party dependencies and translations -release_zip: composer_dependencies htmldoc translate build_frontend - git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).zip -9 HEAD - mkdir -p $(ARCHIVE_PREFIX)/doc - mkdir -p $(ARCHIVE_PREFIX)/vendor - rsync -a doc/html/ $(ARCHIVE_PREFIX)doc/html/ - zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)doc/ - rsync -a vendor/ $(ARCHIVE_PREFIX)vendor/ - zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)vendor/ - rsync -a tpl/ $(ARCHIVE_PREFIX)tpl/ - zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)tpl/ - rm -rf $(ARCHIVE_PREFIX) - -## -# Targets for repository and documentation maintenance -## - -### remove all unversioned files -clean: - @git clean -df - @rm -rf sandbox trivy* - -### generate the AUTHORS file from Git commit information -generate_authors: - @cp .github/mailmap .mailmap - @git shortlog -sne > AUTHORS - @rm .mailmap - -### generate phpDocumentor documentation -phpdoc: clean - @docker run --rm -v $(PWD):/data -u `id -u`:`id -g` phpdoc/phpdoc - -### generate HTML documentation from Markdown pages with Sphinx -htmldoc: - python3 -m venv venv/ - bash -c 'source venv/bin/activate; \ - pip install wheel; \ - pip install sphinx==7.1.0 furo==2023.7.26 myst-parser sphinx-design; \ - sphinx-build -b html -c doc/ doc/md/ doc/html/' - find doc/html/ -type f -exec chmod a-x '{}' \; - rm -r venv - -### Generate Shaarli's translation compiled file (.mo) -translate: - @echo "----------------------" - @echo "Compile translation files" - @echo "----------------------" - @for pofile in `find inc/languages/ -name shaarli.po`; do \ - echo "Compiling $$pofile"; \ - msgfmt -v "$$pofile" -o "`dirname "$$pofile"`/`basename "$$pofile" .po`.mo"; \ - done; - -### Run ESLint check against Shaarli's JS files -eslint: - @yarnpkg run eslint -c .dev/.eslintrc.js assets/vintage/js/ - @yarnpkg run eslint -c .dev/.eslintrc.js assets/default/js/ - @yarnpkg run eslint -c .dev/.eslintrc.js assets/common/js/ - -### Run CSSLint check against Shaarli's SCSS files -sasslint: - @yarnpkg run stylelint --config .dev/.stylelintrc.js 'assets/default/scss/*.scss' - -## -# Security scans -## - -# trivy version (https://github.com/aquasecurity/trivy/releases) -TRIVY_VERSION=0.44.0 -# default trivy exit code when vulnerabilities are found -TRIVY_EXIT_CODE=1 -# default docker image to scan with trivy -TRIVY_TARGET_DOCKER_IMAGE=ghcr.io/shaarli/shaarli:latest - -### download trivy vulneravbility scanner -download_trivy: - wget --quiet --continue -O trivy_$(TRIVY_VERSION)_Linux-64bit.tar.gz https://github.com/aquasecurity/trivy/releases/download/v$(TRIVY_VERSION)/trivy_$(TRIVY_VERSION)_Linux-64bit.tar.gz - tar -z -x trivy -f trivy_$(TRIVY_VERSION)_Linux-64bit.tar.gz - -### run trivy vulnerability scanner on docker image -test_trivy_docker: download_trivy - ./trivy --exit-code $(TRIVY_EXIT_CODE) image $(TRIVY_TARGET_DOCKER_IMAGE) - -### run trivy vulnerability scanner on composer/yarn dependency trees -test_trivy_repo: download_trivy - ./trivy --exit-code $(TRIVY_EXIT_CODE) fs composer.lock - ./trivy --exit-code $(TRIVY_EXIT_CODE) fs yarn.lock diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index c39b91fa..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,62 +0,0 @@ ---- -# Shaarli - Docker Compose example configuration -# -# See: -# - https://shaarli.readthedocs.io/en/master/Docker/#docker-compose -# -# Environment variables: -# - SHAARLI_VIRTUAL_HOST Fully Qualified Domain Name for the Shaarli instance -# - SHAARLI_LETSENCRYPT_EMAIL Contact email for certificate renewal -# - SHAARLI_DOCKER_TAG Shaarli docker tag to use -# See: https://github.com/shaarli/Shaarli/pkgs/container/shaarli/versions?filters%5Bversion_type%5D=tagged -version: '3' - -networks: - http-proxy: - -volumes: - traefik-acme: - shaarli-cache: - shaarli-data: - -services: - shaarli: - image: ghcr.io/shaarli/shaarli:${SHAARLI_DOCKER_TAG} - build: ./ - networks: - - http-proxy - volumes: - - shaarli-cache:/var/www/shaarli/cache - - shaarli-data:/var/www/shaarli/data - labels: - traefik.domain: "${SHAARLI_VIRTUAL_HOST}" - traefik.backend: shaarli - traefik.frontend.rule: "Host:${SHAARLI_VIRTUAL_HOST}" - - traefik: - image: traefik:1.7-alpine - command: - - "--defaultentrypoints=http,https" - - "--entrypoints=Name:http Address::80 Redirect.EntryPoint:https" - - "--entrypoints=Name:https Address::443 TLS" - - "--retry" - - "--docker" - - "--docker.domain=${SHAARLI_VIRTUAL_HOST}" - - "--docker.exposedbydefault=true" - - "--docker.watch=true" - - "--acme" - - "--acme.domains=${SHAARLI_VIRTUAL_HOST}" - - "--acme.email=${SHAARLI_LETSENCRYPT_EMAIL}" - - "--acme.entrypoint=https" - - "--acme.onhostrule=true" - - "--acme.storage=/acme/acme.json" - - "--acme.httpchallenge" - - "--acme.httpchallenge.entrypoint=http" - networks: - - http-proxy - ports: - - 80:80 - - 443:443 - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - - traefik-acme:/acme diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f381ea41..00000000 --- a/package-lock.json +++ /dev/null @@ -1,7486 +0,0 @@ -{ - "name": "shaarli", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "shaarli", - "license": "SEE LICENSE IN COPYING", - "dependencies": { - "awesomplete": "^1.1.2", - "blazy": "^1.8.2", - "fork-awesome": "^1.1.7", - "he": "^1.2.0", - "pure-extras": "^1.0.0", - "purecss": "^1.0.0" - }, - "devDependencies": { - "@babel/core": "^7.11.6", - "@babel/preset-env": "^7.11.5", - "babel-loader": "^8.1.0", - "css-loader": "^7.1.2", - "eslint": "^7.9.0", - "eslint-config-airbnb-base": "^14.2.0", - "eslint-plugin-import": "^2.22.0", - "file-loader": "^1.1.6", - "mini-css-extract-plugin": "^0.11.2", - "sass": "^1.26.11", - "sass-loader": "^10.0.2", - "stylelint": "^16.6.1", - "stylelint-config-standard": "^20.0.0", - "stylelint-scss": "^6.3.2", - "terser-webpack-plugin": "^4.2.2", - "webpack": "^5.92.1", - "webpack-cli": "^5.1.4" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.12.13" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", - "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/core": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.0.tgz", - "integrity": "sha512-8YqpRig5NmIHlMLw09zMlPTvUVMILjqCOtVgu+TVNWEBvy9b5I3RRyhqnrV4hjgEK7n8P9OqvkWJAFmEL6Wwfw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.0", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helpers": "^7.14.0", - "@babel/parser": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@babel/core/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@babel/generator": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.1.tgz", - "integrity": "sha512-TMGhsXMXCP/O1WtQmZjpEYDhCYC9vFhayWZPJSZCGkPJgUqX0rF0wwtrYvnzVxIjcF80tkUertXVk5cwqi5cAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.14.1", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "node_modules/@babel/generator/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", - "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", - "integrity": "sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", - "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.13.15", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.14.5", - "semver": "^6.3.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.1.tgz", - "integrity": "sha512-r8rsUahG4ywm0QpGcCrLaUSOuNAISR3IZCg4Fx05Ozq31aCUrQsTLH6KPxy0N5ULoQ4Sn9qjNdGNtbPWAC6hYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.17.tgz", - "integrity": "sha512-p2VGmBu9oefLZ2nQpgnEnG0ZlRPvL8gAGvPUMQwUdaE8k49rOMuZpOwdQoy5qJf6K8jL3bcAMhVUlHAjIgJHUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "regexpu-core": "^4.7.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.0.tgz", - "integrity": "sha512-JT8tHuFjKBo8NnaUbblz7mIu1nnvUDiHVjXXkulZULyidvo/7P6TY7+YqpV37IfF+KUFxmlK04elKtGKXaiVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.13.0", - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/traverse": "^7.13.0", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz", - "integrity": "sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.13.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.16.tgz", - "integrity": "sha512-1eMtTrXtrwscjcAeO4BVK+vvkxaLJSPFz1w1KLawz6HLNi9bPFGBNwwDyVfiu1Tv/vRRFYfoGaKhmAQPGPn5Wg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.13.15", - "@babel/types": "^7.13.16" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", - "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.13.12" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", - "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.13.12" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.0.tgz", - "integrity": "sha512-L40t9bxIuGOfpIGA3HNkJhU9qYrf4y5A5LUSw7rGMSn+pcG8dfJ0g6Zval6YJGd2nEjI7oP00fRdnhLKndx6bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-simple-access": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", - "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz", - "integrity": "sha512-pUQpFBE9JvC9lrQbpX0TmeNIy5s7GnZjna2lhhcHC7DzgBs6fWn722Y5cfwgrtrqc7NAJwMvOa0mKhq6XaE4jg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-wrap-function": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", - "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.12" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", - "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.13.12" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", - "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.1" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", - "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz", - "integrity": "sha512-1UX9F7K3BS42fI6qd2A4BjKzgGjToscyZTdp1DjknHLCIvpgne6918io+aL5LXFcER/8QWiwpoY902pVEqgTXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-function-name": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", - "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", - "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.1.tgz", - "integrity": "sha512-muUGEKu8E/ftMTPlNp+mc6zL3E9zKWmF5sDHZ5MSsoTP9Wyz64AhEf9kD08xYJ7w6Hdcu8H550ircnPyWSIF0Q==", - "dev": true, - "license": "MIT", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz", - "integrity": "sha512-d0u3zWKcoZf379fOeJdr1a5WPDny4aOFZ6hlfKivgK0LY7ZxNfoaHL2fWwdGtHyVvra38FC+HVYkO+byfSA8AQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-proposal-optional-chaining": "^7.13.12" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.15.tgz", - "integrity": "sha512-VapibkWzFeoa6ubXy/NgV5U2U4MVnUlvnx6wo1XhlsaTrLYWE0UFpDQsVrmn22q5CzeloqJ8gEMHSKxuee6ZdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz", - "integrity": "sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.13.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.13.11.tgz", - "integrity": "sha512-fJTdFI4bfnMjvxJyNuaf8i9mVcZ0UhetaGEUHaHV9KEnibLugJkZAtXikR8KcYj+NYmI4DZMS8yQAyg+hvfSqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-class-static-block": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz", - "integrity": "sha512-ONWKj0H6+wIRCkZi9zSbZtE/r73uOhMVHh256ys0UzfM7I3d4n+spZNWjOnJv2gzopumP2Wxi186vI8N0Y2JyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz", - "integrity": "sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz", - "integrity": "sha512-w4zOPKUFPX1mgvTmL/fcEqy34hrQ1CRcGxdphBc6snDnnqJ47EZDIyop6IwXzAC8G916hsIuXB2ZMBCExC5k7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz", - "integrity": "sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz", - "integrity": "sha512-iePlDPBn//UhxExyS9KyeYU7RM9WScAG+D3Hhno0PLJebAEpDZMocbDe64eqynhNAnwz/vZoL/q/QB2T1OH39A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz", - "integrity": "sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz", - "integrity": "sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-compilation-targets": "^7.13.8", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz", - "integrity": "sha512-0wS/4DUF1CuTmGo+NiaHfHcVSeSLj5S3e6RivPTg/2k3wOv3jO35tZ6/ZWsQhQMvdgI7CwphjQa/ccarLymHVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz", - "integrity": "sha512-fcEdKOkIB7Tf4IxrgEVeFC4zeJSTr78no9wTdBuZZbqF64kzllU0ybo2zrzm7gUQfxGhBgq4E39oRs8Zx/RMYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz", - "integrity": "sha512-MXyyKQd9inhx1kDYPkFRVOBXQ20ES8Pto3T7UZ92xj2mY0EVD8oAVzeyYuVfy/mxAdTSIayOvg+aVzcHV2bn6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.0.tgz", - "integrity": "sha512-59ANdmEwwRUkLjB7CRtwJxxwtjESw+X2IePItA+RGQh+oy5RmpCh/EvVVvh5XQc3yxsm5gtv0+i9oBZhaDNVTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-create-class-features-plugin": "^7.14.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-private-property-in-object": "^7.14.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", - "integrity": "sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.12.13.tgz", - "integrity": "sha512-ZmKQ0ZXR0nYpHZIIuj9zE7oIqCx2hw9TKi+lIo73NNrMPAZGHfS92/VRV0ZmPj6H2ffBgyFHXvJ5NYsNeEaP2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.0.tgz", - "integrity": "sha512-bda3xF8wGl5/5btF794utNOL0Jw+9jE5C1sLZcoK7c4uonE/y3iQiyG+KbkF3WBV/paX58VCpjhxLPkdj5Fe4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", - "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz", - "integrity": "sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz", - "integrity": "sha512-3j6E004Dx0K3eGmhxVJxwwI89CTJrce7lg3UrtFuDAVQ/2+SJ/h/aSFOeE6/n0WB1GsOffsJp6MnPQNQ8nmwhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz", - "integrity": "sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.1.tgz", - "integrity": "sha512-2mQXd0zBrwfp0O1moWIhPpEeTKDvxyHcnma3JATVP1l+CctWBuot6OJG8LQ4DnBj4ZZPSmlb/fm4mu47EOAnVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz", - "integrity": "sha512-9BtHCPUARyVH1oXGcSJD3YpsqRLROJx5ZNP6tN5vnk17N0SVf9WCtf8Nuh1CFmgByKKAIMstitKduoCmsaDK5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-replace-supers": "^7.13.0", - "@babel/helper-split-export-declaration": "^7.12.13", - "globals": "^11.1.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz", - "integrity": "sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.13.17", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz", - "integrity": "sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz", - "integrity": "sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz", - "integrity": "sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz", - "integrity": "sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz", - "integrity": "sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz", - "integrity": "sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz", - "integrity": "sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz", - "integrity": "sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.0.tgz", - "integrity": "sha512-CF4c5LX4LQ03LebQxJ5JZes2OYjzBuk1TdiF7cG7d5dK4lAdw9NZmaxq5K/mouUdNeqwz3TNjnW6v01UqUNgpQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helper-plugin-utils": "^7.13.0", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.0.tgz", - "integrity": "sha512-EX4QePlsTaRZQmw9BsoPeyh5OCtRGIhwfLquhxGp5e32w+dyL8htOcDwamlitmNFK6xBZYlygjdye9dbd9rUlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-simple-access": "^7.13.12", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz", - "integrity": "sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-hoist-variables": "^7.13.0", - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-identifier": "^7.12.11", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.0.tgz", - "integrity": "sha512-nPZdnWtXXeY7I87UZr9VlsWme3Y0cfFFE41Wbxz4bbaexAjNMInXPFUpRRUJ8NoMm0Cw+zxbqjdPmLhcjfazMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", - "integrity": "sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz", - "integrity": "sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz", - "integrity": "sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/helper-replace-supers": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz", - "integrity": "sha512-Jt8k/h/mIwE2JFEOb3lURoY5C85ETcYPnbuAJ96zRBzh1XHtQZfs62ChZ6EP22QlC8c7Xqr9q+e1SU5qttwwjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz", - "integrity": "sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.13.15.tgz", - "integrity": "sha512-Bk9cOLSz8DiurcMETZ8E2YtIVJbFCPGW28DJWUakmyVWtQSm6Wsf0p4B4BfEr/eL2Nkhe/CICiUiMOCi1TPhuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerator-transform": "^0.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz", - "integrity": "sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz", - "integrity": "sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz", - "integrity": "sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz", - "integrity": "sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz", - "integrity": "sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz", - "integrity": "sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", - "integrity": "sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz", - "integrity": "sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.1.tgz", - "integrity": "sha512-0M4yL1l7V4l+j/UHvxcdvNfLB9pPtIooHTbEhgD/6UGyh8Hy3Bm1Mj0buzjDXATCSz3JFibVdnoJZCrlUCanrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.14.0", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-option": "^7.12.17", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.13.12", - "@babel/plugin-proposal-async-generator-functions": "^7.13.15", - "@babel/plugin-proposal-class-properties": "^7.13.0", - "@babel/plugin-proposal-class-static-block": "^7.13.11", - "@babel/plugin-proposal-dynamic-import": "^7.13.8", - "@babel/plugin-proposal-export-namespace-from": "^7.12.13", - "@babel/plugin-proposal-json-strings": "^7.13.8", - "@babel/plugin-proposal-logical-assignment-operators": "^7.13.8", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", - "@babel/plugin-proposal-numeric-separator": "^7.12.13", - "@babel/plugin-proposal-object-rest-spread": "^7.13.8", - "@babel/plugin-proposal-optional-catch-binding": "^7.13.8", - "@babel/plugin-proposal-optional-chaining": "^7.13.12", - "@babel/plugin-proposal-private-methods": "^7.13.0", - "@babel/plugin-proposal-private-property-in-object": "^7.14.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.12.13", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.0", - "@babel/plugin-syntax-top-level-await": "^7.12.13", - "@babel/plugin-transform-arrow-functions": "^7.13.0", - "@babel/plugin-transform-async-to-generator": "^7.13.0", - "@babel/plugin-transform-block-scoped-functions": "^7.12.13", - "@babel/plugin-transform-block-scoping": "^7.14.1", - "@babel/plugin-transform-classes": "^7.13.0", - "@babel/plugin-transform-computed-properties": "^7.13.0", - "@babel/plugin-transform-destructuring": "^7.13.17", - "@babel/plugin-transform-dotall-regex": "^7.12.13", - "@babel/plugin-transform-duplicate-keys": "^7.12.13", - "@babel/plugin-transform-exponentiation-operator": "^7.12.13", - "@babel/plugin-transform-for-of": "^7.13.0", - "@babel/plugin-transform-function-name": "^7.12.13", - "@babel/plugin-transform-literals": "^7.12.13", - "@babel/plugin-transform-member-expression-literals": "^7.12.13", - "@babel/plugin-transform-modules-amd": "^7.14.0", - "@babel/plugin-transform-modules-commonjs": "^7.14.0", - "@babel/plugin-transform-modules-systemjs": "^7.13.8", - "@babel/plugin-transform-modules-umd": "^7.14.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", - "@babel/plugin-transform-new-target": "^7.12.13", - "@babel/plugin-transform-object-super": "^7.12.13", - "@babel/plugin-transform-parameters": "^7.13.0", - "@babel/plugin-transform-property-literals": "^7.12.13", - "@babel/plugin-transform-regenerator": "^7.13.15", - "@babel/plugin-transform-reserved-words": "^7.12.13", - "@babel/plugin-transform-shorthand-properties": "^7.12.13", - "@babel/plugin-transform-spread": "^7.13.0", - "@babel/plugin-transform-sticky-regex": "^7.12.13", - "@babel/plugin-transform-template-literals": "^7.13.0", - "@babel/plugin-transform-typeof-symbol": "^7.12.13", - "@babel/plugin-transform-unicode-escapes": "^7.12.13", - "@babel/plugin-transform-unicode-regex": "^7.12.13", - "@babel/preset-modules": "^0.1.4", - "@babel/types": "^7.14.1", - "babel-plugin-polyfill-corejs2": "^0.2.0", - "babel-plugin-polyfill-corejs3": "^0.2.0", - "babel-plugin-polyfill-regenerator": "^0.2.0", - "core-js-compat": "^3.9.0", - "semver": "^6.3.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", - "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz", - "integrity": "sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.13.4" - } - }, - "node_modules/@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true, - "license": "MIT", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.1.tgz", - "integrity": "sha512-S13Qe85fzLs3gYRUnrpyeIrBJIMYv33qSTg1qoBwiG6nPKwUWAD9odSzWhEedpwOIzSEI6gbdQIWEMiCI42iBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.0", - "to-fast-properties": "^2.0.0" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.3.tgz", - "integrity": "sha512-xI/tL2zxzEbESvnSxwFgwvy5HS00oCXxL4MLs6HUiDcYfwowsoQaABKxUElp1ARITrINzBnsECOc1q0eg2GOrA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^2.3.1" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.3.1.tgz", - "integrity": "sha512-iMNHTyxLbBlWIfGtabT157LH9DUx9X8+Y3oymFEuMj8HNc+rpE3dPFGFgHjpKfjeFDjLjYIAIhXPGvS2lKxL9g==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18" - } - }, - "node_modules/@csstools/media-query-list-parser": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.11.tgz", - "integrity": "sha512-uox5MVhvNHqitPP+SynrB1o8oPxPMt2JLgp5ghJOWf54WGQ5OKu47efne49r1SWqs3wRP8xSWjnO9MBKxhB1dA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.6.3", - "@csstools/css-tokenizer": "^2.3.1" - } - }, - "node_modules/@csstools/selector-specificity": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.1.1.tgz", - "integrity": "sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^6.0.13" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@dual-bundle/import-meta-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", - "integrity": "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz", - "integrity": "sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/move-file/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/move-file/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@types/eslint": { - "version": "8.56.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", - "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz", - "integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", - "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", - "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", - "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": ">=5.0.0" - } - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "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": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/awesomplete": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/awesomplete/-/awesomplete-1.1.5.tgz", - "integrity": "sha512-UFw1mPW8NaSECDSTC36HbAOTpF9JK2wBUJcNn4MSvlNtK7SZ9N72gB+ajHtA6D1abYXRcszZnBA4nHBwvFwzHw==", - "license": "MIT" - }, - "node_modules/babel-loader": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", - "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" - } - }, - "node_modules/babel-loader/node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "object.assign": "^4.1.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.0.tgz", - "integrity": "sha512-9bNwiR0dS881c5SHnzCmmGlMkJLl0OUZvxrxHo9w/iNoRuqaPjqlvBf4HrovXtQs/au5yKkpcdgfT1cC5PAZwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.2.0", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.0.tgz", - "integrity": "sha512-zZyi7p3BCUyzNxLx8KV61zTINkkV65zVkDAFNZmrTCRVhjo1jAS+YLvDJ9Jgd/w2tsAviCwFHReYfxO3Iql8Yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.2.0", - "core-js-compat": "^3.9.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.0.tgz", - "integrity": "sha512-J7vKbCuD2Xi/eEHxquHN14bXAW9CXtecwuLrOIDJtcZzTaPzV1VdEfoUf9AzcRBMolKUQKM9/GVojeh0hFiqMg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.2.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/balanced-match": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", - "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", - "dev": true, - "license": "MIT" - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/blazy": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/blazy/-/blazy-1.8.2.tgz", - "integrity": "sha1-UN/WOLqvkAPv1us6g2rKVBhKtto=", - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/brace-expansion/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "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": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", - "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", - "dev": true, - "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": { - "caniuse-lite": "^1.0.30001629", - "electron-to-chromium": "^1.4.796", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.16" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/cacache": { - "version": "15.0.6", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.6.tgz", - "integrity": "sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cacache/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cacache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001636", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz", - "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==", - "dev": true, - "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" - }, - "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", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/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": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.1" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "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" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clone-deep/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, - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true, - "license": "MIT" - }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", - "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==", - "dev": true, - "license": "MIT" - }, - "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/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/confusing-browser-globals": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", - "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/convert-source-map/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/core-js-compat": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", - "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cosmiconfig/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==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/cosmiconfig/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-functions-list": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz", - "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12 || >=16" - } - }, - "node_modules/css-loader": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", - "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.27.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/css-loader/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true, - "license": "MIT" - }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "dependencies": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.807", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.807.tgz", - "integrity": "sha512-kSmJl2ZwhNf/bcIuCH/imtNOKlpkLDn2jqT5FJ+/0CXjhnFaOa9cOe9gHKKy71eM49izwuQjZhKk+lWQ1JxB7A==", - "dev": true, - "license": "ISC" - }, - "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/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/envinfo": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", - "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==", - "dev": true, - "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "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/es-abstract": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", - "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.2", - "is-string": "^1.0.5", - "object-inspect": "^1.9.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-module-lexer": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.3.tgz", - "integrity": "sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==", - "dev": true, - "license": "MIT" - }, - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "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": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.26.0.tgz", - "integrity": "sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.1", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.21", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.4", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-airbnb-base": { - "version": "14.2.1", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", - "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.2" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", - "eslint-plugin-import": "^2.22.1" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - } - }, - "node_modules/eslint-import-resolver-node/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, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/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, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-module-utils/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint-module-utils/node_modules/pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", - "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" - } - }, - "node_modules/eslint-plugin-import/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, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "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==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint/node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "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==", - "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/eslint/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/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==", - "dev": true, - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint/node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/eslint/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==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", - "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "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==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "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.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "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==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "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==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "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/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==", - "dev": true, - "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==", - "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.4" - }, - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true, - "license": "MIT" - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-loader": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", - "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", - "dev": true, - "license": "MIT", - "dependencies": { - "loader-utils": "^1.0.2", - "schema-utils": "^0.4.5" - }, - "engines": { - "node": ">= 4.3 < 5.0.0 || >= 5.10" - }, - "peerDependencies": { - "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" - } - }, - "node_modules/file-loader/node_modules/schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "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": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.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==", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fork-awesome": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/fork-awesome/-/fork-awesome-1.1.7.tgz", - "integrity": "sha512-IHI7XCSXrKfUIWslse8c/PaaVDT1oBaYge+ju40ihL2ooiQeBpTr4wvIXhgTd2NuhntlvX+M5jYHAPTzNlmv0g==", - "license": "(OFL-1.1 AND MIT)", - "engines": { - "node": ">=0.10.3" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true, - "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==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true, - "license": "MIT" - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true, - "license": "MIT" - }, - "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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby/node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/globjoin": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", - "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", - "dev": true, - "license": "MIT" - }, - "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/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true, - "license": "ISC" - }, - "node_modules/html-tags": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", - "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "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": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true, - "license": "ISC" - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "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==", - "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==", - "dev": true, - "license": "ISC" - }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true, - "license": "MIT" - }, - "node_modules/is-bigint": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", - "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", - "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz", - "integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", - "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "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/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", - "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", - "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", - "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true, - "license": "ISC" - }, - "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, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "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/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "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==", - "dev": true, - "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": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/klona": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", - "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/known-css-properties": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.31.0.tgz", - "integrity": "sha512-sBPIUGTNF0czz0mwGGUoKKJC8Q7On1GPbCSFPfyEsfHb2DyBG0Y4QtV+EVWpINSaiGKZblDNuF5AezxSgOhesQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "license": "MIT", - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mathml-tag-names": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", - "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.3.tgz", - "integrity": "sha512-n9BA8LonkOkW1/zn+IbLPQmovsL0wMb9yx75fMJQZf2X1Zoec9yTZtyMePcyu19wPkmFbzZZA6fLTotpFhQsOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "loader-utils": "^1.1.0", - "normalize-url": "1.9.1", - "schema-utils": "^1.0.0", - "webpack-sources": "^1.1.0" - }, - "engines": { - "node": ">= 6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.4.0 || ^5.0.0" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "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/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true, - "license": "MIT" - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/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_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, - "license": "MIT", - "dependencies": { - "object-assign": "^4.0.1", - "prepend-http": "^1.0.0", - "query-string": "^4.1.0", - "sort-keys": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", - "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz", - "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "has": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.values": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", - "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "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", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true, - "license": "ISC" - }, - "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/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "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.0.0", - "source-map-js": "^1.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-media-query-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", - "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", - "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-resolve-nested-selector": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-safe-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.0.tgz", - "integrity": "sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", - "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true, - "license": "ISC" - }, - "node_modules/pure-extras": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pure-extras/-/pure-extras-1.0.0.tgz", - "integrity": "sha1-N+PMNZDLqFCYFFTNpdso4npjhxo=", - "license": "Open" - }, - "node_modules/purecss": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/purecss/-/purecss-1.0.1.tgz", - "integrity": "sha512-mTUc5ZzpzafswEhCmTDfSRMMyRFdLYdd+KywMwnBC/MuA/Th7jug2z0Xso4WkxvtxoU/BS9aRb7WnBNyuA7YJQ==", - "license": "BSD" - }, - "node_modules/query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true, - "license": "MIT", - "dependencies": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "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/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "license": "MIT", - "dependencies": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true, - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true, - "license": "MIT" - }, - "node_modules/regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/regexpu-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", - "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", - "dev": true, - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.9.tgz", - "integrity": "sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "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": { - "queue-microtask": "^1.2.2" - } - }, - "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/sass": { - "version": "1.32.12", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.12.tgz", - "integrity": "sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/sass-loader": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.1.1.tgz", - "integrity": "sha512-W6gVDXAd5hR/WHsPicvZdjAWHBcEJ44UahgxcIE196fW2ong0ZHMPO1kZuI5q0VlvMQZh32gpv69PLWQm70qrw==", - "dev": true, - "license": "MIT", - "dependencies": { - "klona": "^2.0.4", - "loader-utils": "^2.0.0", - "neo-async": "^2.6.2", - "schema-utils": "^3.0.0", - "semver": "^7.3.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0", - "sass": "^1.3.0", - "webpack": "^4.36.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/sass-loader/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/sass-loader/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/sass-loader/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/sass-loader/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "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/serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "license": "MIT", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/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/slice-ansi/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/slice-ansi/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/sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true, - "license": "MIT" - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "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", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true, - "license": "CC-BY-3.0" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", - "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" - } - }, - "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": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "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/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stylelint": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.6.1.tgz", - "integrity": "sha512-yNgz2PqWLkhH2hw6X9AweV9YvoafbAD5ZsFdKN9BvSDVwGvPh+AUIrn7lYwy1S7IHmtFin75LLfX1m0D2tHu8Q==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" - }, - { - "type": "github", - "url": "https://github.com/sponsors/stylelint" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/css-parser-algorithms": "^2.6.3", - "@csstools/css-tokenizer": "^2.3.1", - "@csstools/media-query-list-parser": "^2.1.11", - "@csstools/selector-specificity": "^3.1.1", - "@dual-bundle/import-meta-resolve": "^4.1.0", - "balanced-match": "^2.0.0", - "colord": "^2.9.3", - "cosmiconfig": "^9.0.0", - "css-functions-list": "^3.2.2", - "css-tree": "^2.3.1", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^9.0.0", - "global-modules": "^2.0.0", - "globby": "^11.1.0", - "globjoin": "^0.1.4", - "html-tags": "^3.3.1", - "ignore": "^5.3.1", - "imurmurhash": "^0.1.4", - "is-plain-object": "^5.0.0", - "known-css-properties": "^0.31.0", - "mathml-tag-names": "^2.1.3", - "meow": "^13.2.0", - "micromatch": "^4.0.7", - "normalize-path": "^3.0.0", - "picocolors": "^1.0.1", - "postcss": "^8.4.38", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-safe-parser": "^7.0.0", - "postcss-selector-parser": "^6.1.0", - "postcss-value-parser": "^4.2.0", - "resolve-from": "^5.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^7.1.0", - "supports-hyperlinks": "^3.0.0", - "svg-tags": "^1.0.0", - "table": "^6.8.2", - "write-file-atomic": "^5.0.1" - }, - "bin": { - "stylelint": "bin/stylelint.mjs" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/stylelint-config-recommended": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-3.0.0.tgz", - "integrity": "sha512-F6yTRuc06xr1h5Qw/ykb2LuFynJ2IxkKfCMf+1xqPffkxh0S09Zc902XCffcsw/XMFq/OzQ1w54fLIDtmRNHnQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "stylelint": ">=10.1.0" - } - }, - "node_modules/stylelint-config-standard": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-20.0.0.tgz", - "integrity": "sha512-IB2iFdzOTA/zS4jSVav6z+wGtin08qfj+YyExHB3LF9lnouQht//YyB0KZq9gGz5HNPkddHOzcY8HsUey6ZUlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "stylelint-config-recommended": "^3.0.0" - }, - "peerDependencies": { - "stylelint": ">=10.1.0" - } - }, - "node_modules/stylelint-scss": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-6.3.2.tgz", - "integrity": "sha512-pNk9mXOVKkQtd+SROPC9io8ISSgX+tOVPhFdBE+LaKQnJMLdWPbGKAGYv4Wmf/RrnOjkutunNTN9kKMhkdE5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "known-css-properties": "^0.31.0", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-selector-parser": "^6.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "stylelint": "^16.0.2" - } - }, - "node_modules/stylelint/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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.0.0.tgz", - "integrity": "sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/stylelint/node_modules/flat-cache": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", - "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.3.1", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/stylelint/node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/stylelint/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/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/supports-color/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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", - "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=14.18" - } - }, - "node_modules/supports-hyperlinks/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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/svg-tags": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", - "dev": true - }, - "node_modules/table": { - "version": "6.8.2", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", - "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.2.0.tgz", - "integrity": "sha512-WSNGFuyWd//XO8n/m/EaOlNLtO0yL8EXT/74LqT4khdhpZjP7lkj/kT5uwRmGitKEVp/Oj7ZUHeGfPtgHhQ5CA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser": { - "version": "5.31.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.1.tgz", - "integrity": "sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz", - "integrity": "sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "cacache": "^15.0.5", - "find-cache-dir": "^3.3.1", - "jest-worker": "^26.5.0", - "p-limit": "^3.0.2", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", - "source-map": "^0.6.1", - "terser": "^5.3.4", - "webpack-sources": "^1.4.3" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser/node_modules/acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true, - "license": "MIT" - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", - "dev": true, - "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": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true, - "license": "MIT" - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/watchpack": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", - "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", - "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", - "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.1.1", - "@webpack-cli/info": "^2.0.2", - "@webpack-cli/serve": "^2.0.5", - "colorette": "^2.0.14", - "commander": "^10.0.1", - "cross-spawn": "^7.0.3", - "envinfo": "^7.7.3", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^5.7.3" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "5.x.x" - }, - "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "node_modules/webpack/node_modules/acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/webpack/node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/webpack/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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/webpack/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack/node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/webpack/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/webpack/node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/webpack/node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "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/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "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" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/phpunit.xml b/phpunit.xml deleted file mode 100644 index 8b66e6c5..00000000 --- a/phpunit.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - tests - tests/languages - - - tests/languages/de - - - tests/languages/en - - - tests/languages/fr - - - - - - application - - - diff --git a/tests/.htaccess b/tests/.htaccess deleted file mode 100644 index f601c1ee..00000000 --- a/tests/.htaccess +++ /dev/null @@ -1,13 +0,0 @@ - - = 2.4> - Require all denied - - - Allow from none - Deny from all - - - - - Require all denied - diff --git a/tests/HistoryTest.php b/tests/HistoryTest.php deleted file mode 100644 index e810104e..00000000 --- a/tests/HistoryTest.php +++ /dev/null @@ -1,206 +0,0 @@ -assertFileNotExists(self::$historyFilePath); - } - - /** - * Test that the history file is created if it doesn't exist. - */ - public function testAddEventCreateFile() - { - $history = new History(self::$historyFilePath); - $history->updateSettings(); - $this->assertFileExists(self::$historyFilePath); - } - - /** - * Not writable history file: raise an exception. - */ - public function testConstructNotWritable() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('History file isn\'t readable or writable'); - - touch(self::$historyFilePath); - chmod(self::$historyFilePath, 0440); - $history = new History(self::$historyFilePath); - $history->updateSettings(); - } - - /** - * Not parsable history file: raise an exception. - */ - public function testConstructNotParsable() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessageRegExp('/Could not parse history file/'); - - file_put_contents(self::$historyFilePath, 'not parsable'); - $history = new History(self::$historyFilePath); - // gzinflate generates a warning - @$history->updateSettings(); - } - - /** - * Test add link event - */ - public function testAddLink() - { - $history = new History(self::$historyFilePath); - $bookmark = (new Bookmark())->setId(0); - $history->addLink($bookmark); - $actual = $history->getHistory()[0]; - $this->assertEquals(History::CREATED, $actual['event']); - $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); - $this->assertEquals(0, $actual['id']); - - $history = new History(self::$historyFilePath); - $bookmark = (new Bookmark())->setId(1); - $history->addLink($bookmark); - $actual = $history->getHistory()[0]; - $this->assertEquals(History::CREATED, $actual['event']); - $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); - $this->assertEquals(1, $actual['id']); - } - -// /** -// * Test updated link event -// */ -// public function testUpdateLink() -// { -// $history = new History(self::$historyFilePath); -// $history->updateLink(['id' => 1]); -// $actual = $history->getHistory()[0]; -// $this->assertEquals(History::UPDATED, $actual['event']); -// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); -// $this->assertEquals(1, $actual['id']); -// } -// -// /** -// * Test delete link event -// */ -// public function testDeleteLink() -// { -// $history = new History(self::$historyFilePath); -// $history->deleteLink(['id' => 1]); -// $actual = $history->getHistory()[0]; -// $this->assertEquals(History::DELETED, $actual['event']); -// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); -// $this->assertEquals(1, $actual['id']); -// } -// -// /** -// * Test updated settings event -// */ -// public function testUpdateSettings() -// { -// $history = new History(self::$historyFilePath); -// $history->updateSettings(); -// $actual = $history->getHistory()[0]; -// $this->assertEquals(History::SETTINGS, $actual['event']); -// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); -// $this->assertEmpty($actual['id']); -// } -// -// /** -// * Make sure that new items are stored at the beginning -// */ -// public function testHistoryOrder() -// { -// $history = new History(self::$historyFilePath); -// $history->updateLink(['id' => 1]); -// $actual = $history->getHistory()[0]; -// $this->assertEquals(History::UPDATED, $actual['event']); -// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); -// $this->assertEquals(1, $actual['id']); -// -// $history->addLink(['id' => 1]); -// $actual = $history->getHistory()[0]; -// $this->assertEquals(History::CREATED, $actual['event']); -// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); -// $this->assertEquals(1, $actual['id']); -// } -// -// /** -// * Re-read history from file after writing an event -// */ -// public function testHistoryRead() -// { -// $history = new History(self::$historyFilePath); -// $history->updateLink(['id' => 1]); -// $history = new History(self::$historyFilePath); -// $actual = $history->getHistory()[0]; -// $this->assertEquals(History::UPDATED, $actual['event']); -// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); -// $this->assertEquals(1, $actual['id']); -// } -// -// /** -// * Re-read history from file after writing an event and make sure that the order is correct -// */ -// public function testHistoryOrderRead() -// { -// $history = new History(self::$historyFilePath); -// $history->updateLink(['id' => 1]); -// $history->addLink(['id' => 1]); -// -// $history = new History(self::$historyFilePath); -// $actual = $history->getHistory()[0]; -// $this->assertEquals(History::CREATED, $actual['event']); -// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); -// $this->assertEquals(1, $actual['id']); -// -// $actual = $history->getHistory()[1]; -// $this->assertEquals(History::UPDATED, $actual['event']); -// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); -// $this->assertEquals(1, $actual['id']); -// } -// -// /** -// * Test retention time: delete old entries. -// */ -// public function testHistoryRententionTime() -// { -// $history = new History(self::$historyFilePath, 5); -// $history->updateLink(['id' => 1]); -// $this->assertEquals(1, count($history->getHistory())); -// $arr = $history->getHistory(); -// $arr[0]['datetime'] = new DateTime('-1 hour'); -// FileUtils::writeFlatDB(self::$historyFilePath, $arr); -// -// $history = new History(self::$historyFilePath, 60); -// $this->assertEquals(1, count($history->getHistory())); -// $this->assertEquals(1, $history->getHistory()[0]['id']); -// $history->updateLink(['id' => 2]); -// $this->assertEquals(1, count($history->getHistory())); -// $this->assertEquals(2, $history->getHistory()[0]['id']); -// } -} diff --git a/tests/LanguagesTest.php b/tests/LanguagesTest.php deleted file mode 100644 index ce24c160..00000000 --- a/tests/LanguagesTest.php +++ /dev/null @@ -1,229 +0,0 @@ -conf = new ConfigManager(self::$configFile); - } - - /** - * Test t() with a simple non identified value. - */ - public function testTranslateSingleNotIDGettext() - { - $this->conf->set('translation.mode', 'gettext'); - new Languages('en', $this->conf); - $text = 'abcdé 564 fgK'; - $this->assertEquals($text, t($text)); - } - - /** - * Test t() with a simple identified value in gettext mode. - */ - public function testTranslateSingleIDGettext() - { - $this->conf->set('translation.mode', 'gettext'); - new Languages('en', $this->conf); - $text = 'permalink'; - $this->assertEquals($text, t($text)); - } - - /** - * Test t() with a non identified plural form in gettext mode. - */ - public function testTranslatePluralNotIDGettext() - { - $this->conf->set('translation.mode', 'gettext'); - new Languages('en', $this->conf); - $text = 'sandwich'; - $nText = 'sandwiches'; - $this->assertEquals('sandwiches', t($text, $nText, 0)); - $this->assertEquals('sandwich', t($text, $nText, 1)); - $this->assertEquals('sandwiches', t($text, $nText, 2)); - } - - /** - * Test t() with an identified plural form in gettext mode. - */ - public function testTranslatePluralIDGettext() - { - $this->conf->set('translation.mode', 'gettext'); - new Languages('en', $this->conf); - $text = 'shaare'; - $nText = 'shaares'; - // In english, zero is followed by plural form - $this->assertEquals('shaares', t($text, $nText, 0)); - $this->assertEquals('shaare', t($text, $nText, 1)); - $this->assertEquals('shaares', t($text, $nText, 2)); - } - - /** - * Test t() with a simple non identified value. - */ - public function testTranslateSingleNotIDPhp() - { - $this->conf->set('translation.mode', 'php'); - new Languages('en', $this->conf); - $text = 'abcdé 564 fgK'; - $this->assertEquals($text, t($text)); - } - - /** - * Test t() with a simple identified value in PHP mode. - */ - public function testTranslateSingleIDPhp() - { - $this->conf->set('translation.mode', 'php'); - new Languages('en', $this->conf); - $text = 'permalink'; - $this->assertEquals($text, t($text)); - } - - /** - * Test t() with a non identified plural form in PHP mode. - */ - public function testTranslatePluralNotIDPhp() - { - $this->conf->set('translation.mode', 'php'); - new Languages('en', $this->conf); - $text = 'sandwich'; - $nText = 'sandwiches'; - $this->assertEquals('sandwiches', t($text, $nText, 0)); - $this->assertEquals('sandwich', t($text, $nText, 1)); - $this->assertEquals('sandwiches', t($text, $nText, 2)); - } - - /** - * Test t() with an identified plural form in PHP mode. - */ - public function testTranslatePluralIDPhp() - { - $this->conf->set('translation.mode', 'php'); - new Languages('en', $this->conf); - $text = 'shaare'; - $nText = 'shaares'; - // In english, zero is followed by plural form - $this->assertEquals('shaares', t($text, $nText, 0)); - $this->assertEquals('shaare', t($text, $nText, 1)); - $this->assertEquals('shaares', t($text, $nText, 2)); - } - - /** - * Test t() with an invalid language set in the configuration in gettext mode. - */ - public function testTranslateWithInvalidConfLanguageGettext() - { - $this->conf->set('translation.mode', 'gettext'); - $this->conf->set('translation.language', 'nope'); - new Languages('fr', $this->conf); - $text = 'grumble'; - $this->assertEquals($text, t($text)); - } - - /** - * Test t() with an invalid language set in the configuration in PHP mode. - */ - public function testTranslateWithInvalidConfLanguagePhp() - { - $this->conf->set('translation.mode', 'php'); - $this->conf->set('translation.language', 'nope'); - new Languages('fr', $this->conf); - $text = 'grumble'; - $this->assertEquals($text, t($text)); - } - - /** - * Test t() with an invalid language set with auto language in gettext mode. - */ - public function testTranslateWithInvalidAutoLanguageGettext() - { - $this->conf->set('translation.mode', 'gettext'); - new Languages('nope', $this->conf); - $text = 'grumble'; - $this->assertEquals($text, t($text)); - } - - /** - * Test t() with an invalid language set with auto language in PHP mode. - */ - public function testTranslateWithInvalidAutoLanguagePhp() - { - $this->conf->set('translation.mode', 'php'); - new Languages('nope', $this->conf); - $text = 'grumble'; - $this->assertEquals($text, t($text)); - } - - /** - * Test t() with an extension language file coming from the theme in gettext mode - */ - public function testTranslationThemeExtensionGettext() - { - $this->conf->set('translation.mode', 'gettext'); - $this->conf->set('raintpl_tpl', 'tests/utils/customtpl/'); - $this->conf->set('theme', 'dummy'); - new Languages('en', $this->conf); - $txt = 'rooster'; // ignore me poedit - $this->assertEquals('rooster', t($txt, $txt, 1, 'dummy')); - } - - /** - * Test t() with an extension language file coming from the theme in PHP mode - */ - public function testTranslationThemeExtensionPhp() - { - $this->conf->set('translation.mode', 'php'); - $this->conf->set('raintpl_tpl', 'tests/utils/customtpl/'); - $this->conf->set('theme', 'dummy'); - new Languages('en', $this->conf); - $txt = 'rooster'; // ignore me poedit - $this->assertEquals('rooster', t($txt, $txt, 1, 'dummy')); - } - - /** - * Test t() with an extension language file in gettext mode - */ - public function testTranslationExtensionGettext() - { - $this->conf->set('translation.mode', 'gettext'); - $this->conf->set('translation.extensions.test', 'tests/utils/languages/'); - new Languages('en', $this->conf); - $txt = 'car'; // ignore me poedit - $this->assertEquals('car', t($txt, $txt, 1, 'test')); - $this->assertEquals('Search', t('Search', 'Search', 1, 'test')); - } - - /** - * Test t() with an extension language file in PHP mode - */ - public function testTranslationExtensionPhp() - { - $this->conf->set('translation.mode', 'php'); - $this->conf->set('translation.extensions.test', 'tests/utils/languages/'); - new Languages('en', $this->conf); - $txt = 'car'; // ignore me poedit - $this->assertEquals('car', t($txt, $txt, 1, 'test')); - $this->assertEquals('Search', t('Search', 'Search', 1, 'test')); - } -} diff --git a/tests/PluginManagerTest.php b/tests/PluginManagerTest.php deleted file mode 100644 index 81434fa2..00000000 --- a/tests/PluginManagerTest.php +++ /dev/null @@ -1,178 +0,0 @@ -pluginManager = new PluginManager($conf); - } - - /** - * Test plugin loading and hook execution. - */ - public function testPlugin(): void - { - PluginManager::$PLUGINS_PATH = self::$pluginPath; - $this->pluginManager->load([self::$pluginName]); - - $this->assertTrue(function_exists('hook_test_random')); - - $data = [0 => 'woot']; - $this->pluginManager->executeHooks('random', $data); - - static::assertCount(2, $data); - static::assertSame('woot', $data[1]); - - $data = [0 => 'woot']; - $this->pluginManager->executeHooks('random', $data, ['target' => 'test']); - - static::assertCount(2, $data); - static::assertSame('page test', $data[1]); - - $data = [0 => 'woot']; - $this->pluginManager->executeHooks('random', $data, ['loggedin' => true]); - - static::assertCount(2, $data); - static::assertEquals('loggedin', $data[1]); - - $data = [0 => 'woot']; - $this->pluginManager->executeHooks('random', $data, ['loggedin' => null]); - - static::assertCount(3, $data); - static::assertEquals('loggedin', $data[1]); - static::assertArrayHasKey(2, $data); - static::assertNull($data[2]); - } - - /** - * Test plugin loading and hook execution with an error: raise an incompatibility error. - */ - public function testPluginWithPhpError(): void - { - PluginManager::$PLUGINS_PATH = self::$pluginPath; - $this->pluginManager->load([self::$pluginName]); - - $this->assertTrue(function_exists('hook_test_error')); - - $data = []; - $this->pluginManager->executeHooks('error', $data); - - $this->assertRegExp( - '/test \[plugin incompatibility\]: Class [\'"]Unknown[\'"] not found/', - $this->pluginManager->getErrors()[0] - ); - } - - /** - * Test missing plugin loading. - */ - public function testPluginNotFound(): void - { - $this->pluginManager->load([]); - $this->pluginManager->load(['nope', 'renope']); - $this->addToAssertionCount(1); - } - - /** - * Test plugin metadata loading. - */ - public function testGetPluginsMeta(): void - { - PluginManager::$PLUGINS_PATH = self::$pluginPath; - $this->pluginManager->load([self::$pluginName]); - - $expectedParameters = [ - 'pop' => [ - 'value' => '', - 'desc' => 'pop description', - ], - 'hip' => [ - 'value' => '', - 'desc' => '', - ], - ]; - $meta = $this->pluginManager->getPluginsMeta(); - $this->assertEquals('test plugin', $meta[self::$pluginName]['description']); - $this->assertEquals($expectedParameters, $meta[self::$pluginName]['parameters']); - } - - /** - * Test plugin custom routes - note that there is no check on callable functions - */ - public function testRegisteredRoutes(): void - { - PluginManager::$PLUGINS_PATH = self::$pluginPath; - $this->pluginManager->load([self::$pluginName]); - - $expectedParameters = [ - [ - 'method' => 'GET', - 'route' => '/test', - 'callable' => 'getFunction', - ], - [ - 'method' => 'POST', - 'route' => '/custom', - 'callable' => 'postFunction', - ], - ]; - $meta = $this->pluginManager->getRegisteredRoutes(); - static::assertSame($expectedParameters, $meta[self::$pluginName]); - } - - /** - * Test plugin custom routes with invalid route - */ - public function testRegisteredRoutesInvalid(): void - { - $plugin = 'test_route_invalid'; - $this->pluginManager->load([$plugin]); - - $meta = $this->pluginManager->getRegisteredRoutes(); - static::assertSame([], $meta); - - $errors = $this->pluginManager->getErrors(); - static::assertSame(['test_route_invalid [plugin incompatibility]: trying to register invalid route.'], $errors); - } - - public function testSearchFilterPlugin(): void - { - PluginManager::$PLUGINS_PATH = self::$pluginPath; - $this->pluginManager->load([self::$pluginName]); - - static::assertNull($this->pluginManager->getFilterSearchEntryHooks()); - - static::assertTrue($this->pluginManager->filterSearchEntry(new Bookmark(), ['_result' => true])); - - static::assertCount(1, $this->pluginManager->getFilterSearchEntryHooks()); - static::assertSame('hook_test_filter_search_entry', $this->pluginManager->getFilterSearchEntryHooks()[0]); - - static::assertFalse($this->pluginManager->filterSearchEntry(new Bookmark(), ['_result' => false])); - } -} diff --git a/tests/TestCase.php b/tests/TestCase.php deleted file mode 100644 index 781e7aa3..00000000 --- a/tests/TestCase.php +++ /dev/null @@ -1,77 +0,0 @@ -expectExceptionMessageMatches($regularExpression); - } else { - parent::expectExceptionMessageRegExp($regularExpression); - } - } - - /** - * assertContains is now used for iterable, strings should use assertStringContainsString - */ - public function assertContainsPolyfill($expected, $actual, string $message = ''): void - { - if (is_string($actual) && method_exists($this, 'assertStringContainsString')) { - static::assertStringContainsString($expected, $actual, $message); - } else { - static::assertContains($expected, $actual, $message); - } - } - - /** - * assertNotContains is now used for iterable, strings should use assertStringNotContainsString - */ - public function assertNotContainsPolyfill($expected, $actual, string $message = ''): void - { - if (is_string($actual) && method_exists($this, 'assertStringNotContainsString')) { - static::assertStringNotContainsString($expected, $actual, $message); - } else { - static::assertNotContains($expected, $actual, $message); - } - } - - /** - * assertFileNotExists has been renamed in assertFileDoesNotExist - */ - public static function assertFileNotExists(string $filename, string $message = ''): void - { - if (method_exists(TestCase::class, 'assertFileDoesNotExist')) { - static::assertFileDoesNotExist($filename, $message); - } else { - parent::assertFileNotExists($filename, $message); - } - } - - /** - * assertRegExp has been renamed in assertMatchesRegularExpression - */ - public static function assertRegExp(string $pattern, string $string, string $message = ''): void - { - if (method_exists(TestCase::class, 'assertMatchesRegularExpression')) { - static::assertMatchesRegularExpression($pattern, $string, $message); - } else { - parent::assertRegExp($pattern, $string, $message); - } - } - - public function isInTestsContext(): bool - { - return true; - } -} diff --git a/tests/ThumbnailerTest.php b/tests/ThumbnailerTest.php deleted file mode 100644 index 2ea5568e..00000000 --- a/tests/ThumbnailerTest.php +++ /dev/null @@ -1,115 +0,0 @@ -conf = new ConfigManager('tests/utils/config/configJson'); - $this->conf->set('thumbnails.mode', Thumbnailer::MODE_ALL); - $this->conf->set('thumbnails.width', self::WIDTH); - $this->conf->set('thumbnails.height', self::HEIGHT); - $this->conf->set('dev.debug', true); - - $this->thumbnailer = new Thumbnailer($this->conf); - // cache files in the sandbox - WTConfigManager::addFile('tests/utils/config/wt.json'); - } - - protected function tearDown(): void - { - $this->rrmdirContent('sandbox/'); - } - - /** - * Test a thumbnail with a custom size in 'all' mode. - */ - public function testThumbnailAllValid() - { - $thumb = $this->thumbnailer->get('https://gitlab.com/shaarli/Shaarli'); - $this->assertNotFalse($thumb); - $image = imagecreatefromstring(file_get_contents($thumb)); - $this->assertEquals(self::WIDTH, imagesx($image)); - $this->assertEquals(self::HEIGHT, imagesy($image)); - } - - /** - * Test a thumbnail with a custom size in 'common' mode. - */ - public function testThumbnailCommonValid() - { - $this->conf->set('thumbnails.mode', Thumbnailer::MODE_COMMON); - $thumb = $this->thumbnailer->get('https://imgur.com/jlFgGpe'); - $this->assertNotFalse($thumb); - $image = imagecreatefromstring(file_get_contents($thumb)); - $this->assertEquals(self::WIDTH, imagesx($image)); - $this->assertEquals(self::HEIGHT, imagesy($image)); - } - - /** - * Test a thumbnail in 'common' mode which isn't include in common websites. - */ - public function testThumbnailCommonInvalid() - { - $this->conf->set('thumbnails.mode', Thumbnailer::MODE_COMMON); - $thumb = $this->thumbnailer->get('https://github.com/shaarli/Shaarli/'); - $this->assertFalse($thumb); - } - - /** - * Test a thumbnail that can't be retrieved. - */ - public function testThumbnailNotValid() - { - $oldlog = ini_get('error_log'); - ini_set('error_log', '/dev/null'); - - $thumbnailer = new Thumbnailer(new ConfigManager()); - $thumb = $thumbnailer->get('nope'); - $this->assertFalse($thumb); - - ini_set('error_log', $oldlog); - } - - protected function rrmdirContent($dir) - { - if (is_dir($dir)) { - $objects = scandir($dir); - foreach ($objects as $object) { - if ($object != "." && $object != "..") { - if (is_dir($dir . "/" . $object)) { - $this->rrmdirContent($dir . "/" . $object); - } else { - unlink($dir . "/" . $object); - } - } - } - } - } -} diff --git a/tests/TimeZoneTest.php b/tests/TimeZoneTest.php deleted file mode 100644 index ba520cf1..00000000 --- a/tests/TimeZoneTest.php +++ /dev/null @@ -1,104 +0,0 @@ -installedTimezones = [ - 'Antarctica/Syowa', - 'Europe/London', - 'Europe/Paris', - 'UTC' - ]; - } - - /** - * Generate a timezone selection form - */ - public function testGenerateTimeZoneForm() - { - $expected = [ - 'continents' => [ - 'Antarctica', - 'Europe', - 'UTC', - 'selected' => '', - ], - 'cities' => [ - ['continent' => 'Antarctica', 'city' => 'Syowa'], - ['continent' => 'Europe', 'city' => 'London'], - ['continent' => 'Europe', 'city' => 'Paris'], - ['continent' => 'UTC', 'city' => 'UTC'], - 'selected' => '', - ] - ]; - - list($continents, $cities) = generateTimeZoneData($this->installedTimezones); - - $this->assertEquals($expected['continents'], $continents); - $this->assertEquals($expected['cities'], $cities); - } - - /** - * Generate a timezone selection form, with a preselected timezone - */ - public function testGenerateTimeZoneFormPreselected() - { - $expected = [ - 'continents' => [ - 'Antarctica', - 'Europe', - 'UTC', - 'selected' => 'Antarctica', - ], - 'cities' => [ - ['continent' => 'Antarctica', 'city' => 'Syowa'], - ['continent' => 'Europe', 'city' => 'London'], - ['continent' => 'Europe', 'city' => 'Paris'], - ['continent' => 'UTC', 'city' => 'UTC'], - 'selected' => 'Syowa', - ] - ]; - - list($continents, $cities) = generateTimeZoneData($this->installedTimezones, 'Antarctica/Syowa'); - - $this->assertEquals($expected['continents'], $continents); - $this->assertEquals($expected['cities'], $cities); - } - - /** - * Check valid timezones - */ - public function testValidTimeZone() - { - $this->assertTrue(isTimeZoneValid('America', 'Argentina/Ushuaia')); - $this->assertTrue(isTimeZoneValid('Europe', 'Oslo')); - } - - /** - * Check invalid timezones - */ - public function testInvalidTimeZone() - { - $this->assertFalse(isTimeZoneValid('CEST', 'CEST')); - $this->assertFalse(isTimeZoneValid('Europe', 'Atlantis')); - $this->assertFalse(isTimeZoneValid('Middle_Earth', 'Moria')); - $this->assertFalse(isTimeZoneValid('UTC', 'UTC')); - } -} diff --git a/tests/UtilsTest.php b/tests/UtilsTest.php deleted file mode 100644 index a18c01bc..00000000 --- a/tests/UtilsTest.php +++ /dev/null @@ -1,463 +0,0 @@ -assertEquals('CyAAJw', smallHash('http://test.io')); - $this->assertEquals(6, strlen(smallHash('https://github.com'))); - } - - /** - * Look for a substring at the beginning of a string - */ - public function testStartsWithCaseInsensitive() - { - $this->assertTrue(startsWith('Lorem ipsum', 'lorem', false)); - $this->assertTrue(startsWith('Lorem ipsum', 'LoReM i', false)); - } - - /** - * Look for a substring at the beginning of a string (case-sensitive) - */ - public function testStartsWithCaseSensitive() - { - $this->assertTrue(startsWith('Lorem ipsum', 'Lorem', true)); - $this->assertFalse(startsWith('Lorem ipsum', 'lorem', true)); - $this->assertFalse(startsWith('Lorem ipsum', 'LoReM i', true)); - } - - /** - * Look for a substring at the beginning of a string (Unicode) - */ - public function testStartsWithSpecialChars() - { - $this->assertTrue(startsWith('å!ùµ', 'å!', false)); - $this->assertTrue(startsWith('µ$åù', 'µ$', true)); - } - - /** - * Look for a substring at the end of a string - */ - public function testEndsWithCaseInsensitive() - { - $this->assertTrue(endsWith('Lorem ipsum', 'ipsum', false)); - $this->assertTrue(endsWith('Lorem ipsum', 'm IpsUM', false)); - } - - /** - * Look for a substring at the end of a string (case-sensitive) - */ - public function testEndsWithCaseSensitive() - { - $this->assertTrue(endsWith('lorem Ipsum', 'Ipsum', true)); - $this->assertFalse(endsWith('lorem Ipsum', 'ipsum', true)); - $this->assertFalse(endsWith('lorem Ipsum', 'M IPsuM', true)); - } - - /** - * Look for a substring at the end of a string (Unicode) - */ - public function testEndsWithSpecialChars() - { - $this->assertTrue(endsWith('å!ùµ', 'ùµ', false)); - $this->assertTrue(endsWith('µ$åù', 'åù', true)); - } - - /** - * Check valid date strings, according to a DateTime format - */ - public function testCheckValidDateFormat() - { - $this->assertTrue(checkDateFormat('Ymd', '20150627')); - $this->assertTrue(checkDateFormat('Y-m-d', '2015-06-27')); - } - - /** - * Check erroneous date strings, according to a DateTime format - */ - public function testCheckInvalidDateFormat() - { - $this->assertFalse(checkDateFormat('Ymd', '2015')); - $this->assertFalse(checkDateFormat('Y-m-d', '2015-06')); - $this->assertFalse(checkDateFormat('Ymd', 'DeLorean')); - } - - /** - * Test generate location with valid data. - */ - public function testGenerateLocation() - { - $ref = 'http://localhost/?test'; - $this->assertEquals($ref, generateLocation($ref, 'localhost')); - $ref = 'http://localhost:8080/?test'; - $this->assertEquals($ref, generateLocation($ref, 'localhost:8080')); - $ref = '?localreferer#hash'; - $this->assertEquals($ref, generateLocation($ref, 'localhost:8080')); - } - - /** - * Test generate location - anti loop. - */ - public function testGenerateLocationLoop() - { - $ref = 'http://localhost/?test'; - $this->assertEquals('./?', generateLocation($ref, 'localhost', ['test'])); - } - - /** - * Test generate location - from other domain. - */ - public function testGenerateLocationOut() - { - $ref = 'http://somewebsite.com/?test'; - $this->assertEquals('./?', generateLocation($ref, 'localhost')); - } - - - /** - * Test generateSecretApi. - */ - public function testGenerateSecretApi() - { - $this->assertEquals(12, strlen(generate_api_secret('foo', 'bar'))); - } - - /** - * Test generateSecretApi with invalid parameters. - */ - public function testGenerateSecretApiInvalid() - { - $this->assertFalse(generate_api_secret('', '')); - $this->assertFalse(generate_api_secret(false, false)); - } - - /** - * Test normalize_spaces. - */ - public function testNormalizeSpace() - { - $str = ' foo bar is important '; - $this->assertEquals('foo bar is important', normalize_spaces($str)); - $this->assertEquals('foo', normalize_spaces('foo')); - $this->assertEquals('', normalize_spaces('')); - $this->assertEquals(null, normalize_spaces(null)); - } - - /** - * Test arrays_combine - */ - public function testCartesianProductGenerator() - { - $arr = [['ab', 'cd'], ['ef', 'gh'], ['ij', 'kl'], ['m']]; - $expected = [ - ['ab', 'ef', 'ij', 'm'], - ['ab', 'ef', 'kl', 'm'], - ['ab', 'gh', 'ij', 'm'], - ['ab', 'gh', 'kl', 'm'], - ['cd', 'ef', 'ij', 'm'], - ['cd', 'ef', 'kl', 'm'], - ['cd', 'gh', 'ij', 'm'], - ['cd', 'gh', 'kl', 'm'], - ]; - $this->assertEquals($expected, iterator_to_array(cartesian_product_generator($arr))); - } - - /** - * Test date_format() with invalid parameter. - */ - public function testDateFormatInvalid() - { - $this->assertFalse(format_date([])); - $this->assertFalse(format_date(null)); - } - - /** - * Test is_integer_mixed with valid values - */ - public function testIsIntegerMixedValid() - { - $this->assertTrue(is_integer_mixed(12)); - $this->assertTrue(is_integer_mixed('12')); - $this->assertTrue(is_integer_mixed(-12)); - $this->assertTrue(is_integer_mixed('-12')); - $this->assertTrue(is_integer_mixed(0)); - $this->assertTrue(is_integer_mixed('0')); - $this->assertTrue(is_integer_mixed(0x0a)); - } - - /** - * Test is_integer_mixed with invalid values - */ - public function testIsIntegerMixedInvalid() - { - $this->assertFalse(is_integer_mixed(true)); - $this->assertFalse(is_integer_mixed(false)); - $this->assertFalse(is_integer_mixed([])); - $this->assertFalse(is_integer_mixed(['test'])); - $this->assertFalse(is_integer_mixed([12])); - $this->assertFalse(is_integer_mixed(new DateTime())); - $this->assertFalse(is_integer_mixed('0x0a')); - $this->assertFalse(is_integer_mixed('12k')); - $this->assertFalse(is_integer_mixed('k12')); - $this->assertFalse(is_integer_mixed('')); - } - - /** - * Test return_bytes - */ - public function testReturnBytes() - { - $this->assertEquals(2 * 1024, return_bytes('2k')); - $this->assertEquals(2 * 1024, return_bytes('2K')); - $this->assertEquals(2 * (pow(1024, 2)), return_bytes('2m')); - $this->assertEquals(2 * (pow(1024, 2)), return_bytes('2M')); - $this->assertEquals(2 * (pow(1024, 3)), return_bytes('2g')); - $this->assertEquals(2 * (pow(1024, 3)), return_bytes('2G')); - $this->assertEquals(374, return_bytes('374')); - $this->assertEquals(374, return_bytes(374)); - $this->assertEquals(0, return_bytes('0')); - $this->assertEquals(0, return_bytes(0)); - $this->assertEquals(-1, return_bytes('-1')); - $this->assertEquals(-1, return_bytes(-1)); - $this->assertEquals('', return_bytes('')); - } - - /** - * Test human_bytes - */ - public function testHumanBytes() - { - $this->assertEquals('2' . t('kiB'), human_bytes(2 * 1024)); - $this->assertEquals('2' . t('kiB'), human_bytes(strval(2 * 1024))); - $this->assertEquals('2' . t('MiB'), human_bytes(2 * (pow(1024, 2)))); - $this->assertEquals('2' . t('MiB'), human_bytes(strval(2 * (pow(1024, 2))))); - $this->assertEquals('2' . t('GiB'), human_bytes(2 * (pow(1024, 3)))); - $this->assertEquals('2' . t('GiB'), human_bytes(strval(2 * (pow(1024, 3))))); - $this->assertEquals('374' . t('B'), human_bytes(374)); - $this->assertEquals('374' . t('B'), human_bytes('374')); - $this->assertEquals('232' . t('kiB'), human_bytes(237481)); - $this->assertEquals(t('Unlimited'), human_bytes('0')); - $this->assertEquals(t('Unlimited'), human_bytes(0)); - $this->assertEquals(t('Setting not set'), human_bytes('')); - } - - /** - * Test get_max_upload_size with formatting - */ - public function testGetMaxUploadSize() - { - $this->assertEquals('1' . t('MiB'), get_max_upload_size(2097152, '1024k')); - $this->assertEquals('1' . t('MiB'), get_max_upload_size('1m', '2m')); - $this->assertEquals('100' . t('B'), get_max_upload_size(100, 100)); - } - - /** - * Test get_max_upload_size without formatting - */ - public function testGetMaxUploadSizeRaw() - { - $this->assertEquals('1048576', get_max_upload_size(2097152, '1024k', false)); - $this->assertEquals('1048576', get_max_upload_size('1m', '2m', false)); - $this->assertEquals('100', get_max_upload_size(100, 100, false)); - } - - /** - * Test alphabetical_sort by value, not reversed, with php-intl. - */ - public function testAlphabeticalSortByValue() - { - $arr = [ - 'zZz', - 'éee', - 'éae', - 'eee', - 'A', - 'a', - 'zzz', - ]; - $expected = [ - 'a', - 'A', - 'éae', - 'eee', - 'éee', - 'zzz', - 'zZz', - ]; - - alphabetical_sort($arr); - $this->assertEquals($expected, $arr); - } - - /** - * Test alphabetical_sort by value, reversed, with php-intl. - */ - public function testAlphabeticalSortByValueReversed() - { - $arr = [ - 'zZz', - 'éee', - 'éae', - 'eee', - 'A', - 'a', - 'zzz', - ]; - $expected = [ - 'zZz', - 'zzz', - 'éee', - 'eee', - 'éae', - 'A', - 'a', - ]; - - alphabetical_sort($arr, true); - $this->assertEquals($expected, $arr); - } - - /** - * Test alphabetical_sort by keys, not reversed, with php-intl. - */ - public function testAlphabeticalSortByKeys() - { - $arr = [ - 'zZz' => true, - 'éee' => true, - 'éae' => true, - 'eee' => true, - 'A' => true, - 'a' => true, - 'zzz' => true, - ]; - $expected = [ - 'a' => true, - 'A' => true, - 'éae' => true, - 'eee' => true, - 'éee' => true, - 'zzz' => true, - 'zZz' => true, - ]; - - alphabetical_sort($arr, true, true); - $this->assertEquals($expected, $arr); - } - - /** - * Test alphabetical_sort by keys, reversed, with php-intl. - */ - public function testAlphabeticalSortByKeysReversed() - { - $arr = [ - 'zZz' => true, - 'éee' => true, - 'éae' => true, - 'eee' => true, - 'A' => true, - 'a' => true, - 'zzz' => true, - ]; - $expected = [ - 'zZz' => true, - 'zzz' => true, - 'éee' => true, - 'eee' => true, - 'éae' => true, - 'A' => true, - 'a' => true, - ]; - - alphabetical_sort($arr, true, true); - $this->assertEquals($expected, $arr); - } -} diff --git a/tests/api/ApiMiddlewareTest.php b/tests/api/ApiMiddlewareTest.php deleted file mode 100644 index 6e222681..00000000 --- a/tests/api/ApiMiddlewareTest.php +++ /dev/null @@ -1,262 +0,0 @@ -conf = new ConfigManager('tests/utils/config/configJson'); - $this->conf->set('api.secret', 'NapoleonWasALizard'); - - $this->refDB = new ReferenceLinkDB(); - $this->refDB->write(self::$testDatastore); - - $history = new History('sandbox/history.php'); - - $this->container = new Container(); - $this->container['conf'] = $this->conf; - $this->container['history'] = $history; - $this->container['pluginManager'] = new PluginManager($this->conf); - } - - /** - * After every test, remove the test datastore. - */ - protected function tearDown(): void - { - @unlink(self::$testDatastore); - } - - /** - * Invoke the middleware with a valid token - */ - public function testInvokeMiddlewareWithValidToken(): void - { - $next = function (Request $request, Response $response): Response { - return $response; - }; - $mw = new ApiMiddleware($this->container); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'REQUEST_URI' => '/echo', - 'HTTP_AUTHORIZATION' => 'Bearer ' . ApiUtilsTest::generateValidJwtToken('NapoleonWasALizard'), - ]); - $request = Request::createFromEnvironment($env); - $response = new Response(); - /** @var Response $response */ - $response = $mw($request, $response, $next); - - $this->assertEquals(200, $response->getStatusCode()); - } - - /** - * Invoke the middleware with a valid token - * Using specific Apache CGI redirected authorization. - */ - public function testInvokeMiddlewareWithValidTokenFromRedirectedHeader(): void - { - $next = function (Request $request, Response $response): Response { - return $response; - }; - - $token = 'Bearer ' . ApiUtilsTest::generateValidJwtToken('NapoleonWasALizard'); - $this->container->environment['REDIRECT_HTTP_AUTHORIZATION'] = $token; - $mw = new ApiMiddleware($this->container); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'REQUEST_URI' => '/echo', - ]); - $request = Request::createFromEnvironment($env); - $response = new Response(); - /** @var Response $response */ - $response = $mw($request, $response, $next); - - $this->assertEquals(200, $response->getStatusCode()); - } - - /** - * Invoke the middleware with the API disabled: - * should return a 401 error Unauthorized. - */ - public function testInvokeMiddlewareApiDisabled() - { - $this->conf->set('api.enabled', false); - $mw = new ApiMiddleware($this->container); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'REQUEST_URI' => '/echo', - ]); - $request = Request::createFromEnvironment($env); - $response = new Response(); - /** @var Response $response */ - $response = $mw($request, $response, null); - - $this->assertEquals(401, $response->getStatusCode()); - $body = json_decode((string) $response->getBody()); - $this->assertEquals('Not authorized', $body); - } - - /** - * Invoke the middleware with the API disabled in debug mode: - * should return a 401 error Unauthorized - with a specific message and a stacktrace. - */ - public function testInvokeMiddlewareApiDisabledDebug() - { - $this->conf->set('api.enabled', false); - $this->conf->set('dev.debug', true); - $mw = new ApiMiddleware($this->container); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'REQUEST_URI' => '/echo', - ]); - $request = Request::createFromEnvironment($env); - $response = new Response(); - /** @var Response $response */ - $response = $mw($request, $response, null); - - $this->assertEquals(401, $response->getStatusCode()); - $body = json_decode((string) $response->getBody()); - $this->assertEquals('Not authorized: API is disabled', $body->message); - $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace); - } - - /** - * Invoke the middleware without a token (debug): - * should return a 401 error Unauthorized - with a specific message and a stacktrace. - */ - public function testInvokeMiddlewareNoTokenProvidedDebug() - { - $this->conf->set('dev.debug', true); - $mw = new ApiMiddleware($this->container); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'REQUEST_URI' => '/echo', - ]); - $request = Request::createFromEnvironment($env); - $response = new Response(); - /** @var Response $response */ - $response = $mw($request, $response, null); - - $this->assertEquals(401, $response->getStatusCode()); - $body = json_decode((string) $response->getBody()); - $this->assertEquals('Not authorized: JWT token not provided', $body->message); - $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace); - } - - /** - * Invoke the middleware without a secret set in settings (debug): - * should return a 401 error Unauthorized - with a specific message and a stacktrace. - */ - public function testInvokeMiddlewareNoSecretSetDebug() - { - $this->conf->set('dev.debug', true); - $this->conf->set('api.secret', ''); - $mw = new ApiMiddleware($this->container); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'REQUEST_URI' => '/echo', - 'HTTP_AUTHORIZATION' => 'Bearer jwt', - ]); - $request = Request::createFromEnvironment($env); - $response = new Response(); - /** @var Response $response */ - $response = $mw($request, $response, null); - - $this->assertEquals(401, $response->getStatusCode()); - $body = json_decode((string) $response->getBody()); - $this->assertEquals('Not authorized: Token secret must be set in Shaarli\'s administration', $body->message); - $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace); - } - - /** - * Invoke the middleware with an invalid JWT token header - */ - public function testInvalidJwtAuthHeaderDebug() - { - $this->conf->set('dev.debug', true); - $mw = new ApiMiddleware($this->container); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'REQUEST_URI' => '/echo', - 'HTTP_AUTHORIZATION' => 'PolarBearer jwt', - ]); - $request = Request::createFromEnvironment($env); - $response = new Response(); - /** @var Response $response */ - $response = $mw($request, $response, null); - - $this->assertEquals(401, $response->getStatusCode()); - $body = json_decode((string) $response->getBody()); - $this->assertEquals('Not authorized: Invalid JWT header', $body->message); - $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace); - } - - /** - * Invoke the middleware with an invalid JWT token (debug): - * should return a 401 error Unauthorized - with a specific message and a stacktrace. - * - * Note: specific JWT errors tests are handled in ApiUtilsTest. - */ - public function testInvokeMiddlewareInvalidJwtDebug() - { - $this->conf->set('dev.debug', true); - $mw = new ApiMiddleware($this->container); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'REQUEST_URI' => '/echo', - 'HTTP_AUTHORIZATION' => 'Bearer jwt', - ]); - $request = Request::createFromEnvironment($env); - $response = new Response(); - /** @var Response $response */ - $response = $mw($request, $response, null); - - $this->assertEquals(401, $response->getStatusCode()); - $body = json_decode((string) $response->getBody()); - $this->assertEquals('Not authorized: Malformed JWT token', $body->message); - $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace); - } -} diff --git a/tests/api/ApiUtilsTest.php b/tests/api/ApiUtilsTest.php deleted file mode 100644 index db396228..00000000 --- a/tests/api/ApiUtilsTest.php +++ /dev/null @@ -1,354 +0,0 @@ -assertTrue(ApiUtils::validateJwtToken(self::generateValidJwtToken($secret), $secret)); - } - - /** - * Test validateJwtToken() with a malformed JWT token. - */ - public function testValidateJwtTokenMalformed() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); - $this->expectExceptionMessage('Malformed JWT token'); - - $token = 'ABC.DEF'; - ApiUtils::validateJwtToken($token, 'foo'); - } - - /** - * Test validateJwtToken() with an empty JWT token. - */ - public function testValidateJwtTokenMalformedEmpty() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); - $this->expectExceptionMessage('Malformed JWT token'); - - $token = false; - ApiUtils::validateJwtToken($token, 'foo'); - } - - /** - * Test validateJwtToken() with a JWT token without header. - */ - public function testValidateJwtTokenMalformedEmptyHeader() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); - $this->expectExceptionMessage('Malformed JWT token'); - - $token = '.payload.signature'; - ApiUtils::validateJwtToken($token, 'foo'); - } - - /** - * Test validateJwtToken() with a JWT token without payload - */ - public function testValidateJwtTokenMalformedEmptyPayload() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); - $this->expectExceptionMessage('Malformed JWT token'); - - $token = 'header..signature'; - ApiUtils::validateJwtToken($token, 'foo'); - } - - /** - * Test validateJwtToken() with a JWT token with an empty signature. - */ - public function testValidateJwtTokenInvalidSignatureEmpty() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); - $this->expectExceptionMessage('Invalid JWT signature'); - - $token = 'header.payload.'; - ApiUtils::validateJwtToken($token, 'foo'); - } - - /** - * Test validateJwtToken() with a JWT token with an invalid signature. - */ - public function testValidateJwtTokenInvalidSignature() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); - $this->expectExceptionMessage('Invalid JWT signature'); - - $token = 'header.payload.nope'; - ApiUtils::validateJwtToken($token, 'foo'); - } - - /** - * Test validateJwtToken() with a JWT token with a signature generated with the wrong API secret. - */ - public function testValidateJwtTokenInvalidSignatureSecret() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); - $this->expectExceptionMessage('Invalid JWT signature'); - - ApiUtils::validateJwtToken(self::generateValidJwtToken('foo'), 'bar'); - } - - /** - * Test validateJwtToken() with a JWT token with a an invalid header (not JSON). - */ - public function testValidateJwtTokenInvalidHeader() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); - $this->expectExceptionMessage('Invalid JWT header'); - - $token = $this->generateCustomJwtToken('notJSON', '{"JSON":1}', 'secret'); - ApiUtils::validateJwtToken($token, 'secret'); - } - - /** - * Test validateJwtToken() with a JWT token with a an invalid payload (not JSON). - */ - public function testValidateJwtTokenInvalidPayload() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); - $this->expectExceptionMessage('Invalid JWT payload'); - - $token = $this->generateCustomJwtToken('{"JSON":1}', 'notJSON', 'secret'); - ApiUtils::validateJwtToken($token, 'secret'); - } - - /** - * Test validateJwtToken() with a JWT token without issued time. - */ - public function testValidateJwtTokenInvalidTimeEmpty() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); - $this->expectExceptionMessage('Invalid JWT issued time'); - - $token = $this->generateCustomJwtToken('{"JSON":1}', '{"JSON":1}', 'secret'); - ApiUtils::validateJwtToken($token, 'secret'); - } - - /** - * Test validateJwtToken() with an expired JWT token. - */ - public function testValidateJwtTokenInvalidTimeExpired() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); - $this->expectExceptionMessage('Invalid JWT issued time'); - - $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() - 600) . '}', 'secret'); - ApiUtils::validateJwtToken($token, 'secret'); - } - - /** - * Test validateJwtToken() with a JWT token issued in the future. - */ - public function testValidateJwtTokenInvalidTimeFuture() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); - $this->expectExceptionMessage('Invalid JWT issued time'); - - $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() + 60) . '}', 'secret'); - ApiUtils::validateJwtToken($token, 'secret'); - } - - /** - * Test formatLink() with a link using all useful fields. - */ - public function testFormatLinkComplete() - { - $indexUrl = 'https://domain.tld/sub/'; - $data = [ - 'id' => 12, - 'url' => 'http://lol.lol', - 'shorturl' => 'abc', - 'title' => 'Important Title', - 'description' => 'It is very lol' . PHP_EOL . 'new line', - 'tags' => 'blip .blop ', - 'private' => '1', - 'created' => \DateTime::createFromFormat('Ymd_His', '20170107_160102'), - 'updated' => \DateTime::createFromFormat('Ymd_His', '20170107_160612'), - ]; - $bookmark = new Bookmark(); - $bookmark->fromArray($data); - - $expected = [ - 'id' => 12, - 'url' => 'http://lol.lol', - 'shorturl' => 'abc', - 'title' => 'Important Title', - 'description' => 'It is very lol' . PHP_EOL . 'new line', - 'tags' => ['blip', '.blop'], - 'private' => true, - 'created' => '2017-01-07T16:01:02+00:00', - 'updated' => '2017-01-07T16:06:12+00:00', - ]; - - $this->assertEquals($expected, ApiUtils::formatLink($bookmark, $indexUrl)); - } - - /** - * Test formatLink() with only minimal fields filled, and internal link. - */ - public function testFormatLinkMinimalNote() - { - $indexUrl = 'https://domain.tld/sub/'; - $data = [ - 'id' => 12, - 'url' => '?abc', - 'shorturl' => 'abc', - 'title' => 'Note', - 'description' => '', - 'tags' => '', - 'private' => '', - 'created' => \DateTime::createFromFormat('Ymd_His', '20170107_160102'), - ]; - $bookmark = new Bookmark(); - $bookmark->fromArray($data); - - $expected = [ - 'id' => 12, - 'url' => 'https://domain.tld/sub/?abc', - 'shorturl' => 'abc', - 'title' => 'Note', - 'description' => '', - 'tags' => [], - 'private' => false, - 'created' => '2017-01-07T16:01:02+00:00', - 'updated' => '', - ]; - - $this->assertEquals($expected, ApiUtils::formatLink($bookmark, $indexUrl)); - } - - /** - * Test updateLink with valid data, and also unnecessary fields. - */ - public function testUpdateLink() - { - $created = \DateTime::createFromFormat('Ymd_His', '20170107_160102'); - $data = [ - 'id' => 12, - 'url' => '?abc', - 'shorturl' => 'abc', - 'title' => 'Note', - 'description' => '', - 'tags' => '', - 'private' => '', - 'created' => $created, - ]; - $old = new Bookmark(); - $old->fromArray($data); - - $data = [ - 'id' => 13, - 'shorturl' => 'nope', - 'url' => 'http://somewhere.else', - 'title' => 'Le Cid', - 'description' => 'Percé jusques au fond du cœur [...]', - 'tags' => 'corneille rodrigue', - 'private' => true, - 'created' => 'creation', - 'updated' => 'updation', - ]; - $new = new Bookmark(); - $new->fromArray($data); - - $result = ApiUtils::updateLink($old, $new); - $this->assertEquals(12, $result->getId()); - $this->assertEquals('http://somewhere.else', $result->getUrl()); - $this->assertEquals('abc', $result->getShortUrl()); - $this->assertEquals('Le Cid', $result->getTitle()); - $this->assertEquals('Percé jusques au fond du cœur [...]', $result->getDescription()); - $this->assertEquals('corneille rodrigue', $result->getTagsString()); - $this->assertEquals(true, $result->isPrivate()); - $this->assertEquals($created, $result->getCreated()); - } - - /** - * Test updateLink with minimal data. - */ - public function testUpdateLinkMinimal() - { - $created = \DateTime::createFromFormat('Ymd_His', '20170107_160102'); - $data = [ - 'id' => 12, - 'url' => '?abc', - 'shorturl' => 'abc', - 'title' => 'Note', - 'description' => 'Interesting description!', - 'tags' => 'doggo', - 'private' => true, - 'created' => $created, - ]; - $old = new Bookmark(); - $old->fromArray($data); - - $new = new Bookmark(); - - $result = ApiUtils::updateLink($old, $new); - $this->assertEquals(12, $result->getId()); - $this->assertEquals('', $result->getUrl()); - $this->assertEquals('abc', $result->getShortUrl()); - $this->assertEquals('', $result->getTitle()); - $this->assertEquals('', $result->getDescription()); - $this->assertEquals('', $result->getTagsString()); - $this->assertEquals(false, $result->isPrivate()); - $this->assertEquals($created, $result->getCreated()); - } -} diff --git a/tests/api/controllers/history/HistoryTest.php b/tests/api/controllers/history/HistoryTest.php deleted file mode 100644 index f0596b91..00000000 --- a/tests/api/controllers/history/HistoryTest.php +++ /dev/null @@ -1,215 +0,0 @@ -conf = new ConfigManager('tests/utils/config/configJson'); - $this->refHistory = new ReferenceHistory(); - $this->refHistory->write(self::$testHistory); - $this->container = new Container(); - $this->container['conf'] = $this->conf; - $this->container['db'] = true; - $this->container['history'] = new History(self::$testHistory); - - $this->controller = new HistoryController($this->container); - } - - /** - * After every test, remove the test datastore. - */ - protected function tearDown(): void - { - @unlink(self::$testHistory); - } - - /** - * Test /history service without parameter. - */ - public function testGetHistory() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->getHistory($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - - $this->assertEquals($this->refHistory->count(), count($data)); - - $this->assertEquals(History::DELETED, $data[0]['event']); - $this->assertEquals( - \DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM), - $data[0]['datetime'] - ); - $this->assertEquals(124, $data[0]['id']); - - $this->assertEquals(History::SETTINGS, $data[1]['event']); - $this->assertEquals( - \DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM), - $data[1]['datetime'] - ); - $this->assertNull($data[1]['id']); - - $this->assertEquals(History::UPDATED, $data[2]['event']); - $this->assertEquals( - \DateTime::createFromFormat('Ymd_His', '20170301_121214')->format(\DateTime::ATOM), - $data[2]['datetime'] - ); - $this->assertEquals(123, $data[2]['id']); - - $this->assertEquals(History::CREATED, $data[3]['event']); - $this->assertEquals( - \DateTime::createFromFormat('Ymd_His', '20170201_121214')->format(\DateTime::ATOM), - $data[3]['datetime'] - ); - $this->assertEquals(124, $data[3]['id']); - - $this->assertEquals(History::CREATED, $data[4]['event']); - $this->assertEquals( - \DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM), - $data[4]['datetime'] - ); - $this->assertEquals(123, $data[4]['id']); - } - - /** - * Test /history service with limit parameter. - */ - public function testGetHistoryLimit() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'limit=1' - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->getHistory($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - - $this->assertEquals(1, count($data)); - - $this->assertEquals(History::DELETED, $data[0]['event']); - $this->assertEquals( - \DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM), - $data[0]['datetime'] - ); - $this->assertEquals(124, $data[0]['id']); - } - - /** - * Test /history service with offset parameter. - */ - public function testGetHistoryOffset() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'offset=4' - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->getHistory($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - - $this->assertEquals(1, count($data)); - - $this->assertEquals(History::CREATED, $data[0]['event']); - $this->assertEquals( - \DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM), - $data[0]['datetime'] - ); - $this->assertEquals(123, $data[0]['id']); - } - - /** - * Test /history service with since parameter. - */ - public function testGetHistorySince() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'since=2017-03-03T00:00:00%2B00:00' - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->getHistory($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - - $this->assertEquals(1, count($data)); - - $this->assertEquals(History::DELETED, $data[0]['event']); - $this->assertEquals( - \DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM), - $data[0]['datetime'] - ); - $this->assertEquals(124, $data[0]['id']); - } - - /** - * Test /history service with since parameter. - */ - public function testGetHistorySinceOffsetLimit() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'since=2017-02-01T00:00:00%2B00:00&offset=1&limit=1' - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->getHistory($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - - $this->assertEquals(1, count($data)); - - $this->assertEquals(History::SETTINGS, $data[0]['event']); - $this->assertEquals( - \DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM), - $data[0]['datetime'] - ); - } -} diff --git a/tests/api/controllers/info/InfoTest.php b/tests/api/controllers/info/InfoTest.php deleted file mode 100644 index 2b0fd510..00000000 --- a/tests/api/controllers/info/InfoTest.php +++ /dev/null @@ -1,131 +0,0 @@ -conf = new ConfigManager('tests/utils/config/configJson'); - $this->conf->set('resource.datastore', self::$testDatastore); - $this->refDB = new ReferenceLinkDB(); - $this->refDB->write(self::$testDatastore); - $this->pluginManager = new PluginManager($this->conf); - $history = new History('sandbox/history.php'); - - $this->container = new Container(); - $this->container['conf'] = $this->conf; - $this->container['db'] = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $history, - $mutex, - true - ); - $this->container['history'] = null; - - $this->controller = new Info($this->container); - } - - /** - * After every test, remove the test datastore. - */ - protected function tearDown(): void - { - @unlink(self::$testDatastore); - } - - /** - * Test /info service. - */ - public function testGetInfo() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->getInfo($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - - $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, $data['global_counter']); - $this->assertEquals(2, $data['private_counter']); - $this->assertEquals('Shaarli', $data['settings']['title']); - $this->assertEquals('?', $data['settings']['header_link']); - $this->assertEquals('Europe/Paris', $data['settings']['timezone']); - $this->assertEquals(ConfigManager::$DEFAULT_PLUGINS, $data['settings']['enabled_plugins']); - $this->assertEquals(true, $data['settings']['default_private_links']); - - $title = 'My bookmarks'; - $headerLink = 'http://shaarli.tld'; - $timezone = 'Europe/Paris'; - $enabledPlugins = ['foo', 'bar']; - $defaultPrivateLinks = true; - $this->conf->set('general.title', $title); - $this->conf->set('general.header_link', $headerLink); - $this->conf->set('general.timezone', $timezone); - $this->conf->set('general.enabled_plugins', $enabledPlugins); - $this->conf->set('privacy.default_private_links', $defaultPrivateLinks); - - $response = $this->controller->getInfo($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - - $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, $data['global_counter']); - $this->assertEquals(2, $data['private_counter']); - $this->assertEquals($title, $data['settings']['title']); - $this->assertEquals($headerLink, $data['settings']['header_link']); - $this->assertEquals($timezone, $data['settings']['timezone']); - $this->assertEquals($enabledPlugins, $data['settings']['enabled_plugins']); - $this->assertEquals($defaultPrivateLinks, $data['settings']['default_private_links']); - } -} diff --git a/tests/api/controllers/links/DeleteLinkTest.php b/tests/api/controllers/links/DeleteLinkTest.php deleted file mode 100644 index 2c3c3ecc..00000000 --- a/tests/api/controllers/links/DeleteLinkTest.php +++ /dev/null @@ -1,153 +0,0 @@ -mutex = new NoMutex(); - $this->conf = new ConfigManager('tests/utils/config/configJson'); - $this->conf->set('resource.datastore', self::$testDatastore); - $this->refDB = new ReferenceLinkDB(); - $this->refDB->write(self::$testDatastore); - $refHistory = new ReferenceHistory(); - $refHistory->write(self::$testHistory); - $this->history = new History(self::$testHistory); - $this->pluginManager = new PluginManager($this->conf); - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $this->container = new Container(); - $this->container['conf'] = $this->conf; - $this->container['db'] = $this->bookmarkService; - $this->container['history'] = $this->history; - - $this->controller = new Links($this->container); - } - - /** - * After each test, remove the test datastore. - */ - protected function tearDown(): void - { - @unlink(self::$testDatastore); - @unlink(self::$testHistory); - } - - /** - * Test DELETE link endpoint: the link should be removed. - */ - public function testDeleteLinkValid() - { - $id = '41'; - $this->assertTrue($this->bookmarkService->exists($id)); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'DELETE', - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->deleteLink($request, new Response(), ['id' => $id]); - $this->assertEquals(204, $response->getStatusCode()); - $this->assertEmpty((string) $response->getBody()); - - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - $this->assertFalse($this->bookmarkService->exists($id)); - - $historyEntry = $this->history->getHistory()[0]; - $this->assertEquals(History::DELETED, $historyEntry['event']); - $this->assertTrue( - (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] - ); - $this->assertEquals($id, $historyEntry['id']); - } - - /** - * Test DELETE link endpoint: reach not existing ID. - */ - public function testDeleteLink404() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiLinkNotFoundException::class); - - $id = -1; - $this->assertFalse($this->bookmarkService->exists($id)); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'DELETE', - ]); - $request = Request::createFromEnvironment($env); - - $this->controller->deleteLink($request, new Response(), ['id' => $id]); - } -} diff --git a/tests/api/controllers/links/GetLinkIdTest.php b/tests/api/controllers/links/GetLinkIdTest.php deleted file mode 100644 index 5fbb7505..00000000 --- a/tests/api/controllers/links/GetLinkIdTest.php +++ /dev/null @@ -1,147 +0,0 @@ -conf = new ConfigManager('tests/utils/config/configJson'); - $this->conf->set('resource.datastore', self::$testDatastore); - $this->refDB = new ReferenceLinkDB(); - $this->refDB->write(self::$testDatastore); - $history = new History('sandbox/history.php'); - - $this->container = new Container(); - $this->container['conf'] = $this->conf; - $pluginManager = new PluginManager($this->conf); - $this->container['db'] = new BookmarkFileService( - $this->conf, - $pluginManager, - $history, - $mutex, - true - ); - $this->container['history'] = null; - - $this->controller = new Links($this->container); - } - - /** - * After each test, remove the test datastore. - */ - protected function tearDown(): void - { - @unlink(self::$testDatastore); - } - - /** - * Test basic getLink service: return link ID=41. - */ - public function testGetLinkId() - { - // Used by index_url(). - $_SERVER['SERVER_NAME'] = 'domain.tld'; - $_SERVER['SERVER_PORT'] = 80; - $_SERVER['SCRIPT_NAME'] = '/'; - - $id = 41; - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->getLink($request, new Response(), ['id' => $id]); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_LINK, count($data)); - $this->assertEquals($id, $data['id']); - - // Check link elements - $this->assertEquals('http://domain.tld/shaare/WDWyig', $data['url']); - $this->assertEquals('WDWyig', $data['shorturl']); - $this->assertEquals('Link title: @website', $data['title']); - $this->assertEquals( - 'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag', - $data['description'] - ); - $this->assertEquals('sTuff', $data['tags'][0]); - $this->assertEquals(false, $data['private']); - $this->assertEquals( - \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM), - $data['created'] - ); - $this->assertEmpty($data['updated']); - } - - /** - * Test basic getLink service: get non existent link => ApiLinkNotFoundException. - */ - public function testGetLink404() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiLinkNotFoundException::class); - $this->expectExceptionMessage('Link not found'); - - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - ]); - $request = Request::createFromEnvironment($env); - - $this->controller->getLink($request, new Response(), ['id' => -1]); - } -} diff --git a/tests/api/controllers/links/GetLinksTest.php b/tests/api/controllers/links/GetLinksTest.php deleted file mode 100644 index 217eb5d1..00000000 --- a/tests/api/controllers/links/GetLinksTest.php +++ /dev/null @@ -1,492 +0,0 @@ -conf = new ConfigManager('tests/utils/config/configJson'); - $this->conf->set('resource.datastore', self::$testDatastore); - $this->refDB = new ReferenceLinkDB(); - $this->refDB->write(self::$testDatastore); - $history = new History('sandbox/history.php'); - - $this->container = new Container(); - $this->container['conf'] = $this->conf; - $pluginManager = new PluginManager($this->conf); - $this->container['db'] = new BookmarkFileService( - $this->conf, - $pluginManager, - $history, - $mutex, - true - ); - $this->container['history'] = null; - - $this->controller = new Links($this->container); - } - - /** - * After every test, remove the test datastore. - */ - protected function tearDown(): void - { - @unlink(self::$testDatastore); - } - - /** - * Test basic getLinks service: returns all bookmarks. - */ - public function testGetLinks() - { - // Used by index_url(). - $_SERVER['SERVER_NAME'] = 'domain.tld'; - $_SERVER['SERVER_PORT'] = 80; - $_SERVER['SCRIPT_NAME'] = '/'; - - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals($this->refDB->countLinks(), count($data)); - - // Check order - $order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42]; - $cpt = 0; - foreach ($data as $link) { - $this->assertEquals(self::NB_FIELDS_LINK, count($link)); - $this->assertEquals($order[$cpt++], $link['id']); - } - - // Check first element fields - $first = $data[2]; - $this->assertEquals('http://domain.tld/shaare/WDWyig', $first['url']); - $this->assertEquals('WDWyig', $first['shorturl']); - $this->assertEquals('Link title: @website', $first['title']); - $this->assertEquals( - 'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag', - $first['description'] - ); - $this->assertEquals('sTuff', $first['tags'][0]); - $this->assertEquals(false, $first['private']); - $this->assertEquals( - \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM), - $first['created'] - ); - $this->assertEmpty($first['updated']); - - // Multi tags - $link = $data[3]; - $this->assertEquals(7, count($link['tags'])); - - // Update date - $this->assertEquals( - \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160803_093033')->format(\DateTime::ATOM), - $link['updated'] - ); - } - - /** - * Test getLinks service with offset and limit parameter: - * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC). - */ - public function testGetLinksOffsetLimit() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'offset=3&limit=1' - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(1, count($data)); - $this->assertEquals(8, $data[0]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); - } - - /** - * Test getLinks with limit=all (return all link). - */ - public function testGetLinksLimitAll() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'limit=all' - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals($this->refDB->countLinks(), count($data)); - // Check order - $order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42]; - $cpt = 0; - foreach ($data as $link) { - $this->assertEquals(self::NB_FIELDS_LINK, count($link)); - $this->assertEquals($order[$cpt++], $link['id']); - } - } - - /** - * Test getLinks service with offset and limit parameter: - * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC). - */ - public function testGetLinksOffsetTooHigh() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'offset=100' - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEmpty(count($data)); - } - - /** - * Test getLinks with visibility parameter set to all - */ - public function testGetLinksVisibilityAll() - { - $env = Environment::mock( - [ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'visibility=all' - ] - ); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string)$response->getBody(), true); - $this->assertEquals($this->refDB->countLinks(), count($data)); - $this->assertEquals(10, $data[0]['id']); - $this->assertEquals(41, $data[2]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); - } - - /** - * Test getLinks with visibility parameter set to private - */ - public function testGetLinksVisibilityPrivate() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'visibility=private' - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals($this->refDB->countPrivateLinks(), count($data)); - $this->assertEquals(6, $data[0]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); - } - - /** - * Test getLinks with visibility parameter set to public - */ - public function testGetLinksVisibilityPublic() - { - $env = Environment::mock( - [ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'visibility=public' - ] - ); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string)$response->getBody(), true); - $this->assertEquals($this->refDB->countPublicLinks(), count($data)); - $this->assertEquals(10, $data[0]['id']); - $this->assertEquals(41, $data[2]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); - } - - /** - * Test getLinks service with offset and limit parameter: - * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC). - */ - public function testGetLinksSearchTerm() - { - // Only in description - 1 result - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchterm=Tropical' - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(1, count($data)); - $this->assertEquals(1, $data[0]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); - - // Only in tags - 1 result - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchterm=tag3' - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(1, count($data)); - $this->assertEquals(0, $data[0]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); - - // Multiple results (2) - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchterm=stallman' - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(2, count($data)); - $this->assertEquals(41, $data[0]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); - $this->assertEquals(8, $data[1]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[1])); - - // Multiword - 2 results - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchterm=stallman+software' - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(2, count($data)); - $this->assertEquals(41, $data[0]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); - $this->assertEquals(8, $data[1]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[1])); - - // URL encoding - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchterm=' . urlencode('@web') - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(2, count($data)); - $this->assertEquals(41, $data[0]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); - $this->assertEquals(8, $data[1]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[1])); - } - - public function testGetLinksSearchTermNoResult() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchterm=nope' - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(0, count($data)); - } - - public function testGetLinksSearchTags() - { - // Single tag - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchtags=dev', - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(2, count($data)); - $this->assertEquals(0, $data[0]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); - $this->assertEquals(4, $data[1]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[1])); - - // Multitag + exclude - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchtags=stuff+-gnu', - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(1, count($data)); - $this->assertEquals(41, $data[0]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); - - // wildcard: placeholder at the start - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchtags=*Tuff', - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(2, count($data)); - $this->assertEquals(41, $data[0]['id']); - - // wildcard: placeholder at the end - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchtags=c*', - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(5, count($data)); - $this->assertEquals(6, $data[0]['id']); - - // wildcard: placeholder at the middle - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchtags=w*b', - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(4, count($data)); - $this->assertEquals(6, $data[0]['id']); - - // wildcard: match all - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchtags=*', - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data)); - $this->assertEquals(10, $data[0]['id']); - $this->assertEquals(41, $data[2]['id']); - - // wildcard: optional ('*' does not need to expand) - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchtags=*stuff*', - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(2, count($data)); - $this->assertEquals(41, $data[0]['id']); - - // wildcard: exclusions - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchtags=*a*+-*e*', - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(1, count($data)); - $this->assertEquals(41, $data[0]['id']); // finds '#hashtag' in descr. - - // wildcard: exclude all - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchtags=-*', - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(0, count($data)); - } - - /** - * Test getLinks service with search tags+terms. - */ - public function testGetLinksSearchTermsAndTags() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'searchterm=poke&searchtags=dev', - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getLinks($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(1, count($data)); - $this->assertEquals(0, $data[0]['id']); - $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); - } -} diff --git a/tests/api/controllers/links/PostLinkTest.php b/tests/api/controllers/links/PostLinkTest.php deleted file mode 100644 index faf43ee1..00000000 --- a/tests/api/controllers/links/PostLinkTest.php +++ /dev/null @@ -1,289 +0,0 @@ -conf = new ConfigManager('tests/utils/config/configJson'); - $this->conf->set('resource.datastore', self::$testDatastore); - $this->refDB = new ReferenceLinkDB(); - $this->refDB->write(self::$testDatastore); - $refHistory = new ReferenceHistory(); - $refHistory->write(self::$testHistory); - $this->history = new History(self::$testHistory); - $pluginManager = new PluginManager($this->conf); - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $pluginManager, - $this->history, - $mutex, - true - ); - $this->container = new Container(); - $this->container['conf'] = $this->conf; - $this->container['db'] = $this->bookmarkService; - $this->container['history'] = $this->history; - - $this->controller = new Links($this->container); - - $mock = $this->createMock(Router::class); - $mock->expects($this->any()) - ->method('pathFor') - ->willReturn('/api/v1/bookmarks/1'); - - // affect @property-read... seems to work - $this->controller->getCi()->router = $mock; - - // Used by index_url(). - $this->controller->getCi()['environment'] = [ - 'SERVER_NAME' => 'domain.tld', - 'SERVER_PORT' => 80, - 'SCRIPT_NAME' => '/', - ]; - } - - /** - * After every test, remove the test datastore. - */ - protected function tearDown(): void - { - @unlink(self::$testDatastore); - @unlink(self::$testHistory); - } - - /** - * Test link creation without any field: creates a blank note. - */ - public function testPostLinkMinimal() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'POST', - ]); - - $request = Request::createFromEnvironment($env); - - $response = $this->controller->postLink($request, new Response()); - $this->assertEquals(201, $response->getStatusCode()); - $this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_LINK, count($data)); - $this->assertEquals(43, $data['id']); - $this->assertRegExp('/[\w_-]{6}/', $data['shorturl']); - $this->assertEquals('http://domain.tld/shaare/' . $data['shorturl'], $data['url']); - $this->assertEquals('/shaare/' . $data['shorturl'], $data['title']); - $this->assertEquals('', $data['description']); - $this->assertEquals([], $data['tags']); - $this->assertEquals(true, $data['private']); - $this->assertTrue( - new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) - ); - $this->assertEquals('', $data['updated']); - - $historyEntry = $this->history->getHistory()[0]; - $this->assertEquals(History::CREATED, $historyEntry['event']); - $this->assertTrue( - (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] - ); - $this->assertEquals(43, $historyEntry['id']); - } - - /** - * Test link creation with all available fields. - */ - public function testPostLinkFull() - { - $link = [ - 'url' => 'website.tld/test?foo=bar', - 'title' => 'new entry', - 'description' => 'shaare description', - 'tags' => ['one', 'two'], - 'private' => true, - 'created' => '2015-05-05T12:30:00+03:00', - 'updated' => '2016-06-05T14:32:10+03:00', - ]; - $env = Environment::mock([ - 'REQUEST_METHOD' => 'POST', - 'CONTENT_TYPE' => 'application/json' - ]); - - $request = Request::createFromEnvironment($env); - $request = $request->withParsedBody($link); - $response = $this->controller->postLink($request, new Response()); - - $this->assertEquals(201, $response->getStatusCode()); - $this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_LINK, count($data)); - $this->assertEquals(43, $data['id']); - $this->assertRegExp('/[\w_-]{6}/', $data['shorturl']); - $this->assertEquals('http://' . $link['url'], $data['url']); - $this->assertEquals($link['title'], $data['title']); - $this->assertEquals($link['description'], $data['description']); - $this->assertEquals($link['tags'], $data['tags']); - $this->assertEquals(true, $data['private']); - $this->assertSame($link['created'], $data['created']); - $this->assertSame($link['updated'], $data['updated']); - } - - /** - * Test link creation with an existing link (duplicate URL). Should return a 409 HTTP error and the existing link. - */ - public function testPostLinkDuplicate() - { - $link = [ - 'url' => 'mediagoblin.org/', - 'title' => 'new entry', - 'description' => 'shaare description', - 'tags' => ['one', 'two'], - 'private' => true, - ]; - $env = Environment::mock([ - 'REQUEST_METHOD' => 'POST', - 'CONTENT_TYPE' => 'application/json' - ]); - - $request = Request::createFromEnvironment($env); - $request = $request->withParsedBody($link); - $response = $this->controller->postLink($request, new Response()); - - $this->assertEquals(409, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_LINK, count($data)); - $this->assertEquals(7, $data['id']); - $this->assertEquals('IuWvgA', $data['shorturl']); - $this->assertEquals('http://mediagoblin.org/', $data['url']); - $this->assertEquals('MediaGoblin', $data['title']); - $this->assertEquals('A free software media publishing platform #hashtagOther', $data['description']); - $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']); - $this->assertEquals(false, $data['private']); - $this->assertEquals( - \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130614_184135'), - \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) - ); - $this->assertEquals( - \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130615_184230'), - \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']) - ); - } - - /** - * Test link creation with a tag string provided - */ - public function testPostLinkWithTagString(): void - { - $link = [ - 'tags' => 'one two', - ]; - $env = Environment::mock([ - 'REQUEST_METHOD' => 'POST', - 'CONTENT_TYPE' => 'application/json' - ]); - - $request = Request::createFromEnvironment($env); - $request = $request->withParsedBody($link); - $response = $this->controller->postLink($request, new Response()); - - $this->assertEquals(201, $response->getStatusCode()); - $this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_LINK, count($data)); - $this->assertEquals(['one', 'two'], $data['tags']); - } - - /** - * Test link creation with a tag string provided - */ - public function testPostLinkWithTagString2(): void - { - $link = [ - 'tags' => ['one two'], - ]; - $env = Environment::mock([ - 'REQUEST_METHOD' => 'POST', - 'CONTENT_TYPE' => 'application/json' - ]); - - $request = Request::createFromEnvironment($env); - $request = $request->withParsedBody($link); - $response = $this->controller->postLink($request, new Response()); - - $this->assertEquals(201, $response->getStatusCode()); - $this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_LINK, count($data)); - $this->assertEquals(['one', 'two'], $data['tags']); - } -} diff --git a/tests/api/controllers/links/PutLinkTest.php b/tests/api/controllers/links/PutLinkTest.php deleted file mode 100644 index 9bd196db..00000000 --- a/tests/api/controllers/links/PutLinkTest.php +++ /dev/null @@ -1,292 +0,0 @@ -conf = new ConfigManager('tests/utils/config/configJson'); - $this->conf->set('resource.datastore', self::$testDatastore); - $this->refDB = new ReferenceLinkDB(); - $this->refDB->write(self::$testDatastore); - $refHistory = new ReferenceHistory(); - $refHistory->write(self::$testHistory); - $this->history = new History(self::$testHistory); - $pluginManager = new PluginManager($this->conf); - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $pluginManager, - $this->history, - $mutex, - true - ); - $this->container = new Container(); - $this->container['conf'] = $this->conf; - $this->container['db'] = $this->bookmarkService; - $this->container['history'] = $this->history; - - $this->controller = new Links($this->container); - - // Used by index_url(). - $this->controller->getCi()['environment'] = [ - 'SERVER_NAME' => 'domain.tld', - 'SERVER_PORT' => 80, - 'SCRIPT_NAME' => '/', - ]; - } - - /** - * After every test, remove the test datastore. - */ - protected function tearDown(): void - { - @unlink(self::$testDatastore); - @unlink(self::$testHistory); - } - - /** - * Test link update without value: reset the link to default values - */ - public function testPutLinkMinimal() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'PUT', - ]); - $id = '41'; - $request = Request::createFromEnvironment($env); - - $response = $this->controller->putLink($request, new Response(), ['id' => $id]); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_LINK, count($data)); - $this->assertEquals($id, $data['id']); - $this->assertEquals('WDWyig', $data['shorturl']); - $this->assertEquals('http://domain.tld/shaare/WDWyig', $data['url']); - $this->assertEquals('/shaare/WDWyig', $data['title']); - $this->assertEquals('', $data['description']); - $this->assertEquals([], $data['tags']); - $this->assertEquals(true, $data['private']); - $this->assertEquals( - \DateTime::createFromFormat('Ymd_His', '20150310_114651'), - \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) - ); - $this->assertTrue( - new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']) - ); - - $historyEntry = $this->history->getHistory()[0]; - $this->assertEquals(History::UPDATED, $historyEntry['event']); - $this->assertTrue( - (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] - ); - $this->assertEquals($id, $historyEntry['id']); - } - - /** - * Test link update with new values - */ - public function testPutLinkWithValues() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'PUT', - 'CONTENT_TYPE' => 'application/json' - ]); - $id = 41; - $update = [ - 'url' => 'http://somewhere.else', - 'title' => 'Le Cid', - 'description' => 'Percé jusques au fond du cœur [...]', - 'tags' => ['corneille', 'rodrigue'], - 'private' => true, - ]; - $request = Request::createFromEnvironment($env); - $request = $request->withParsedBody($update); - - $response = $this->controller->putLink($request, new Response(), ['id' => $id]); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_LINK, count($data)); - $this->assertEquals($id, $data['id']); - $this->assertEquals('WDWyig', $data['shorturl']); - $this->assertEquals('http://somewhere.else', $data['url']); - $this->assertEquals('Le Cid', $data['title']); - $this->assertEquals('Percé jusques au fond du cœur [...]', $data['description']); - $this->assertEquals(['corneille', 'rodrigue'], $data['tags']); - $this->assertEquals(true, $data['private']); - $this->assertEquals( - \DateTime::createFromFormat('Ymd_His', '20150310_114651'), - \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) - ); - $this->assertTrue( - new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']) - ); - } - - /** - * Test link update with an existing URL: 409 Conflict with the existing link as body - */ - public function testPutLinkDuplicate() - { - $link = [ - 'url' => 'mediagoblin.org/', - 'title' => 'new entry', - 'description' => 'shaare description', - 'tags' => ['one', 'two'], - 'private' => true, - ]; - $env = Environment::mock([ - 'REQUEST_METHOD' => 'PUT', - 'CONTENT_TYPE' => 'application/json' - ]); - - $request = Request::createFromEnvironment($env); - $request = $request->withParsedBody($link); - $response = $this->controller->putLink($request, new Response(), ['id' => 41]); - - $this->assertEquals(409, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_LINK, count($data)); - $this->assertEquals(7, $data['id']); - $this->assertEquals('IuWvgA', $data['shorturl']); - $this->assertEquals('http://mediagoblin.org/', $data['url']); - $this->assertEquals('MediaGoblin', $data['title']); - $this->assertEquals('A free software media publishing platform #hashtagOther', $data['description']); - $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']); - $this->assertEquals(false, $data['private']); - $this->assertEquals( - \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130614_184135'), - \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) - ); - $this->assertEquals( - \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130615_184230'), - \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']) - ); - } - - /** - * Test link update on non existent link => ApiLinkNotFoundException. - */ - public function testGetLink404() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiLinkNotFoundException::class); - $this->expectExceptionMessage('Link not found'); - - $env = Environment::mock([ - 'REQUEST_METHOD' => 'PUT', - ]); - $request = Request::createFromEnvironment($env); - - $this->controller->putLink($request, new Response(), ['id' => -1]); - } - - /** - * Test link creation with a tag string provided - */ - public function testPutLinkWithTagString(): void - { - $link = [ - 'tags' => 'one two', - ]; - $id = '41'; - $env = Environment::mock([ - 'REQUEST_METHOD' => 'PUT', - 'CONTENT_TYPE' => 'application/json' - ]); - - $request = Request::createFromEnvironment($env); - $request = $request->withParsedBody($link); - $response = $this->controller->putLink($request, new Response(), ['id' => $id]); - - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_LINK, count($data)); - $this->assertEquals(['one', 'two'], $data['tags']); - } - - /** - * Test link creation with a tag string provided - */ - public function testPutLinkWithTagString2(): void - { - $link = [ - 'tags' => ['one two'], - ]; - $id = '41'; - $env = Environment::mock([ - 'REQUEST_METHOD' => 'PUT', - 'CONTENT_TYPE' => 'application/json' - ]); - - $request = Request::createFromEnvironment($env); - $request = $request->withParsedBody($link); - $response = $this->controller->putLink($request, new Response(), ['id' => $id]); - - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_LINK, count($data)); - $this->assertEquals(['one', 'two'], $data['tags']); - } -} diff --git a/tests/api/controllers/tags/DeleteTagTest.php b/tests/api/controllers/tags/DeleteTagTest.php deleted file mode 100644 index 63a3e264..00000000 --- a/tests/api/controllers/tags/DeleteTagTest.php +++ /dev/null @@ -1,198 +0,0 @@ -mutex = new NoMutex(); - $this->conf = new ConfigManager('tests/utils/config/configJson'); - $this->conf->set('resource.datastore', self::$testDatastore); - $this->refDB = new ReferenceLinkDB(); - $this->refDB->write(self::$testDatastore); - $refHistory = new ReferenceHistory(); - $refHistory->write(self::$testHistory); - $this->history = new History(self::$testHistory); - $this->pluginManager = new PluginManager($this->conf); - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $this->container = new Container(); - $this->container['conf'] = $this->conf; - $this->container['db'] = $this->bookmarkService; - $this->container['history'] = $this->history; - - $this->controller = new Tags($this->container); - } - - /** - * After each test, remove the test datastore. - */ - protected function tearDown(): void - { - @unlink(self::$testDatastore); - @unlink(self::$testHistory); - } - - /** - * Test DELETE tag endpoint: the tag should be removed. - */ - public function testDeleteTagValid() - { - $tagName = 'gnu'; - $tags = $this->bookmarkService->bookmarksCountPerTag(); - $this->assertTrue($tags[$tagName] > 0); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'DELETE', - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->deleteTag($request, new Response(), ['tagName' => $tagName]); - $this->assertEquals(204, $response->getStatusCode()); - $this->assertEmpty((string) $response->getBody()); - - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - $tags = $this->bookmarkService->bookmarksCountPerTag(); - $this->assertFalse(isset($tags[$tagName])); - - // 2 bookmarks affected - $historyEntry = $this->history->getHistory()[0]; - $this->assertEquals(History::UPDATED, $historyEntry['event']); - $this->assertTrue( - (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] - ); - $historyEntry = $this->history->getHistory()[1]; - $this->assertEquals(History::UPDATED, $historyEntry['event']); - $this->assertTrue( - (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] - ); - } - - /** - * Test DELETE tag endpoint: the tag should be removed. - */ - public function testDeleteTagCaseSensitivity() - { - $tagName = 'sTuff'; - $tags = $this->bookmarkService->bookmarksCountPerTag(); - $this->assertTrue($tags[$tagName] > 0); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'DELETE', - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->deleteTag($request, new Response(), ['tagName' => $tagName]); - $this->assertEquals(204, $response->getStatusCode()); - $this->assertEmpty((string) $response->getBody()); - - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - $tags = $this->bookmarkService->bookmarksCountPerTag(); - $this->assertFalse(isset($tags[$tagName])); - $this->assertTrue($tags[strtolower($tagName)] > 0); - - $historyEntry = $this->history->getHistory()[0]; - $this->assertEquals(History::UPDATED, $historyEntry['event']); - $this->assertTrue( - (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] - ); - } - - /** - * Test DELETE tag endpoint: reach not existing tag. - */ - public function testDeleteLink404() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiTagNotFoundException::class); - $this->expectExceptionMessage('Tag not found'); - - $tagName = 'nopenope'; - $tags = $this->bookmarkService->bookmarksCountPerTag(); - $this->assertFalse(isset($tags[$tagName])); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'DELETE', - ]); - $request = Request::createFromEnvironment($env); - - $this->controller->deleteTag($request, new Response(), ['tagName' => $tagName]); - } -} diff --git a/tests/api/controllers/tags/GetTagNameTest.php b/tests/api/controllers/tags/GetTagNameTest.php deleted file mode 100644 index 8ba4b83c..00000000 --- a/tests/api/controllers/tags/GetTagNameTest.php +++ /dev/null @@ -1,147 +0,0 @@ -conf = new ConfigManager('tests/utils/config/configJson'); - $this->conf->set('resource.datastore', self::$testDatastore); - $this->refDB = new ReferenceLinkDB(); - $this->refDB->write(self::$testDatastore); - $history = new History('sandbox/history.php'); - - $this->container = new Container(); - $this->container['conf'] = $this->conf; - $this->pluginManager = new PluginManager($this->conf); - $this->container['db'] = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $history, - $mutex, - true - ); - $this->container['history'] = null; - - $this->controller = new Tags($this->container); - } - - /** - * After each test, remove the test datastore. - */ - protected function tearDown(): void - { - @unlink(self::$testDatastore); - } - - /** - * Test basic getTag service: return gnu tag with 2 occurrences. - */ - public function testGetTag() - { - $tagName = 'gnu'; - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->getTag($request, new Response(), ['tagName' => $tagName]); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_TAG, count($data)); - $this->assertEquals($tagName, $data['name']); - $this->assertEquals(2, $data['occurrences']); - } - - /** - * Test getTag service which is not case sensitive: occurrences with both sTuff and stuff - */ - public function testGetTagNotCaseSensitive() - { - $tagName = 'sTuff'; - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->getTag($request, new Response(), ['tagName' => $tagName]); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_TAG, count($data)); - $this->assertEquals($tagName, $data['name']); - $this->assertEquals(2, $data['occurrences']); - } - - /** - * Test basic getTag service: get non existent tag => ApiTagNotFoundException. - */ - public function testGetTag404() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiTagNotFoundException::class); - $this->expectExceptionMessage('Tag not found'); - - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - ]); - $request = Request::createFromEnvironment($env); - - $this->controller->getTag($request, new Response(), ['tagName' => 'nopenope']); - } -} diff --git a/tests/api/controllers/tags/GetTagsTest.php b/tests/api/controllers/tags/GetTagsTest.php deleted file mode 100644 index 1a183081..00000000 --- a/tests/api/controllers/tags/GetTagsTest.php +++ /dev/null @@ -1,227 +0,0 @@ -conf = new ConfigManager('tests/utils/config/configJson'); - $this->conf->set('resource.datastore', self::$testDatastore); - $this->refDB = new ReferenceLinkDB(); - $this->refDB->write(self::$testDatastore); - $history = new History('sandbox/history.php'); - $this->pluginManager = new PluginManager($this->conf); - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $history, - $mutex, - true - ); - $this->container = new Container(); - $this->container['conf'] = $this->conf; - $this->container['db'] = $this->bookmarkService; - $this->container['history'] = null; - - $this->controller = new Tags($this->container); - } - - /** - * After every test, remove the test datastore. - */ - protected function tearDown(): void - { - @unlink(self::$testDatastore); - } - - /** - * Test basic getTags service: returns all tags. - */ - public function testGetTagsAll() - { - $tags = $this->bookmarkService->bookmarksCountPerTag(); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - ]); - $request = Request::createFromEnvironment($env); - - $response = $this->controller->getTags($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(count($tags), count($data)); - - // Check order - $this->assertEquals(self::NB_FIELDS_TAG, count($data[0])); - $this->assertEquals('web', $data[0]['name']); - $this->assertEquals(4, $data[0]['occurrences']); - $this->assertEquals(self::NB_FIELDS_TAG, count($data[1])); - $this->assertEquals('cartoon', $data[1]['name']); - $this->assertEquals(3, $data[1]['occurrences']); - // Case insensitive - $this->assertEquals(self::NB_FIELDS_TAG, count($data[5])); - $this->assertEquals('sTuff', $data[5]['name']); - $this->assertEquals(2, $data[5]['occurrences']); - // End - $this->assertEquals(self::NB_FIELDS_TAG, count($data[count($data) - 1])); - $this->assertEquals('w3c', $data[count($data) - 1]['name']); - $this->assertEquals(1, $data[count($data) - 1]['occurrences']); - } - - /** - * Test getTags service with offset and limit parameter: - * limit=1 and offset=1 should return only the second tag, cartoon with 3 occurrences - */ - public function testGetTagsOffsetLimit() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'offset=1&limit=1' - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getTags($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(1, count($data)); - $this->assertEquals(self::NB_FIELDS_TAG, count($data[0])); - $this->assertEquals('cartoon', $data[0]['name']); - $this->assertEquals(3, $data[0]['occurrences']); - } - - /** - * Test getTags with limit=all (return all tags). - */ - public function testGetTagsLimitAll() - { - $tags = $this->bookmarkService->bookmarksCountPerTag(); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'limit=all' - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getTags($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(count($tags), count($data)); - } - - /** - * Test getTags service with offset and limit parameter: - * limit=1 and offset=1 should not return any tag - */ - public function testGetTagsOffsetTooHigh() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'offset=100' - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getTags($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEmpty(count($data)); - } - - /** - * Test getTags with visibility parameter set to private - */ - public function testGetTagsVisibilityPrivate() - { - $tags = $this->bookmarkService->bookmarksCountPerTag([], 'private'); - $env = Environment::mock([ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'visibility=private' - ]); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getTags($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(count($tags), count($data)); - $this->assertEquals(self::NB_FIELDS_TAG, count($data[0])); - $this->assertEquals('Mercurial', $data[0]['name']); - $this->assertEquals(1, $data[0]['occurrences']); - } - - /** - * Test getTags with visibility parameter set to public - */ - public function testGetTagsVisibilityPublic() - { - $tags = $this->bookmarkService->bookmarksCountPerTag([], 'public'); - $env = Environment::mock( - [ - 'REQUEST_METHOD' => 'GET', - 'QUERY_STRING' => 'visibility=public' - ] - ); - $request = Request::createFromEnvironment($env); - $response = $this->controller->getTags($request, new Response()); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string)$response->getBody(), true); - $this->assertEquals(count($tags), count($data)); - $this->assertEquals(self::NB_FIELDS_TAG, count($data[0])); - $this->assertEquals('web', $data[0]['name']); - $this->assertEquals(3, $data[0]['occurrences']); - } -} diff --git a/tests/api/controllers/tags/PutTagTest.php b/tests/api/controllers/tags/PutTagTest.php deleted file mode 100644 index b2ebcd52..00000000 --- a/tests/api/controllers/tags/PutTagTest.php +++ /dev/null @@ -1,225 +0,0 @@ -conf = new ConfigManager('tests/utils/config/configJson'); - $this->conf->set('resource.datastore', self::$testDatastore); - $this->refDB = new ReferenceLinkDB(); - $this->refDB->write(self::$testDatastore); - $refHistory = new ReferenceHistory(); - $refHistory->write(self::$testHistory); - $this->history = new History(self::$testHistory); - $this->pluginManager = new PluginManager($this->conf); - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $mutex, - true - ); - - $this->container = new Container(); - $this->container['conf'] = $this->conf; - $this->container['db'] = $this->bookmarkService; - $this->container['history'] = $this->history; - - $this->controller = new Tags($this->container); - } - - /** - * After every test, remove the test datastore. - */ - protected function tearDown(): void - { - @unlink(self::$testDatastore); - @unlink(self::$testHistory); - } - - /** - * Test tags update - */ - public function testPutLinkValid() - { - $env = Environment::mock([ - 'REQUEST_METHOD' => 'PUT', - ]); - $tagName = 'gnu'; - $update = ['name' => $newName = 'newtag']; - $request = Request::createFromEnvironment($env); - $request = $request->withParsedBody($update); - - $response = $this->controller->putTag($request, new Response(), ['tagName' => $tagName]); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_TAG, count($data)); - $this->assertEquals($newName, $data['name']); - $this->assertEquals(2, $data['occurrences']); - - $tags = $this->bookmarkService->bookmarksCountPerTag(); - $this->assertNotTrue(isset($tags[$tagName])); - $this->assertEquals(2, $tags[$newName]); - - $historyEntry = $this->history->getHistory()[0]; - $this->assertEquals(History::UPDATED, $historyEntry['event']); - $this->assertTrue( - (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] - ); - $historyEntry = $this->history->getHistory()[1]; - $this->assertEquals(History::UPDATED, $historyEntry['event']); - $this->assertTrue( - (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] - ); - } - - /** - * Test tag update with an existing tag: they should be merged - */ - public function testPutTagMerge() - { - $tagName = 'gnu'; - $newName = 'w3c'; - - $tags = $this->bookmarkService->bookmarksCountPerTag(); - $this->assertEquals(1, $tags[$newName]); - $this->assertEquals(2, $tags[$tagName]); - - $env = Environment::mock([ - 'REQUEST_METHOD' => 'PUT', - ]); - $update = ['name' => $newName]; - $request = Request::createFromEnvironment($env); - $request = $request->withParsedBody($update); - - $response = $this->controller->putTag($request, new Response(), ['tagName' => $tagName]); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode((string) $response->getBody(), true); - $this->assertEquals(self::NB_FIELDS_TAG, count($data)); - $this->assertEquals($newName, $data['name']); - $this->assertEquals(3, $data['occurrences']); - - $tags = $this->bookmarkService->bookmarksCountPerTag(); - $this->assertNotTrue(isset($tags[$tagName])); - $this->assertEquals(3, $tags[$newName]); - } - - /** - * Test tag update with an empty new tag name => ApiBadParametersException - */ - public function testPutTagEmpty() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiBadParametersException::class); - $this->expectExceptionMessage('New tag name is required in the request body'); - - $tagName = 'gnu'; - $newName = ''; - - $tags = $this->bookmarkService->bookmarksCountPerTag(); - $this->assertEquals(2, $tags[$tagName]); - - $env = Environment::mock([ - 'REQUEST_METHOD' => 'PUT', - ]); - $request = Request::createFromEnvironment($env); - - $env = Environment::mock([ - 'REQUEST_METHOD' => 'PUT', - ]); - $update = ['name' => $newName]; - $request = Request::createFromEnvironment($env); - $request = $request->withParsedBody($update); - - try { - $this->controller->putTag($request, new Response(), ['tagName' => $tagName]); - } catch (ApiBadParametersException $e) { - $tags = $this->bookmarkService->bookmarksCountPerTag(); - $this->assertEquals(2, $tags[$tagName]); - throw $e; - } - } - - /** - * Test tag update on non existent tag => ApiTagNotFoundException. - */ - public function testPutTag404() - { - $this->expectException(\Shaarli\Api\Exceptions\ApiTagNotFoundException::class); - $this->expectExceptionMessage('Tag not found'); - - $env = Environment::mock([ - 'REQUEST_METHOD' => 'PUT', - ]); - $request = Request::createFromEnvironment($env); - - $this->controller->putTag($request, new Response(), ['tagName' => 'nopenope']); - } -} diff --git a/tests/bookmark/BookmarkArrayTest.php b/tests/bookmark/BookmarkArrayTest.php deleted file mode 100644 index c2f90d66..00000000 --- a/tests/bookmark/BookmarkArrayTest.php +++ /dev/null @@ -1,224 +0,0 @@ -assertTrue(is_iterable($array)); - $this->assertEmpty($array); - } - - /** - * Test adding entries to the array, specifying the key offset or not. - */ - public function testArrayAccessAddEntries() - { - $array = new BookmarkArray(); - $bookmark = new Bookmark(); - $bookmark->setId(11)->validate(); - $array[] = $bookmark; - $this->assertCount(1, $array); - $this->assertTrue(isset($array[11])); - $this->assertNull($array[0]); - $this->assertEquals($bookmark, $array[11]); - - $bookmark = new Bookmark(); - $bookmark->setId(14)->validate(); - $array[14] = $bookmark; - $this->assertCount(2, $array); - $this->assertTrue(isset($array[14])); - $this->assertNull($array[0]); - $this->assertEquals($bookmark, $array[14]); - } - - /** - * Test adding a bad entry: wrong type - */ - public function testArrayAccessAddBadEntryInstance() - { - $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class); - - $array = new BookmarkArray(); - $array[] = 'nope'; - } - - /** - * Test adding a bad entry: no id - */ - public function testArrayAccessAddBadEntryNoId() - { - $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class); - - $array = new BookmarkArray(); - $bookmark = new Bookmark(); - $array[] = $bookmark; - } - - /** - * Test adding a bad entry: no url - */ - public function testArrayAccessAddBadEntryNoUrl() - { - $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class); - - $array = new BookmarkArray(); - $bookmark = (new Bookmark())->setId(11); - $array[] = $bookmark; - } - - /** - * Test adding a bad entry: invalid offset - */ - public function testArrayAccessAddBadEntryOffset() - { - $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class); - - $array = new BookmarkArray(); - $bookmark = (new Bookmark())->setId(11); - $bookmark->validate(); - $array['nope'] = $bookmark; - } - - /** - * Test adding a bad entry: ID/offset not consistent - */ - public function testArrayAccessAddBadEntryIdOffset() - { - $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class); - - $array = new BookmarkArray(); - $bookmark = (new Bookmark())->setId(11); - $bookmark->validate(); - $array[14] = $bookmark; - } - - /** - * Test update entries through array access. - */ - public function testArrayAccessUpdateEntries() - { - $array = new BookmarkArray(); - $bookmark = new Bookmark(); - $bookmark->setId(11)->validate(); - $bookmark->setTitle('old'); - $array[] = $bookmark; - $bookmark = new Bookmark(); - $bookmark->setId(11)->validate(); - $bookmark->setTitle('test'); - $array[] = $bookmark; - $this->assertCount(1, $array); - $this->assertEquals('test', $array[11]->getTitle()); - - $bookmark = new Bookmark(); - $bookmark->setId(11)->validate(); - $bookmark->setTitle('test2'); - $array[11] = $bookmark; - $this->assertCount(1, $array); - $this->assertEquals('test2', $array[11]->getTitle()); - } - - /** - * Test delete entries through array access. - */ - public function testArrayAccessDeleteEntries() - { - $array = new BookmarkArray(); - $bookmark11 = new Bookmark(); - $bookmark11->setId(11)->validate(); - $array[] = $bookmark11; - $bookmark14 = new Bookmark(); - $bookmark14->setId(14)->validate(); - $array[] = $bookmark14; - $bookmark23 = new Bookmark(); - $bookmark23->setId(23)->validate(); - $array[] = $bookmark23; - $bookmark0 = new Bookmark(); - $bookmark0->setId(0)->validate(); - $array[] = $bookmark0; - $this->assertCount(4, $array); - - unset($array[14]); - $this->assertCount(3, $array); - $this->assertEquals($bookmark11, $array[11]); - $this->assertEquals($bookmark23, $array[23]); - $this->assertEquals($bookmark0, $array[0]); - - unset($array[23]); - $this->assertCount(2, $array); - $this->assertEquals($bookmark11, $array[11]); - $this->assertEquals($bookmark0, $array[0]); - - unset($array[11]); - $this->assertCount(1, $array); - $this->assertEquals($bookmark0, $array[0]); - - unset($array[0]); - $this->assertCount(0, $array); - } - - /** - * Test iterating through array access. - */ - public function testArrayAccessIterate() - { - $array = new BookmarkArray(); - $bookmark11 = new Bookmark(); - $bookmark11->setId(11)->validate(); - $array[] = $bookmark11; - $bookmark14 = new Bookmark(); - $bookmark14->setId(14)->validate(); - $array[] = $bookmark14; - $bookmark23 = new Bookmark(); - $bookmark23->setId(23)->validate(); - $array[] = $bookmark23; - $this->assertCount(3, $array); - - foreach ($array as $id => $bookmark) { - $this->assertEquals(${'bookmark' . $id}, $bookmark); - } - } - - /** - * Test reordering the array. - */ - public function testReorder() - { - $refDB = new ReferenceLinkDB(); - $refDB->write('sandbox/datastore.php'); - - - $bookmarks = $refDB->getLinks(); - $bookmarks->reorder('ASC'); - $this->assertInstanceOf(BookmarkArray::class, $bookmarks); - - $stickyIds = [11, 10]; - $standardIds = [42, 4, 9, 1, 0, 7, 6, 8, 41]; - $linkIds = array_merge($stickyIds, $standardIds); - $cpt = 0; - foreach ($bookmarks as $key => $value) { - $this->assertEquals($linkIds[$cpt++], $key); - } - - $bookmarks = $refDB->getLinks(); - $bookmarks->reorder('DESC'); - $this->assertInstanceOf(BookmarkArray::class, $bookmarks); - - $linkIds = array_merge(array_reverse($stickyIds), array_reverse($standardIds)); - $cpt = 0; - foreach ($bookmarks as $key => $value) { - $this->assertEquals($linkIds[$cpt++], $key); - } - } -} diff --git a/tests/bookmark/BookmarkFileServiceTest.php b/tests/bookmark/BookmarkFileServiceTest.php deleted file mode 100644 index 0a265e3c..00000000 --- a/tests/bookmark/BookmarkFileServiceTest.php +++ /dev/null @@ -1,1273 +0,0 @@ -mutex = new NoMutex(); - - if (file_exists(self::$testDatastore)) { - unlink(self::$testDatastore); - } - - if (file_exists(self::$testConf . '.json.php')) { - unlink(self::$testConf . '.json.php'); - } - - if (file_exists(self::$testUpdates)) { - unlink(self::$testUpdates); - } - - copy('tests/utils/config/configJson.json.php', self::$testConf . '.json.php'); - $this->conf = new ConfigManager(self::$testConf); - $this->conf->set('resource.datastore', self::$testDatastore); - $this->conf->set('resource.updates', self::$testUpdates); - $this->refDB = new ReferenceLinkDB(); - $this->refDB->write(self::$testDatastore); - $this->history = new History('sandbox/history.php'); - $this->pluginManager = new PluginManager($this->conf); - $this->publicLinkDB = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - false - ); - $this->privateLinkDB = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - } - - /** - * Test migrate() method with a legacy datastore. - */ - public function testDatabaseMigration() - { - if (!defined('SHAARLI_VERSION')) { - define('SHAARLI_VERSION', 'dev'); - } - - $this->refDB = new ReferenceLinkDB(true); - $this->refDB->write(self::$testDatastore); - $db = self::getMethod('migrate'); - $db->invokeArgs($this->privateLinkDB, []); - - $db = new FakeBookmarkService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - $this->assertInstanceOf(BookmarkArray::class, $db->getBookmarks()); - $this->assertEquals($this->refDB->countLinks(), $db->count()); - } - - /** - * Test get() method for a defined and saved bookmark - */ - public function testGetDefinedSaved() - { - $bookmark = $this->privateLinkDB->get(42); - $this->assertEquals(42, $bookmark->getId()); - $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle()); - } - - /** - * Test get() method for a defined and not saved bookmark - */ - public function testGetDefinedNotSaved() - { - $bookmark = new Bookmark(); - $this->privateLinkDB->add($bookmark); - $createdBookmark = $this->privateLinkDB->get(43); - $this->assertEquals(43, $createdBookmark->getId()); - $this->assertEmpty($createdBookmark->getDescription()); - } - - /** - * Test get() method for an undefined bookmark - */ - public function testGetUndefined() - { - $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class); - - $this->privateLinkDB->get(666); - } - - /** - * Test add() method for a bookmark fully built - */ - public function testAddFull() - { - $bookmark = new Bookmark(); - $bookmark->setUrl($url = 'https://domain.tld/index.php'); - $bookmark->setShortUrl('abc'); - $bookmark->setTitle($title = 'This a brand new bookmark'); - $bookmark->setDescription($desc = 'It should be created and written'); - $bookmark->setTags($tags = ['tag1', 'tagssss']); - $bookmark->setThumbnail($thumb = 'http://thumb.tld/dle.png'); - $bookmark->setPrivate(true); - $bookmark->setSticky(true); - $bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190518_140354')); - $bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190518_150354')); - - $this->privateLinkDB->add($bookmark); - $bookmark = $this->privateLinkDB->get(43); - $this->assertEquals(43, $bookmark->getId()); - $this->assertEquals($url, $bookmark->getUrl()); - $this->assertEquals('abc', $bookmark->getShortUrl()); - $this->assertEquals($title, $bookmark->getTitle()); - $this->assertEquals($desc, $bookmark->getDescription()); - $this->assertEquals($tags, $bookmark->getTags()); - $this->assertEquals($thumb, $bookmark->getThumbnail()); - $this->assertTrue($bookmark->isPrivate()); - $this->assertTrue($bookmark->isSticky()); - $this->assertEquals($created, $bookmark->getCreated()); - $this->assertEquals($updated, $bookmark->getUpdated()); - - // reload from file - $this->privateLinkDB = new FakeBookmarkService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $bookmark = $this->privateLinkDB->get(43); - $this->assertEquals(43, $bookmark->getId()); - $this->assertEquals($url, $bookmark->getUrl()); - $this->assertEquals('abc', $bookmark->getShortUrl()); - $this->assertEquals($title, $bookmark->getTitle()); - $this->assertEquals($desc, $bookmark->getDescription()); - $this->assertEquals($tags, $bookmark->getTags()); - $this->assertEquals($thumb, $bookmark->getThumbnail()); - $this->assertTrue($bookmark->isPrivate()); - $this->assertTrue($bookmark->isSticky()); - $this->assertEquals($created, $bookmark->getCreated()); - $this->assertEquals($updated, $bookmark->getUpdated()); - } - - /** - * Test add() method for a bookmark without any field set - */ - public function testAddMinimal() - { - $bookmark = new Bookmark(); - $this->privateLinkDB->add($bookmark); - - $bookmark = $this->privateLinkDB->get(43); - $this->assertEquals(43, $bookmark->getId()); - $this->assertRegExp('#/shaare/[\w\-]{6}#', $bookmark->getUrl()); - $this->assertRegExp('/[\w\-]{6}/', $bookmark->getShortUrl()); - $this->assertEquals($bookmark->getUrl(), $bookmark->getTitle()); - $this->assertEmpty($bookmark->getDescription()); - $this->assertEmpty($bookmark->getTags()); - $this->assertEmpty($bookmark->getThumbnail()); - $this->assertFalse($bookmark->isPrivate()); - $this->assertFalse($bookmark->isSticky()); - $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getCreated()); - $this->assertNull($bookmark->getUpdated()); - - // reload from file - $this->privateLinkDB = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $bookmark = $this->privateLinkDB->get(43); - $this->assertEquals(43, $bookmark->getId()); - $this->assertRegExp('#/shaare/[\w\-]{6}#', $bookmark->getUrl()); - $this->assertRegExp('/[\w\-]{6}/', $bookmark->getShortUrl()); - $this->assertEquals($bookmark->getUrl(), $bookmark->getTitle()); - $this->assertEmpty($bookmark->getDescription()); - $this->assertEmpty($bookmark->getTags()); - $this->assertEmpty($bookmark->getThumbnail()); - $this->assertFalse($bookmark->isPrivate()); - $this->assertFalse($bookmark->isSticky()); - $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getCreated()); - $this->assertNull($bookmark->getUpdated()); - } - - /** - * Test add() method for a bookmark without any field set and without writing the data store - */ - public function testAddMinimalNoWrite() - { - $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class); - - $bookmark = new Bookmark(); - $this->privateLinkDB->add($bookmark, false); - - $bookmark = $this->privateLinkDB->get(43); - $this->assertEquals(43, $bookmark->getId()); - - // reload from file - $this->privateLinkDB = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $this->privateLinkDB->get(43); - } - - /** - * Test add() method while logged out - */ - public function testAddLoggedOut() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('You\'re not authorized to alter the datastore'); - - $this->publicLinkDB->add(new Bookmark()); - } - - /** - * Test add() method with a Bookmark already containing an ID - */ - public function testAddWithId() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('This bookmarks already exists'); - - $bookmark = new Bookmark(); - $bookmark->setId(43); - $this->privateLinkDB->add($bookmark); - } - - /** - * Test set() method for a bookmark fully built - */ - public function testSetFull() - { - $bookmark = $this->privateLinkDB->get(42); - $bookmark->setUrl($url = 'https://domain.tld/index.php'); - $bookmark->setShortUrl('abc'); - $bookmark->setTitle($title = 'This a brand new bookmark'); - $bookmark->setDescription($desc = 'It should be created and written'); - $bookmark->setTags($tags = ['tag1', 'tagssss']); - $bookmark->setThumbnail($thumb = 'http://thumb.tld/dle.png'); - $bookmark->setPrivate(true); - $bookmark->setSticky(true); - $bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190518_140354')); - $bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190518_150354')); - - $this->privateLinkDB->set($bookmark); - $bookmark = $this->privateLinkDB->get(42); - $this->assertEquals(42, $bookmark->getId()); - $this->assertEquals($url, $bookmark->getUrl()); - $this->assertEquals('abc', $bookmark->getShortUrl()); - $this->assertEquals($title, $bookmark->getTitle()); - $this->assertEquals($desc, $bookmark->getDescription()); - $this->assertEquals($tags, $bookmark->getTags()); - $this->assertEquals($thumb, $bookmark->getThumbnail()); - $this->assertTrue($bookmark->isPrivate()); - $this->assertTrue($bookmark->isSticky()); - $this->assertEquals($created, $bookmark->getCreated()); - $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated()); - - // reload from file - $this->privateLinkDB = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $bookmark = $this->privateLinkDB->get(42); - $this->assertEquals(42, $bookmark->getId()); - $this->assertEquals($url, $bookmark->getUrl()); - $this->assertEquals('abc', $bookmark->getShortUrl()); - $this->assertEquals($title, $bookmark->getTitle()); - $this->assertEquals($desc, $bookmark->getDescription()); - $this->assertEquals($tags, $bookmark->getTags()); - $this->assertEquals($thumb, $bookmark->getThumbnail()); - $this->assertTrue($bookmark->isPrivate()); - $this->assertTrue($bookmark->isSticky()); - $this->assertEquals($created, $bookmark->getCreated()); - $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated()); - } - - /** - * Test set() method for a bookmark without any field set - */ - public function testSetMinimal() - { - $bookmark = $this->privateLinkDB->get(42); - $this->privateLinkDB->set($bookmark); - - $bookmark = $this->privateLinkDB->get(42); - $this->assertEquals(42, $bookmark->getId()); - $this->assertEquals('/shaare/WDWyig', $bookmark->getUrl()); - $this->assertEquals('1eYJ1Q', $bookmark->getShortUrl()); - $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle()); - $this->assertEquals('Used to test bookmarks reordering.', $bookmark->getDescription()); - $this->assertEquals(['ut'], $bookmark->getTags()); - $this->assertFalse($bookmark->getThumbnail()); - $this->assertFalse($bookmark->isPrivate()); - $this->assertFalse($bookmark->isSticky()); - $this->assertEquals( - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20100310_101010'), - $bookmark->getCreated() - ); - $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated()); - - // reload from file - $this->privateLinkDB = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $bookmark = $this->privateLinkDB->get(42); - $this->assertEquals(42, $bookmark->getId()); - $this->assertEquals('/shaare/WDWyig', $bookmark->getUrl()); - $this->assertEquals('1eYJ1Q', $bookmark->getShortUrl()); - $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle()); - $this->assertEquals('Used to test bookmarks reordering.', $bookmark->getDescription()); - $this->assertEquals(['ut'], $bookmark->getTags()); - $this->assertFalse($bookmark->getThumbnail()); - $this->assertFalse($bookmark->isPrivate()); - $this->assertFalse($bookmark->isSticky()); - $this->assertEquals( - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20100310_101010'), - $bookmark->getCreated() - ); - $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated()); - } - - /** - * Test set() method for a bookmark without any field set and without writing the data store - */ - public function testSetMinimalNoWrite() - { - $bookmark = $this->privateLinkDB->get(42); - $bookmark->setTitle($title = 'hi!'); - $this->privateLinkDB->set($bookmark, false); - - $bookmark = $this->privateLinkDB->get(42); - $this->assertEquals(42, $bookmark->getId()); - $this->assertEquals($title, $bookmark->getTitle()); - - // reload from file - $this->privateLinkDB = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $bookmark = $this->privateLinkDB->get(42); - $this->assertEquals(42, $bookmark->getId()); - $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle()); - } - - /** - * Test set() method while logged out - */ - public function testSetLoggedOut() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('You\'re not authorized to alter the datastore'); - - $this->publicLinkDB->set(new Bookmark()); - } - - /** - * Test set() method with a Bookmark without an ID defined. - */ - public function testSetWithoutId() - { - $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class); - - $bookmark = new Bookmark(); - $this->privateLinkDB->set($bookmark); - } - - /** - * Test set() method with a Bookmark with an unknow ID - */ - public function testSetWithUnknownId() - { - $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class); - - $bookmark = new Bookmark(); - $bookmark->setId(666); - $this->privateLinkDB->set($bookmark); - } - - /** - * Test addOrSet() method with a new ID - */ - public function testAddOrSetNew() - { - $bookmark = new Bookmark(); - $this->privateLinkDB->addOrSet($bookmark); - - $bookmark = $this->privateLinkDB->get(43); - $this->assertEquals(43, $bookmark->getId()); - - // reload from file - $this->privateLinkDB = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $bookmark = $this->privateLinkDB->get(43); - $this->assertEquals(43, $bookmark->getId()); - } - - /** - * Test addOrSet() method with an existing ID - */ - public function testAddOrSetExisting() - { - $bookmark = $this->privateLinkDB->get(42); - $bookmark->setTitle($title = 'hi!'); - $this->privateLinkDB->addOrSet($bookmark); - - $bookmark = $this->privateLinkDB->get(42); - $this->assertEquals(42, $bookmark->getId()); - $this->assertEquals($title, $bookmark->getTitle()); - - // reload from file - $this->privateLinkDB = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $bookmark = $this->privateLinkDB->get(42); - $this->assertEquals(42, $bookmark->getId()); - $this->assertEquals($title, $bookmark->getTitle()); - } - - /** - * Test addOrSet() method while logged out - */ - public function testAddOrSetLoggedOut() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('You\'re not authorized to alter the datastore'); - - $this->publicLinkDB->addOrSet(new Bookmark()); - } - - /** - * Test addOrSet() method for a bookmark without any field set and without writing the data store - */ - public function testAddOrSetMinimalNoWrite() - { - $bookmark = $this->privateLinkDB->get(42); - $bookmark->setTitle($title = 'hi!'); - $this->privateLinkDB->addOrSet($bookmark, false); - - $bookmark = $this->privateLinkDB->get(42); - $this->assertEquals(42, $bookmark->getId()); - $this->assertEquals($title, $bookmark->getTitle()); - - // reload from file - $this->privateLinkDB = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $bookmark = $this->privateLinkDB->get(42); - $this->assertEquals(42, $bookmark->getId()); - $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle()); - } - - /** - * Test remove() method with an existing Bookmark - */ - public function testRemoveExisting() - { - $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class); - - $bookmark = $this->privateLinkDB->get(42); - $this->privateLinkDB->remove($bookmark); - - $exception = null; - try { - $this->privateLinkDB->get(42); - } catch (BookmarkNotFoundException $e) { - $exception = $e; - } - $this->assertInstanceOf(BookmarkNotFoundException::class, $exception); - - // reload from file - $this->privateLinkDB = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $this->privateLinkDB->get(42); - } - - /** - * Test remove() method while logged out - */ - public function testRemoveLoggedOut() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('You\'re not authorized to alter the datastore'); - - $bookmark = $this->privateLinkDB->get(42); - $this->publicLinkDB->remove($bookmark); - } - - /** - * Test remove() method with a Bookmark with an unknown ID - */ - public function testRemoveWithUnknownId() - { - $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class); - - $bookmark = new Bookmark(); - $bookmark->setId(666); - $this->privateLinkDB->remove($bookmark); - } - - /** - * Test exists() method - */ - public function testExists() - { - $this->assertTrue($this->privateLinkDB->exists(42)); // public - $this->assertTrue($this->privateLinkDB->exists(6)); // private - - $this->assertTrue($this->privateLinkDB->exists(42, BookmarkFilter::$ALL)); - $this->assertTrue($this->privateLinkDB->exists(6, BookmarkFilter::$ALL)); - - $this->assertTrue($this->privateLinkDB->exists(42, BookmarkFilter::$PUBLIC)); - $this->assertFalse($this->privateLinkDB->exists(6, BookmarkFilter::$PUBLIC)); - - $this->assertFalse($this->privateLinkDB->exists(42, BookmarkFilter::$PRIVATE)); - $this->assertTrue($this->privateLinkDB->exists(6, BookmarkFilter::$PRIVATE)); - - $this->assertTrue($this->publicLinkDB->exists(42)); - $this->assertFalse($this->publicLinkDB->exists(6)); - - $this->assertTrue($this->publicLinkDB->exists(42, BookmarkFilter::$PUBLIC)); - $this->assertFalse($this->publicLinkDB->exists(6, BookmarkFilter::$PUBLIC)); - - $this->assertFalse($this->publicLinkDB->exists(42, BookmarkFilter::$PRIVATE)); - $this->assertTrue($this->publicLinkDB->exists(6, BookmarkFilter::$PRIVATE)); - } - - /** - * Test initialize() method - */ - public function testInitialize() - { - $dbSize = $this->privateLinkDB->count(); - $this->privateLinkDB->initialize(); - $this->assertEquals($dbSize + 3, $this->privateLinkDB->count()); - $this->assertStringStartsWith( - 'Shaarli will automatically pick up the thumbnail for links to a variety of websites.', - $this->privateLinkDB->get(43)->getDescription() - ); - $this->assertStringStartsWith( - 'Adding a shaare without entering a URL creates a text-only "note" post such as this one.', - $this->privateLinkDB->get(44)->getDescription() - ); - $this->assertStringStartsWith( - 'Welcome to Shaarli!', - $this->privateLinkDB->get(45)->getDescription() - ); - } - - /* - * The following tests have been taken from the legacy LinkDB test and adapted - * to make sure that nothing have been broken in the migration process. - * They mostly cover search/filters. Some of them might be redundant with the previous ones. - */ - /** - * Attempt to instantiate a LinkDB whereas the datastore is not writable - */ - public function testConstructDatastoreNotWriteable() - { - $this->expectException(\Shaarli\Bookmark\Exception\NotWritableDataStoreException::class); - $this->expectExceptionMessageRegExp('#Couldn\'t load data from the data store file "null".*#'); - - $conf = new ConfigManager('tests/utils/config/configJson'); - $conf->set('resource.datastore', 'null/store.db'); - new BookmarkFileService($conf, $this->pluginManager, $this->history, $this->mutex, true); - } - - /** - * The DB doesn't exist, ensure it is created with an empty datastore - */ - public function testCheckDBNewLoggedIn() - { - unlink(self::$testDatastore); - $this->assertFileNotExists(self::$testDatastore); - new BookmarkFileService($this->conf, $this->pluginManager, $this->history, $this->mutex, true); - $this->assertFileExists(self::$testDatastore); - - // ensure the correct data has been written - $this->assertGreaterThan(0, filesize(self::$testDatastore)); - } - - /** - * The DB doesn't exist, but not logged in, ensure it initialized, but the file is not written - */ - public function testCheckDBNewLoggedOut() - { - unlink(self::$testDatastore); - $this->assertFileNotExists(self::$testDatastore); - $db = new FakeBookmarkService($this->conf, $this->pluginManager, $this->history, $this->mutex, false); - $this->assertFileNotExists(self::$testDatastore); - $this->assertInstanceOf(BookmarkArray::class, $db->getBookmarks()); - $this->assertCount(0, $db->getBookmarks()); - } - - /** - * Load public bookmarks from the DB - */ - public function testReadPublicDB() - { - $this->assertEquals( - $this->refDB->countPublicLinks(), - $this->publicLinkDB->count() - ); - } - - /** - * Load public and private bookmarks from the DB - */ - public function testReadPrivateDB() - { - $this->assertEquals( - $this->refDB->countLinks(), - $this->privateLinkDB->count() - ); - } - - /** - * Save the bookmarks to the DB - */ - public function testSave() - { - $testDB = new BookmarkFileService($this->conf, $this->pluginManager, $this->history, $this->mutex, true); - $dbSize = $testDB->count(); - - $bookmark = new Bookmark(); - $testDB->add($bookmark); - - $testDB = new BookmarkFileService($this->conf, $this->pluginManager, $this->history, $this->mutex, true); - $this->assertEquals($dbSize + 1, $testDB->count()); - } - - /** - * Count existing bookmarks - public bookmarks hidden - */ - public function testCountHiddenPublic() - { - $this->conf->set('privacy.hide_public_links', true); - $linkDB = new BookmarkFileService($this->conf, $this->pluginManager, $this->history, $this->mutex, false); - - $this->assertEquals(0, $linkDB->count()); - } - - /** - * The URL corresponds to an existing entry in the DB - */ - public function testGetKnownLinkFromURL() - { - $link = $this->publicLinkDB->findByUrl('http://mediagoblin.org/'); - - $this->assertNotEquals(false, $link); - $this->assertContainsPolyfill( - 'A free software media publishing platform', - $link->getDescription() - ); - } - - /** - * The URL is not in the DB - */ - public function testGetUnknownLinkFromURL() - { - $this->assertEquals( - false, - $this->publicLinkDB->findByUrl('http://dev.null') - ); - } - - /** - * Lists all tags - */ - public function testAllTags() - { - $this->assertEquals( - [ - 'web' => 3, - 'cartoon' => 2, - 'gnu' => 2, - 'dev' => 1, - 'samba' => 1, - 'media' => 1, - 'software' => 1, - 'stallman' => 1, - 'free' => 1, - '-exclude' => 1, - 'hashtag' => 2, - // The DB contains a link with `sTuff` and another one with `stuff` tag. - // They need to be grouped with the first case found - order by date DESC: `sTuff`. - 'sTuff' => 2, - 'ut' => 1, - 'assurance' => 1, - 'coding-style' => 1, - 'quality' => 1, - 'standards' => 1, - ], - $this->publicLinkDB->bookmarksCountPerTag() - ); - - $this->assertEquals( - [ - 'web' => 4, - 'cartoon' => 3, - 'gnu' => 2, - 'dev' => 2, - 'samba' => 1, - 'media' => 1, - 'software' => 1, - 'stallman' => 1, - 'free' => 1, - 'html' => 1, - 'w3c' => 1, - 'css' => 1, - 'Mercurial' => 1, - 'sTuff' => 2, - '-exclude' => 1, - '.hidden' => 1, - 'hashtag' => 2, - 'tag1' => 1, - 'tag2' => 1, - 'tag3' => 1, - 'tag4' => 1, - 'ut' => 1, - 'assurance' => 1, - 'coding-style' => 1, - 'quality' => 1, - 'standards' => 1, - ], - $this->privateLinkDB->bookmarksCountPerTag() - ); - $this->assertEquals( - [ - 'cartoon' => 2, - 'gnu' => 1, - 'dev' => 1, - 'samba' => 1, - 'media' => 1, - 'html' => 1, - 'w3c' => 1, - 'css' => 1, - 'Mercurial' => 1, - '.hidden' => 1, - 'hashtag' => 1, - ], - $this->privateLinkDB->bookmarksCountPerTag(['web']) - ); - $this->assertEquals( - [ - 'html' => 1, - 'w3c' => 1, - 'css' => 1, - 'Mercurial' => 1, - ], - $this->privateLinkDB->bookmarksCountPerTag(['web'], 'private') - ); - } - - /** - * Test filter with string. - */ - public function testFilterString() - { - $tags = 'dev cartoon'; - $request = ['searchtags' => $tags]; - $this->assertEquals( - 2, - count($this->privateLinkDB->search($request, null, true)->getBookmarks()) - ); - } - - /** - * Test filter with array. - */ - public function testFilterArray() - { - $tags = ['dev', 'cartoon']; - $request = ['searchtags' => $tags]; - $this->assertEquals( - 2, - count($this->privateLinkDB->search($request, null, true)->getBookmarks()) - ); - } - - /** - * Test hidden tags feature: - * tags starting with a dot '.' are only visible when logged in. - */ - public function testHiddenTags() - { - $tags = '.hidden'; - $request = ['searchtags' => $tags]; - $this->assertEquals( - 1, - count($this->privateLinkDB->search($request, 'all', true)->getBookmarks()) - ); - - $this->assertEquals( - 0, - count($this->publicLinkDB->search($request, 'public', true)->getBookmarks()) - ); - } - - /** - * Test filterHash() with a valid smallhash. - */ - public function testFilterHashValid() - { - $request = smallHash('20150310_114651'); - $this->assertSame( - $request, - $this->publicLinkDB->findByHash($request)->getShortUrl() - ); - $request = smallHash('20150310_114633' . 8); - $this->assertSame( - $request, - $this->publicLinkDB->findByHash($request)->getShortUrl() - ); - } - - /** - * Test filterHash() with an invalid smallhash. - */ - public function testFilterHashInValid1() - { - $this->expectException(BookmarkNotFoundException::class); - - $request = 'blabla'; - $this->publicLinkDB->findByHash($request); - } - - /** - * Test filterHash() with an empty smallhash. - */ - public function testFilterHashInValid() - { - $this->expectException(BookmarkNotFoundException::class); - - $this->publicLinkDB->findByHash(''); - } - - /** - * Test filterHash() on a private bookmark while logged out. - */ - public function testFilterHashPrivateWhileLoggedOut() - { - $this->expectException(BookmarkNotFoundException::class); - $this->expectExceptionMessage('The link you are trying to reach does not exist or has been deleted'); - - $hash = smallHash('20141125_084734' . 6); - - $this->publicLinkDB->findByHash($hash); - } - - /** - * Test filterHash() with private key. - */ - public function testFilterHashWithPrivateKey() - { - $hash = smallHash('20141125_084734' . 6); - $privateKey = 'this is usually auto generated'; - - $bookmark = $this->privateLinkDB->findByHash($hash); - $bookmark->setAdditionalContentEntry('private_key', $privateKey); - $this->privateLinkDB->save(); - - $this->privateLinkDB = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - false - ); - $bookmark = $this->privateLinkDB->findByHash($hash, $privateKey); - - static::assertSame(6, $bookmark->getId()); - } - - /** - * Test linksCountPerTag all tags without filter. - * Equal occurrences should be sorted alphabetically. - */ - public function testCountLinkPerTagAllNoFilter() - { - $expected = [ - 'web' => 4, - 'cartoon' => 3, - 'dev' => 2, - 'gnu' => 2, - 'hashtag' => 2, - 'sTuff' => 2, - '-exclude' => 1, - '.hidden' => 1, - 'Mercurial' => 1, - 'css' => 1, - 'free' => 1, - 'html' => 1, - 'media' => 1, - 'samba' => 1, - 'software' => 1, - 'stallman' => 1, - 'tag1' => 1, - 'tag2' => 1, - 'tag3' => 1, - 'tag4' => 1, - 'ut' => 1, - 'w3c' => 1, - 'assurance' => 1, - 'coding-style' => 1, - 'quality' => 1, - 'standards' => 1, - ]; - $tags = $this->privateLinkDB->bookmarksCountPerTag(); - - $this->assertEquals($expected, $tags, var_export($tags, true)); - } - - /** - * Test linksCountPerTag all tags with filter. - * Equal occurrences should be sorted alphabetically. - */ - public function testCountLinkPerTagAllWithFilter() - { - $expected = [ - 'hashtag' => 2, - '-exclude' => 1, - '.hidden' => 1, - 'free' => 1, - 'media' => 1, - 'software' => 1, - 'stallman' => 1, - 'stuff' => 1, - 'web' => 1, - ]; - $tags = $this->privateLinkDB->bookmarksCountPerTag(['gnu']); - - $this->assertEquals($expected, $tags, var_export($tags, true)); - } - - /** - * Test linksCountPerTag public tags with filter. - * Equal occurrences should be sorted alphabetically. - */ - public function testCountLinkPerTagPublicWithFilter() - { - $expected = [ - 'hashtag' => 2, - '-exclude' => 1, - '.hidden' => 1, - 'free' => 1, - 'media' => 1, - 'software' => 1, - 'stallman' => 1, - 'stuff' => 1, - 'web' => 1, - ]; - $tags = $this->privateLinkDB->bookmarksCountPerTag(['gnu'], 'public'); - - $this->assertEquals($expected, $tags, var_export($tags, true)); - } - - /** - * Test linksCountPerTag public tags with filter. - * Equal occurrences should be sorted alphabetically. - */ - public function testCountLinkPerTagPrivateWithFilter() - { - $expected = [ - 'cartoon' => 1, - 'tag1' => 1, - 'tag2' => 1, - 'tag3' => 1, - 'tag4' => 1, - ]; - $tags = $this->privateLinkDB->bookmarksCountPerTag(['dev'], 'private'); - - $this->assertEquals($expected, $tags, var_export($tags, true)); - } - - /** - * Test linksCountPerTag public tags with filter. - * Equal occurrences should be sorted alphabetically. - */ - public function testCountTagsNoMarkdown() - { - $expected = [ - 'cartoon' => 3, - 'dev' => 2, - 'tag1' => 1, - 'tag2' => 1, - 'tag3' => 1, - 'tag4' => 1, - 'web' => 4, - 'gnu' => 2, - 'hashtag' => 2, - 'sTuff' => 2, - '-exclude' => 1, - '.hidden' => 1, - 'Mercurial' => 1, - 'css' => 1, - 'free' => 1, - 'html' => 1, - 'media' => 1, - 'newTagToCount' => 1, - 'samba' => 1, - 'software' => 1, - 'stallman' => 1, - 'ut' => 1, - 'w3c' => 1, - 'assurance' => 1, - 'coding-style' => 1, - 'quality' => 1, - 'standards' => 1, - ]; - $bookmark = new Bookmark(); - $bookmark->setTags(['newTagToCount', BookmarkMarkdownFormatter::NO_MD_TAG]); - $this->privateLinkDB->add($bookmark); - - $tags = $this->privateLinkDB->bookmarksCountPerTag(); - - $this->assertEquals($expected, $tags, var_export($tags, true)); - } - - /** - * Test find by dates in the middle of the datastore (sorted by dates) with a single bookmark as a result. - */ - public function testFilterByDateMidTimePeriodSingleBookmark(): void - { - $bookmarks = $this->privateLinkDB->findByDate( - DateTime::createFromFormat('Ymd_His', '20121206_150000'), - DateTime::createFromFormat('Ymd_His', '20121206_160000'), - $before, - $after - ); - - static::assertCount(1, $bookmarks); - - static::assertSame(9, $bookmarks[0]->getId()); - static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_142300'), $before); - static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_172539'), $after); - } - - /** - * Test find by dates in the middle of the datastore (sorted by dates) with a multiple bookmarks as a result. - */ - public function testFilterByDateMidTimePeriodMultipleBookmarks(): void - { - $bookmarks = $this->privateLinkDB->findByDate( - DateTime::createFromFormat('Ymd_His', '20121206_150000'), - DateTime::createFromFormat('Ymd_His', '20121206_180000'), - $before, - $after - ); - - static::assertCount(2, $bookmarks); - - static::assertSame(1, $bookmarks[0]->getId()); - static::assertSame(9, $bookmarks[1]->getId()); - static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_142300'), $before); - static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_182539'), $after); - } - - /** - * Test find by dates at the end of the datastore (sorted by dates). - */ - public function testFilterByDateLastTimePeriod(): void - { - $after = new DateTime(); - $bookmarks = $this->privateLinkDB->findByDate( - DateTime::createFromFormat('Ymd_His', '20150310_114640'), - DateTime::createFromFormat('Ymd_His', '20450101_010101'), - $before, - $after - ); - - static::assertCount(1, $bookmarks); - - static::assertSame(41, $bookmarks[0]->getId()); - static::assertEquals(DateTime::createFromFormat('Ymd_His', '20150310_114633'), $before); - static::assertNull($after); - } - - /** - * Test find by dates at the beginning of the datastore (sorted by dates). - */ - public function testFilterByDateFirstTimePeriod(): void - { - $before = new DateTime(); - $bookmarks = $this->privateLinkDB->findByDate( - DateTime::createFromFormat('Ymd_His', '20000101_101010'), - DateTime::createFromFormat('Ymd_His', '20100309_110000'), - $before, - $after - ); - - static::assertCount(1, $bookmarks); - - static::assertSame(11, $bookmarks[0]->getId()); - static::assertNull($before); - static::assertEquals(DateTime::createFromFormat('Ymd_His', '20100310_101010'), $after); - } - - /** - * Test getLatest with a sticky bookmark: it should be ignored and return the latest by creation date instead. - */ - public function testGetLatestWithSticky(): void - { - $bookmark = $this->publicLinkDB->getLatest(); - - static::assertSame(41, $bookmark->getId()); - } - - /** - * Test getLatest with a sticky bookmark: it should be ignored and return the latest by creation date instead. - */ - public function testGetLatestEmptyDatastore(): void - { - unlink($this->conf->get('resource.datastore')); - $this->publicLinkDB = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - false - ); - - $bookmark = $this->publicLinkDB->getLatest(); - - static::assertNull($bookmark); - } - - /** - * Allows to test LinkDB's private methods - * - * @see - * https://sebastian-bergmann.de/archives/881-Testing-Your-Privates.html - * http://stackoverflow.com/a/2798203 - */ - protected static function getMethod($name) - { - $class = new ReflectionClass('Shaarli\Bookmark\BookmarkFileService'); - $method = $class->getMethod($name); - $method->setAccessible(true); - return $method; - } -} diff --git a/tests/bookmark/BookmarkFilterTest.php b/tests/bookmark/BookmarkFilterTest.php deleted file mode 100644 index 7c5c3b6e..00000000 --- a/tests/bookmark/BookmarkFilterTest.php +++ /dev/null @@ -1,554 +0,0 @@ -set('resource.datastore', self::$testDatastore); - static::$pluginManager = new PluginManager($conf); - self::$refDB = new ReferenceLinkDB(); - self::$refDB->write(self::$testDatastore); - $history = new History('sandbox/history.php'); - self::$bookmarkService = new FakeBookmarkService($conf, static::$pluginManager, $history, $mutex, true); - self::$linkFilter = new BookmarkFilter(self::$bookmarkService->getBookmarks(), $conf, static::$pluginManager); - } - - /** - * Blank filter. - */ - public function testFilter() - { - $this->assertEquals( - self::$refDB->countLinks(), - count(self::$linkFilter->filter('', '')) - ); - - $this->assertEquals( - self::$refDB->countLinks(), - count(self::$linkFilter->filter('', '', 'all')) - ); - - $this->assertEquals( - self::$refDB->countLinks(), - count(self::$linkFilter->filter('', '', 'randomstr')) - ); - - // Private only. - $this->assertEquals( - self::$refDB->countPrivateLinks(), - count(self::$linkFilter->filter('', '', false, 'private')) - ); - - // Public only. - $this->assertEquals( - self::$refDB->countPublicLinks(), - count(self::$linkFilter->filter('', '', false, 'public')) - ); - - $this->assertEquals( - ReferenceLinkDB::$NB_LINKS_TOTAL, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '')) - ); - - $this->assertEquals( - self::$refDB->countUntaggedLinks(), - count( - self::$linkFilter->filter( - BookmarkFilter::$FILTER_TAG, - /*$request=*/ - '', - /*$casesensitive=*/ - false, - /*$visibility=*/ - 'all', - /*$untaggedonly=*/ - true - ) - ) - ); - - $this->assertEquals( - ReferenceLinkDB::$NB_LINKS_TOTAL, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, '')) - ); - } - - /** - * Filter bookmarks using a tag - */ - public function testFilterOneTag() - { - $this->assertEquals( - 4, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false)) - ); - - $this->assertEquals( - 4, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '+web', false)) - ); - - $this->assertEquals( - 4, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'all')) - ); - - $this->assertEquals( - 4, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'default-blabla')) - ); - - // Private only. - $this->assertEquals( - 1, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'private')) - ); - - // Public only. - $this->assertEquals( - 3, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'public')) - ); - } - - /** - * Filter bookmarks using a tag - case-sensitive - */ - public function testFilterCaseSensitiveTag() - { - $this->assertEquals( - 0, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'mercurial', true)) - ); - - $this->assertEquals( - 1, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'Mercurial', true)) - ); - } - - /** - * Filter bookmarks using a tag combination - */ - public function testFilterMultipleTags() - { - $this->assertEquals( - 2, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'dev cartoon', false)) - ); - } - - /** - * Filter bookmarks using a non-existent tag - */ - public function testFilterUnknownTag() - { - $this->assertEquals( - 0, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'null', false)) - ); - } - - /** - * Retrieve a link entry with its hash - */ - public function testFilterSmallHash() - { - $links = self::$linkFilter->filter(BookmarkFilter::$FILTER_HASH, 'IuWvgA'); - - $this->assertEquals( - 1, - count($links) - ); - - $this->assertEquals( - 'MediaGoblin', - $links[7]->getTitle() - ); - } - - /** - * No link for this hash - */ - public function testFilterUnknownSmallHash() - { - $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class); - - self::$linkFilter->filter(BookmarkFilter::$FILTER_HASH, 'Iblaah'); - } - - /** - * Full-text search - no result found. - */ - public function testFilterFullTextNoResult() - { - $this->assertEquals( - 0, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'azertyuiop')) - ); - } - - /** - * Full-text search - result from a link's URL - */ - public function testFilterFullTextURL() - { - $this->assertEquals( - 2, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'ars.userfriendly.org')) - ); - - $this->assertEquals( - 2, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'ars org')) - ); - } - - /** - * Full-text search - result from a link's title only - */ - public function testFilterFullTextTitle() - { - // use miscellaneous cases - $this->assertEquals( - 2, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'userfriendly -')) - ); - $this->assertEquals( - 2, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'UserFriendly -')) - ); - $this->assertEquals( - 2, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'uSeRFrIendlY -')) - ); - - // use miscellaneous case and offset - $this->assertEquals( - 2, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'RFrIendL')) - ); - } - - /** - * Full-text search - result from the link's description only - */ - public function testFilterFullTextDescription() - { - $this->assertEquals( - 1, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'publishing media')) - ); - - $this->assertEquals( - 1, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'mercurial w3c')) - ); - - $this->assertEquals( - 3, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, '"free software"')) - ); - } - - /** - * Full-text search - result from the link's tags only - */ - public function testFilterFullTextTags() - { - $this->assertEquals( - 6, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web')) - ); - - $this->assertEquals( - 6, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', 'all')) - ); - - $this->assertEquals( - 6, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', 'bla')) - ); - - // Private only. - $this->assertEquals( - 1, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', false, 'private')) - ); - - // Public only. - $this->assertEquals( - 5, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', false, 'public')) - ); - } - - /** - * Full-text search - result set from mixed sources - */ - public function testFilterFullTextMixed() - { - $this->assertEquals( - 3, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'free software')) - ); - } - - /** - * Full-text search - test exclusion with '-'. - */ - public function testExcludeSearch() - { - $this->assertEquals( - 1, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'free -gnu')) - ); - - $this->assertEquals( - ReferenceLinkDB::$NB_LINKS_TOTAL - 1, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, '-revolution')) - ); - } - - /** - * Full-text search - test AND, exact terms and exclusion combined, across fields. - */ - public function testMultiSearch() - { - $this->assertEquals( - 2, - count(self::$linkFilter->filter( - BookmarkFilter::$FILTER_TEXT, - '"Free Software " stallman "read this" @website stuff' - )) - ); - - $this->assertEquals( - 1, - count(self::$linkFilter->filter( - BookmarkFilter::$FILTER_TEXT, - '"free software " stallman "read this" -beard @website stuff' - )) - ); - } - - /** - * Full-text search - make sure that exact search won't work across fields. - */ - public function testSearchExactTermMultiFieldsKo() - { - $this->assertEquals( - 0, - count(self::$linkFilter->filter( - BookmarkFilter::$FILTER_TEXT, - '"designer naming"' - )) - ); - - $this->assertEquals( - 0, - count(self::$linkFilter->filter( - BookmarkFilter::$FILTER_TEXT, - '"designernaming"' - )) - ); - } - - /** - * Tag search with exclusion. - */ - public function testTagFilterWithExclusion() - { - $this->assertEquals( - 1, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'gnu -free')) - ); - - $this->assertEquals( - ReferenceLinkDB::$NB_LINKS_TOTAL - 1, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '-free')) - ); - } - - /** - * Test crossed search (terms + tags). - */ - public function testFilterCrossedSearch() - { - $terms = '"Free Software " stallman "read this" @website stuff'; - $tags = 'free'; - $this->assertEquals( - 1, - count(self::$linkFilter->filter( - BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT, - [$tags, $terms] - )) - ); - $this->assertEquals( - 2, - count(self::$linkFilter->filter( - BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT, - ['', $terms] - )) - ); - $this->assertEquals( - 1, - count(self::$linkFilter->filter( - BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT, - [false, 'PSR-2'] - )) - ); - $this->assertEquals( - 1, - count(self::$linkFilter->filter( - BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT, - [$tags, ''] - )) - ); - $this->assertEquals( - ReferenceLinkDB::$NB_LINKS_TOTAL, - count(self::$linkFilter->filter( - BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT, - '' - )) - ); - } - - /** - * Tag search with OR optional tags. - */ - public function testTagFilterOr() - { - $this->assertEquals( - 5, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '~cartoon ~web')) - ); - - $this->assertEquals( - 6, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '~c*t*n ~st*')) - ); - - $this->assertEquals( - 2, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '~cartoon ~web dev')) - ); - - $this->assertEquals( - 2, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '~cartoon ~web +dev')) - ); - - $this->assertEquals( - 4, - count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '~cartoon ~web -samba')) - ); - } - - /** - * Filter bookmarks by #hashtag. - */ - public function testFilterByHashtag() - { - $hashtag = 'hashtag'; - $this->assertEquals( - 3, - count(self::$linkFilter->filter( - BookmarkFilter::$FILTER_TAG, - $hashtag - )) - ); - - $hashtag = 'private'; - $this->assertEquals( - 1, - count(self::$linkFilter->filter( - BookmarkFilter::$FILTER_TAG, - $hashtag, - false, - 'private' - )) - ); - } - - /** - * Test search result highlights in every field of bookmark reference #9. - */ - public function testFullTextSearchHighlight(): void - { - $bookmarks = self::$linkFilter->filter( - BookmarkFilter::$FILTER_TEXT, - '"psr-2" coding guide http fig "psr-2/" "This guide" basic standard. coding-style quality assurance' - ); - - static::assertCount(1, $bookmarks); - static::assertArrayHasKey(9, $bookmarks); - - $bookmark = $bookmarks[9]; - $expectedHighlights = [ - 'title' => [ - ['start' => 0, 'end' => 5], // "psr-2" - ['start' => 7, 'end' => 13], // coding - ['start' => 20, 'end' => 25], // guide - ], - 'description' => [ - ['start' => 0, 'end' => 10], // "This guide" - ['start' => 45, 'end' => 50], // basic - ['start' => 58, 'end' => 67], // standard. - ], - 'url' => [ - ['start' => 0, 'end' => 4], // http - ['start' => 15, 'end' => 18], // fig - ['start' => 27, 'end' => 33], // "psr-2/" - ], - 'tags' => [ - ['start' => 0, 'end' => 12], // coding-style - ['start' => 23, 'end' => 30], // quality - ['start' => 31, 'end' => 40], // assurance - ], - ]; - static::assertSame($expectedHighlights, $bookmark->getAdditionalContentEntry('search_highlight')); - } -} diff --git a/tests/bookmark/BookmarkInitializerTest.php b/tests/bookmark/BookmarkInitializerTest.php deleted file mode 100644 index 619cf83c..00000000 --- a/tests/bookmark/BookmarkInitializerTest.php +++ /dev/null @@ -1,185 +0,0 @@ -mutex = new NoMutex(); - if (file_exists(self::$testDatastore)) { - unlink(self::$testDatastore); - } - - copy('tests/utils/config/configJson.json.php', self::$testConf . '.json.php'); - $this->conf = new ConfigManager(self::$testConf); - $this->conf->set('resource.datastore', self::$testDatastore); - $this->pluginManager = new PluginManager($this->conf); - $this->history = new History('sandbox/history.php'); - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $this->initializer = new BookmarkInitializer($this->bookmarkService); - } - - /** - * Test initialize() with a data store containing bookmarks. - */ - public function testInitializeNotEmptyDataStore(): void - { - $refDB = new ReferenceLinkDB(); - $refDB->write(self::$testDatastore); - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - $this->initializer = new BookmarkInitializer($this->bookmarkService); - - $this->initializer->initialize(); - - $this->assertEquals($refDB->countLinks() + 3, $this->bookmarkService->count()); - - $bookmark = $this->bookmarkService->get(43); - $this->assertStringStartsWith( - 'Shaarli will automatically pick up the thumbnail for links to a variety of websites.', - $bookmark->getDescription() - ); - $this->assertTrue($bookmark->isPrivate()); - - $bookmark = $this->bookmarkService->get(44); - $this->assertStringStartsWith( - 'Adding a shaare without entering a URL creates a text-only "note" post such as this one.', - $bookmark->getDescription() - ); - $this->assertTrue($bookmark->isPrivate()); - - $bookmark = $this->bookmarkService->get(45); - $this->assertStringStartsWith( - 'Welcome to Shaarli!', - $bookmark->getDescription() - ); - $this->assertFalse($bookmark->isPrivate()); - - $this->bookmarkService->save(); - - // Reload from file - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - $this->assertEquals($refDB->countLinks() + 3, $this->bookmarkService->count()); - - $bookmark = $this->bookmarkService->get(43); - $this->assertStringStartsWith( - 'Shaarli will automatically pick up the thumbnail for links to a variety of websites.', - $bookmark->getDescription() - ); - $this->assertTrue($bookmark->isPrivate()); - - $bookmark = $this->bookmarkService->get(44); - $this->assertStringStartsWith( - 'Adding a shaare without entering a URL creates a text-only "note" post such as this one.', - $bookmark->getDescription() - ); - $this->assertTrue($bookmark->isPrivate()); - - $bookmark = $this->bookmarkService->get(45); - $this->assertStringStartsWith( - 'Welcome to Shaarli!', - $bookmark->getDescription() - ); - $this->assertFalse($bookmark->isPrivate()); - } - - /** - * Test initialize() with an a non existent datastore file . - */ - public function testInitializeNonExistentDataStore(): void - { - $this->conf->set('resource.datastore', static::$testDatastore . '_empty'); - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $this->pluginManager, - $this->history, - $this->mutex, - true - ); - - $this->initializer->initialize(); - - $this->assertEquals(3, $this->bookmarkService->count()); - $bookmark = $this->bookmarkService->get(0); - $this->assertStringStartsWith( - 'Shaarli will automatically pick up the thumbnail for links to a variety of websites.', - $bookmark->getDescription() - ); - $this->assertTrue($bookmark->isPrivate()); - - $bookmark = $this->bookmarkService->get(1); - $this->assertStringStartsWith( - 'Adding a shaare without entering a URL creates a text-only "note" post such as this one.', - $bookmark->getDescription() - ); - $this->assertTrue($bookmark->isPrivate()); - - $bookmark = $this->bookmarkService->get(2); - $this->assertStringStartsWith( - 'Welcome to Shaarli!', - $bookmark->getDescription() - ); - $this->assertFalse($bookmark->isPrivate()); - } -} diff --git a/tests/bookmark/BookmarkTest.php b/tests/bookmark/BookmarkTest.php deleted file mode 100644 index 6a34e2ce..00000000 --- a/tests/bookmark/BookmarkTest.php +++ /dev/null @@ -1,468 +0,0 @@ - 1, - 'shorturl' => 'abc', - 'url' => 'https://domain.tld/oof.html?param=value#anchor', - 'title' => 'This is an array link', - 'description' => 'HTML desc

hi!

', - 'thumbnail' => 'https://domain.tld/pic.png', - 'sticky' => true, - 'created' => new \DateTime('-1 minute'), - 'tags' => ['tag1', 'tag2', 'chair'], - 'updated' => new \DateTime(), - 'private' => true, - ]; - - $bookmark = (new Bookmark())->fromArray($data); - $this->assertEquals($data['id'], $bookmark->getId()); - $this->assertEquals($data['shorturl'], $bookmark->getShortUrl()); - $this->assertEquals($data['url'], $bookmark->getUrl()); - $this->assertEquals($data['title'], $bookmark->getTitle()); - $this->assertEquals($data['description'], $bookmark->getDescription()); - $this->assertEquals($data['thumbnail'], $bookmark->getThumbnail()); - $this->assertEquals($data['sticky'], $bookmark->isSticky()); - $this->assertEquals($data['created'], $bookmark->getCreated()); - $this->assertEquals($data['tags'], $bookmark->getTags()); - $this->assertEquals('tag1 tag2 chair', $bookmark->getTagsString()); - $this->assertEquals($data['updated'], $bookmark->getUpdated()); - $this->assertEquals($data['private'], $bookmark->isPrivate()); - $this->assertFalse($bookmark->isNote()); - } - - /** - * Test fromArray() with a link with minimal data. - * Note that I use null values everywhere but this should not happen in the real world. - */ - public function testFromArrayMinimal() - { - $data = [ - 'id' => null, - 'shorturl' => null, - 'url' => null, - 'title' => null, - 'description' => null, - 'created' => null, - 'tags' => null, - 'private' => null, - ]; - - $bookmark = (new Bookmark())->fromArray($data); - $this->assertNull($bookmark->getId()); - $this->assertNull($bookmark->getShortUrl()); - $this->assertNull($bookmark->getUrl()); - $this->assertNull($bookmark->getTitle()); - $this->assertEquals('', $bookmark->getDescription()); - $this->assertNull($bookmark->getCreated()); - $this->assertEquals([], $bookmark->getTags()); - $this->assertEquals('', $bookmark->getTagsString()); - $this->assertNull($bookmark->getUpdated()); - $this->assertFalse($bookmark->getThumbnail()); - $this->assertFalse($bookmark->isSticky()); - $this->assertFalse($bookmark->isPrivate()); - $this->assertTrue($bookmark->isNote()); - } - - /** - * Test fromArray() with a link with a custom tags separator - */ - public function testFromArrayCustomTagsSeparator() - { - $data = [ - 'id' => 1, - 'tags' => ['tag1', 'tag2', 'chair'], - ]; - - $bookmark = (new Bookmark())->fromArray($data, '@'); - $this->assertEquals($data['id'], $bookmark->getId()); - $this->assertEquals($data['tags'], $bookmark->getTags()); - $this->assertEquals('tag1@tag2@chair', $bookmark->getTagsString('@')); - } - - - /** - * Test validate() with a valid minimal bookmark - */ - public function testValidateValidFullBookmark() - { - $bookmark = new Bookmark(); - $bookmark->setId(2); - $bookmark->setShortUrl('abc'); - $bookmark->setCreated($date = \DateTime::createFromFormat('Ymd_His', '20190514_200102')); - $bookmark->setUpdated($dateUp = \DateTime::createFromFormat('Ymd_His', '20190514_210203')); - $bookmark->setUrl($url = 'https://domain.tld/oof.html?param=value#anchor'); - $bookmark->setTitle($title = 'This is an array link'); - $bookmark->setDescription($desc = 'HTML desc

hi!

'); - $bookmark->setTags($tags = ['tag1', 'tag2', 'chair']); - $bookmark->setThumbnail($thumb = 'https://domain.tld/pic.png'); - $bookmark->setPrivate(true); - $bookmark->validate(); - - $this->assertEquals(2, $bookmark->getId()); - $this->assertEquals('abc', $bookmark->getShortUrl()); - $this->assertEquals($date, $bookmark->getCreated()); - $this->assertEquals($dateUp, $bookmark->getUpdated()); - $this->assertEquals($url, $bookmark->getUrl()); - $this->assertEquals($title, $bookmark->getTitle()); - $this->assertEquals($desc, $bookmark->getDescription()); - $this->assertEquals($tags, $bookmark->getTags()); - $this->assertEquals(implode(' ', $tags), $bookmark->getTagsString()); - $this->assertEquals($thumb, $bookmark->getThumbnail()); - $this->assertTrue($bookmark->isPrivate()); - $this->assertFalse($bookmark->isNote()); - } - - /** - * Test validate() with a valid minimal bookmark - */ - public function testValidateValidMinimalBookmark() - { - $bookmark = new Bookmark(); - $bookmark->setId(1); - $bookmark->setShortUrl('abc'); - $bookmark->setCreated($date = \DateTime::createFromFormat('Ymd_His', '20190514_200102')); - $bookmark->validate(); - - $this->assertEquals(1, $bookmark->getId()); - $this->assertEquals('abc', $bookmark->getShortUrl()); - $this->assertEquals($date, $bookmark->getCreated()); - $this->assertEquals('/shaare/abc', $bookmark->getUrl()); - $this->assertEquals('/shaare/abc', $bookmark->getTitle()); - $this->assertEquals('', $bookmark->getDescription()); - $this->assertEquals([], $bookmark->getTags()); - $this->assertEquals('', $bookmark->getTagsString()); - $this->assertFalse($bookmark->getThumbnail()); - $this->assertFalse($bookmark->isPrivate()); - $this->assertTrue($bookmark->isNote()); - $this->assertNull($bookmark->getUpdated()); - } - - /** - * Test validate() with a a bookmark without ID. - */ - public function testValidateNotValidNoId() - { - $bookmark = new Bookmark(); - $bookmark->setShortUrl('abc'); - $bookmark->setCreated(\DateTime::createFromFormat('Ymd_His', '20190514_200102')); - $exception = null; - try { - $bookmark->validate(); - } catch (InvalidBookmarkException $e) { - $exception = $e; - } - $this->assertNotNull($exception); - $this->assertContainsPolyfill('- ID: ' . PHP_EOL, $exception->getMessage()); - } - - /** - * Test validate() with a a bookmark without short url. - */ - public function testValidateNotValidNoShortUrl() - { - $bookmark = new Bookmark(); - $bookmark->setId(1); - $bookmark->setCreated(\DateTime::createFromFormat('Ymd_His', '20190514_200102')); - $bookmark->setShortUrl(null); - $exception = null; - try { - $bookmark->validate(); - } catch (InvalidBookmarkException $e) { - $exception = $e; - } - $this->assertNotNull($exception); - $this->assertContainsPolyfill('- ShortUrl: ' . PHP_EOL, $exception->getMessage()); - } - - /** - * Test validate() with a a bookmark without created datetime. - */ - public function testValidateNotValidNoCreated() - { - $bookmark = new Bookmark(); - $bookmark->setId(1); - $bookmark->setShortUrl('abc'); - $bookmark->setCreated(null); - $exception = null; - try { - $bookmark->validate(); - } catch (InvalidBookmarkException $e) { - $exception = $e; - } - $this->assertNotNull($exception); - $this->assertContainsPolyfill('- Created: ' . PHP_EOL, $exception->getMessage()); - } - - /** - * Test setId() and make sure that default fields are generated. - */ - public function testSetIdEmptyGeneratedFields() - { - $bookmark = new Bookmark(); - $bookmark->setId(2); - - $this->assertEquals(2, $bookmark->getId()); - $this->assertRegExp('/[\w\-]{6}/', $bookmark->getShortUrl()); - $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getCreated()); - } - - /** - * Test setId() and with generated fields already set. - */ - public function testSetIdSetGeneratedFields() - { - $bookmark = new Bookmark(); - $bookmark->setShortUrl('abc'); - $bookmark->setCreated($date = \DateTime::createFromFormat('Ymd_His', '20190514_200102')); - $bookmark->setId(2); - - $this->assertEquals(2, $bookmark->getId()); - $this->assertEquals('abc', $bookmark->getShortUrl()); - $this->assertEquals($date, $bookmark->getCreated()); - } - - /** - * Test setUrl() and make sure it accepts custom protocols - */ - public function testGetUrlWithValidProtocols() - { - $bookmark = new Bookmark(); - $bookmark->setUrl($url = 'myprotocol://helloworld', ['myprotocol']); - $this->assertEquals($url, $bookmark->getUrl()); - - $bookmark->setUrl($url = 'https://helloworld.tld', ['myprotocol']); - $this->assertEquals($url, $bookmark->getUrl()); - } - - /** - * Test setUrl() and make sure it accepts custom protocols - */ - public function testGetUrlWithNotValidProtocols() - { - $bookmark = new Bookmark(); - $bookmark->setUrl('myprotocol://helloworld', []); - $this->assertEquals('http://helloworld', $bookmark->getUrl()); - - $bookmark->setUrl($url = 'https://helloworld.tld', []); - $this->assertEquals($url, $bookmark->getUrl()); - } - - /** - * Test addTag() and DeleteTag() - */ - - public function testAddDeleteTags() - { - $bookmark = new Bookmark(); - - $bookmark->addTag('tag1'); - $this->assertEquals( - [ - 'tag1', - ], - $bookmark->getTags() - ); - - // Ignore if tag is already present - $bookmark->addTag('tag2'); - $bookmark->addTag('tag1'); - $this->assertEquals( - [ - 'tag1', - 'tag2', - ], - $bookmark->getTags() - ); - - // Ignore deleting tags not present - $bookmark->deleteTag('tag5'); - $this->assertEquals( - [ - 'tag1', - 'tag2', - ], - $bookmark->getTags() - ); - - // Delete multiples - $bookmark->setTags(['tag3', 'tag1', 'tag4', 'tag3', 'tag3', 'tag4']); - $bookmark->deleteTag('tag3'); - $this->assertEquals( - [ - 'tag1', - 'tag4', - 'tag4', - ], - $bookmark->getTags() - ); - $bookmark->deleteTag('tag4'); - $this->assertEquals( - [ - 'tag1', - ], - $bookmark->getTags() - ); - } - - /** - * Test setTagsString() with exotic data - */ - public function testSetTagsString() - { - $bookmark = new Bookmark(); - - $str = 'tag1 tag2 tag3.tag3-2 tag4 -tag5 '; - $bookmark->setTagsString($str); - $this->assertEquals( - [ - 'tag1', - 'tag2', - 'tag3.tag3-2', - 'tag4', - 'tag5', - ], - $bookmark->getTags() - ); - } - - /** - * Test setTags() with exotic data - */ - public function testSetTags() - { - $bookmark = new Bookmark(); - - $array = [ - 'tag1 ', - ' tag2', - 'tag3.tag3-2', - ' tag4', - ' ', - '-tag5 ', - ]; - $bookmark->setTags($array); - $this->assertEquals( - [ - 'tag1', - 'tag2', - 'tag3.tag3-2', - 'tag4', - 'tag5', - ], - $bookmark->getTags() - ); - } - - /** - * Test renameTag() - */ - public function testRenameTag() - { - $bookmark = new Bookmark(); - $bookmark->setTags(['tag1', 'tag2', 'chair']); - $bookmark->renameTag('chair', 'table'); - $this->assertEquals(['tag1', 'tag2', 'table'], $bookmark->getTags()); - $bookmark->renameTag('tag1', 'tag42'); - $this->assertEquals(['tag42', 'tag2', 'table'], $bookmark->getTags()); - $bookmark->renameTag('tag42', 'tag43'); - $this->assertEquals(['tag43', 'tag2', 'table'], $bookmark->getTags()); - $bookmark->renameTag('table', 'desk'); - $this->assertEquals(['tag43', 'tag2', 'desk'], $bookmark->getTags()); - } - - /** - * Test renameTag() with a tag that is not present in the bookmark - */ - public function testRenameTagNotExists() - { - $bookmark = new Bookmark(); - $bookmark->setTags(['tag1', 'tag2', 'chair']); - $bookmark->renameTag('nope', 'table'); - $this->assertEquals(['tag1', 'tag2', 'chair'], $bookmark->getTags()); - } - - /** - * Test deleteTag() - */ - public function testDeleteTag() - { - $bookmark = new Bookmark(); - $bookmark->setTags(['tag1', 'tag2', 'chair']); - $bookmark->deleteTag('chair'); - $this->assertEquals(['tag1', 'tag2'], $bookmark->getTags()); - $bookmark->deleteTag('tag1'); - $this->assertEquals(['tag2'], $bookmark->getTags()); - $bookmark->deleteTag('tag2'); - $this->assertEquals([], $bookmark->getTags()); - } - - /** - * Test deleteTag() with a tag that is not present in the bookmark - */ - public function testDeleteTagNotExists() - { - $bookmark = new Bookmark(); - $bookmark->setTags(['tag1', 'tag2', 'chair']); - $bookmark->deleteTag('nope'); - $this->assertEquals(['tag1', 'tag2', 'chair'], $bookmark->getTags()); - } - - /** - * Test shouldUpdateThumbnail() with bookmarks needing an update. - */ - public function testShouldUpdateThumbnail(): void - { - $bookmark = (new Bookmark())->setUrl('http://domain.tld/with-image'); - - static::assertTrue($bookmark->shouldUpdateThumbnail()); - - $bookmark = (new Bookmark()) - ->setUrl('http://domain.tld/with-image') - ->setThumbnail('unknown file') - ; - - static::assertTrue($bookmark->shouldUpdateThumbnail()); - } - - /** - * Test shouldUpdateThumbnail() with bookmarks that should not update. - */ - public function testShouldNotUpdateThumbnail(): void - { - $bookmark = (new Bookmark()); - - static::assertFalse($bookmark->shouldUpdateThumbnail()); - - $bookmark = (new Bookmark()) - ->setUrl('ftp://domain.tld/other-protocol', ['ftp']) - ; - - static::assertFalse($bookmark->shouldUpdateThumbnail()); - - $bookmark = (new Bookmark()) - ->setUrl('http://domain.tld/with-image') - ->setThumbnail(__FILE__) - ; - - static::assertFalse($bookmark->shouldUpdateThumbnail()); - - $bookmark = (new Bookmark())->setUrl('/shaare/abcdef'); - - static::assertFalse($bookmark->shouldUpdateThumbnail()); - } -} diff --git a/tests/bookmark/LinkUtilsTest.php b/tests/bookmark/LinkUtilsTest.php deleted file mode 100644 index 922bb484..00000000 --- a/tests/bookmark/LinkUtilsTest.php +++ /dev/null @@ -1,767 +0,0 @@ -stuff' . $title . ''; - $this->assertEquals($title, html_extract_title($html)); - $html = '' . $title . 'blablaanother'; - $this->assertEquals($title, html_extract_title($html)); - } - - /** - * Test html_extract_title() when the title is not found. - */ - public function testHtmlExtractNonExistentTitle() - { - $html = 'stuff'; - $this->assertFalse(html_extract_title($html)); - } - - /** - * Test headers_extract_charset() when the charset is found. - */ - public function testHeadersExtractExistentCharset() - { - $charset = 'x-MacCroatian'; - $headers = 'text/html; charset=' . $charset; - $this->assertEquals(strtolower($charset), header_extract_charset($headers)); - } - - /** - * Test headers_extract_charset() when the charset is found with odd quotes. - */ - public function testHeadersExtractExistentCharsetWithQuotes() - { - $charset = 'x-MacCroatian'; - $headers = 'text/html; charset="' . $charset . '"otherstuff="test"'; - $this->assertEquals(strtolower($charset), header_extract_charset($headers)); - - $headers = 'text/html; charset=\'' . $charset . '\'otherstuff="test"'; - $this->assertEquals(strtolower($charset), header_extract_charset($headers)); - } - - /** - * Test headers_extract_charset() when the charset is not found. - */ - public function testHeadersExtractNonExistentCharset() - { - $headers = ''; - $this->assertFalse(header_extract_charset($headers)); - - $headers = 'text/html'; - $this->assertFalse(header_extract_charset($headers)); - } - - /** - * Test html_extract_charset() when the charset is found. - */ - public function testHtmlExtractExistentCharset() - { - $charset = 'x-MacCroatian'; - $html = 'stuff2'; - $this->assertEquals(strtolower($charset), html_extract_charset($html)); - } - - /** - * Test html_extract_charset() when the charset is not found. - */ - public function testHtmlExtractNonExistentCharset() - { - $html = 'stuff'; - $this->assertFalse(html_extract_charset($html)); - $html = 'stuff'; - $this->assertFalse(html_extract_charset($html)); - } - - /** - * Test html_extract_tag() when the tag '; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // Simple OpenGraph - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // Simple reversed OpenGraph - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // ItemProp OpenGraph - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // OpenGraph without quotes - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // OpenGraph reversed without quotes - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // OpenGraph with noise - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // OpenGraph reversed with noise - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // OpenGraph multiple properties start - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // OpenGraph multiple properties end - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // OpenGraph multiple properties both end - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // OpenGraph multiple properties both end with noise - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // OpenGraph reversed multiple properties start - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // OpenGraph reversed multiple properties end - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // OpenGraph reversed multiple properties both end - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // OpenGraph reversed multiple properties both end with noise - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - // Suggestion from #1375 - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - } - - /** - * Test html_extract_tag() with double quoted content containing single quote, and the opposite. - */ - public function testHtmlExtractExistentNameTagWithMixedQuotes(): void - { - $description = 'Bob and Alice share M&M\'s.'; - - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - $description = 'Bob and Alice share "cookies".'; - - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - - $html = ''; - $this->assertEquals($description, html_extract_tag('description', $html)); - } - - /** - * Test html_extract_tag() when the tag assertFalse(html_extract_tag('description', $html)); - - // Partial meta tag - $html = ''; - $this->assertFalse(html_extract_tag('description', $html)); - - $html = ''; - $this->assertFalse(html_extract_tag('description', $html)); - - $html = ''; - $this->assertFalse(html_extract_tag('description', $html)); - - $html = ''; - $this->assertFalse(html_extract_tag('description', $html)); - - $html = ''; - $this->assertFalse(html_extract_tag('description', $html)); - - $html = ''; - $this->assertFalse(html_extract_tag('description', $html)); - } - - /** - * Test html_extract_tag() when the tag '; - $this->assertEquals($description, html_extract_tag('description', $html)); - } - - /** - * Test html_extract_tag() when the tag '; - $this->assertFalse(html_extract_tag('description', $html)); - } - - public function testHtmlExtractDescriptionFromGoogleRealCase(): void - { - $html = 'id="gsr">' . - '' . - 'assertSame('Bonnes fêtes de fin d\'année ! #GoogleDoodle', html_extract_tag('description', $html)); - } - - /** - * Test the header callback with valid value - */ - public function testCurlHeaderCallbackOk(): void - { - $callback = get_curl_header_callback($charset, 'ut_curl_getinfo_ok'); - $data = [ - 'HTTP/1.1 200 OK', - 'Server: GitHub.com', - 'Date: Sat, 28 Oct 2017 12:01:33 GMT', - 'Content-Type: text/html; charset=utf-8', - 'Status: 200 OK', - ]; - - foreach ($data as $chunk) { - static::assertIsInt($callback(null, $chunk)); - } - - static::assertSame('utf-8', $charset); - } - - /** - * Test the download callback with valid value - */ - public function testCurlDownloadCallbackOk(): void - { - $charset = 'utf-8'; - $callback = get_curl_download_callback( - $charset, - $title, - $desc, - $keywords, - false, - ' ' - ); - - $data = [ - 'th=device-width">' - . 'Refactoring · GitHub' - . '' - . '', - ]; - - foreach ($data as $chunk) { - static::assertSame(strlen($chunk), $callback(null, $chunk)); - } - - static::assertSame('utf-8', $charset); - static::assertSame('Refactoring · GitHub', $title); - static::assertEmpty($desc); - static::assertEmpty($keywords); - } - - /** - * Test the header callback with valid value - */ - public function testCurlHeaderCallbackNoCharset(): void - { - $callback = get_curl_header_callback($charset, 'ut_curl_getinfo_no_charset'); - $data = [ - 'HTTP/1.1 200 OK', - ]; - - foreach ($data as $chunk) { - static::assertSame(strlen($chunk), $callback(null, $chunk)); - } - - static::assertFalse($charset); - } - - /** - * Test the download callback with valid values and no charset - */ - public function testCurlDownloadCallbackOkNoCharset(): void - { - $charset = null; - $callback = get_curl_download_callback( - $charset, - $title, - $desc, - $keywords, - false, - ' ' - ); - - $data = [ - 'end' => 'th=device-width">' - . 'Refactoring · GitHub' - . '' - . '', - ]; - - foreach ($data as $chunk) { - static::assertSame(strlen($chunk), $callback(null, $chunk)); - } - - $this->assertEmpty($charset); - $this->assertEquals('Refactoring · GitHub', $title); - $this->assertEmpty($desc); - $this->assertEmpty($keywords); - } - - /** - * Test the download callback with valid values and no charset - */ - public function testCurlDownloadCallbackOkHtmlCharset(): void - { - $charset = null; - $callback = get_curl_download_callback( - $charset, - $title, - $desc, - $keywords, - false, - ' ' - ); - - $data = [ - '', - 'end' => 'th=device-width">' - . 'Refactoring · GitHub' - . '' - . '', - ]; - foreach ($data as $chunk) { - static::assertSame(strlen($chunk), $callback(null, $chunk)); - } - - $this->assertEquals('utf-8', $charset); - $this->assertEquals('Refactoring · GitHub', $title); - $this->assertEmpty($desc); - $this->assertEmpty($keywords); - } - - /** - * Test the download callback with valid values and no title - */ - public function testCurlDownloadCallbackOkNoTitle(): void - { - $charset = 'utf-8'; - $callback = get_curl_download_callback( - $charset, - $title, - $desc, - $keywords, - false, - ' ' - ); - - $data = [ - 'end' => 'th=device-width">Refactoring · GitHub' - . 'Refactoring · GitHub' - . '' - . '', - ]; - - foreach ($data as $chunk) { - static::assertSame(strlen($chunk), $callback(null, $chunk)); - } - - $this->assertEquals('utf-8', $charset); - $this->assertEquals('Refactoring · GitHub', $title); - $this->assertEquals('link desc', $desc); - $this->assertEquals('key1 key2', $keywords); - } - - /** - * Test the download callback with valid value, and retrieve_description option enabled, - * but no desc or keyword defined in the page. - */ - public function testCurlDownloadCallbackOkWithDescNotFound(): void - { - $charset = 'utf-8'; - $callback = get_curl_download_callback( - $charset, - $title, - $desc, - $keywords, - true, - 'ut_curl_getinfo_ok' - ); - $data = [ - 'th=device-width">' - . 'Refactoring · GitHub' - . '' - . 'http://hello.there/is=someone#here otherstuff'; - $processedText = text2clickable($text); - $this->assertEquals($expectedText, $processedText); - - $text = 'stuff http://hello.there/is=someone#here(please) otherstuff'; - $expectedText = 'stuff ' - . 'http://hello.there/is=someone#here(please) otherstuff'; - $processedText = text2clickable($text); - $this->assertEquals($expectedText, $processedText); - - $text = 'stuff http://hello.there/is=someone#here(please)&no otherstuff'; - $text = 'stuff http://hello.there/is=someone#here(please)&no otherstuff'; - $expectedText = 'stuff ' - . 'http://hello.there/is=someone#here(please)&no otherstuff'; - $processedText = text2clickable($text); - $this->assertEquals($expectedText, $processedText); - } - - /** - * Test testSpace2nbsp. - */ - public function testSpace2nbsp() - { - $text = ' Are you thrilled by flags ?' . PHP_EOL . ' Really?'; - $expectedText = '  Are you   thrilled  by flags   ?' . PHP_EOL . ' Really?'; - $processedText = space2nbsp($text); - $this->assertEquals($expectedText, $processedText); - } - - /** - * Test hashtags auto-link. - */ - public function testHashtagAutolink() - { - $index = 'http://domain.tld/'; - $rawDescription = '#hashtag\n - # nothashtag\n - test#nothashtag #hashtag \#nothashtag\n - test #hashtag #hashtag test #hashtag.test\n - #hashtag #hashtag-nothashtag #hashtag_hashtag\n - What is #ашок anyway?\n - カタカナ #カタカナ」カタカナ\n'; - $autolinkedDescription = hashtag_autolink($rawDescription, $index); - - $this->assertContainsPolyfill($this->getHashtagLink('hashtag', $index), $autolinkedDescription); - $this->assertNotContainsPolyfill(' #hashtag', $autolinkedDescription); - $this->assertNotContainsPolyfill('>#nothashtag', $autolinkedDescription); - $this->assertContainsPolyfill($this->getHashtagLink('ашок', $index), $autolinkedDescription); - $this->assertContainsPolyfill($this->getHashtagLink('カタカナ', $index), $autolinkedDescription); - $this->assertContainsPolyfill($this->getHashtagLink('hashtag_hashtag', $index), $autolinkedDescription); - $this->assertNotContainsPolyfill($this->getHashtagLink('hashtag-nothashtag', $index), $autolinkedDescription); - } - - /** - * Test hashtags auto-link without index URL. - */ - public function testHashtagAutolinkNoIndex() - { - $rawDescription = 'blabla #hashtag x#nothashtag'; - $autolinkedDescription = hashtag_autolink($rawDescription); - - $this->assertContainsPolyfill($this->getHashtagLink('hashtag'), $autolinkedDescription); - $this->assertNotContainsPolyfill(' #hashtag', $autolinkedDescription); - $this->assertNotContainsPolyfill('>#nothashtag', $autolinkedDescription); - } - - /** - * Test is_note with note URLs. - */ - public function testIsNote() - { - $this->assertTrue(is_note('?')); - $this->assertTrue(is_note('?abcDEf')); - $this->assertTrue(is_note('?_abcDEf#123')); - } - - /** - * Test is_note with non note URLs. - */ - public function testIsNotNote() - { - $this->assertFalse(is_note('')); - $this->assertFalse(is_note('nope')); - $this->assertFalse(is_note('https://github.com/shaarli/Shaarli/?hi')); - } - - /** - * Test tags_str2array with whitespace separator. - */ - public function testTagsStr2ArrayWithSpaceSeparator(): void - { - $separator = ' '; - - static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1 tag2 tag3', $separator)); - static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1 tag2 tag3', $separator)); - static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array(' tag1 tag2 tag3 ', $separator)); - static::assertSame(['tag1@', 'tag2,', '.tag3'], tags_str2array(' tag1@ tag2, .tag3 ', $separator)); - static::assertSame([], tags_str2array('', $separator)); - static::assertSame([], tags_str2array(' ', $separator)); - static::assertSame([], tags_str2array(null, $separator)); - } - - /** - * Test tags_str2array with @ separator. - */ - public function testTagsStr2ArrayWithCharSeparator(): void - { - $separator = '@'; - - static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1@tag2@tag3', $separator)); - static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1@@@@tag2@@@@tag3', $separator)); - static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('@@@tag1@@@tag2@@@@tag3@@', $separator)); - static::assertSame( - ['tag1#', 'tag2, and other', '.tag3'], - tags_str2array('@@@ tag1# @@@ tag2, and other @@@@.tag3@@', $separator) - ); - static::assertSame([], tags_str2array('', $separator)); - static::assertSame([], tags_str2array(' ', $separator)); - static::assertSame([], tags_str2array(null, $separator)); - } - - /** - * Test tags_str2array with / separator. - */ - public function testTagsStr2ArrayWithRegexDelimiterSeparator(): void - { - $separator = '/'; - - static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1/tag2/tag3', $separator)); - static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1////tag2////tag3', $separator)); - static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('///tag1///tag2////tag3//', $separator)); - static::assertSame( - ['tag1#', 'tag2, and other', '.tag3'], - tags_str2array('/// tag1# /// tag2, and other ////.tag3//', $separator) - ); - static::assertSame([], tags_str2array('', $separator)); - static::assertSame([], tags_str2array(' ', $separator)); - static::assertSame([], tags_str2array(null, $separator)); - } - - /** - * Test tags_array2str with ' ' separator. - */ - public function testTagsArray2StrWithSpaceSeparator(): void - { - $separator = ' '; - - static::assertSame('tag1 tag2 tag3', tags_array2str(['tag1', 'tag2', 'tag3'], $separator)); - static::assertSame('tag1, tag2@ tag3', tags_array2str(['tag1,', 'tag2@', 'tag3'], $separator)); - static::assertSame('tag1 tag2 tag3', tags_array2str([' tag1 ', 'tag2', 'tag3 '], $separator)); - static::assertSame('tag1 tag2 tag3', tags_array2str([' tag1 ', ' ', 'tag2', ' ', 'tag3 '], $separator)); - static::assertSame('tag1', tags_array2str([' tag1 '], $separator)); - static::assertSame('', tags_array2str([' '], $separator)); - static::assertSame('', tags_array2str([], $separator)); - static::assertSame('', tags_array2str(null, $separator)); - } - - /** - * Test tags_array2str with @ separator. - */ - public function testTagsArray2StrWithCharSeparator(): void - { - $separator = '@'; - - static::assertSame('tag1@tag2@tag3', tags_array2str(['tag1', 'tag2', 'tag3'], $separator)); - static::assertSame('tag1,@tag2@tag3', tags_array2str(['tag1,', 'tag2@', 'tag3'], $separator)); - static::assertSame( - 'tag1@tag2, and other@tag3', - tags_array2str(['@@@@ tag1@@@', ' @tag2, and other @', 'tag3@@@@'], $separator) - ); - static::assertSame('tag1@tag2@tag3', tags_array2str(['@@@tag1@@@', '@', 'tag2', '@@@', 'tag3@@@'], $separator)); - static::assertSame('tag1', tags_array2str(['@@@@tag1@@@@'], $separator)); - static::assertSame('', tags_array2str(['@@@'], $separator)); - static::assertSame('', tags_array2str([], $separator)); - static::assertSame('', tags_array2str(null, $separator)); - } - - /** - * Test tags_array2str with @ separator. - */ - public function testTagsFilterWithSpaceSeparator(): void - { - $separator = ' '; - - static::assertSame(['tag1', 'tag2', 'tag3'], tags_filter(['tag1', 'tag2', 'tag3'], $separator)); - static::assertSame(['tag1,', 'tag2@', 'tag3'], tags_filter(['tag1,', 'tag2@', 'tag3'], $separator)); - static::assertSame(['tag1', 'tag2', 'tag3'], tags_filter([' tag1 ', 'tag2', 'tag3 '], $separator)); - static::assertSame( - ['tag1', 'tag2', 'tag3'], - tags_filter([' tag1 ', ' ', 'tag2', ' ', 'tag3 '], $separator) - ); - static::assertSame(['tag1'], tags_filter([' tag1 '], $separator)); - static::assertSame([], tags_filter([' '], $separator)); - static::assertSame([], tags_filter([], $separator)); - static::assertSame([], tags_filter(null, $separator)); - } - - /** - * Test tags_array2str with @ separator. - */ - public function testTagsArrayFilterWithSpaceSeparator(): void - { - $separator = '@'; - - static::assertSame(['tag1', 'tag2', 'tag3'], tags_filter(['tag1', 'tag2', 'tag3'], $separator)); - static::assertSame(['tag1,', 'tag2#', 'tag3'], tags_filter(['tag1,', 'tag2#', 'tag3'], $separator)); - static::assertSame( - ['tag1', 'tag2, and other', 'tag3'], - tags_filter(['@@@@ tag1@@@', ' @tag2, and other @', 'tag3@@@@'], $separator) - ); - static::assertSame( - ['tag1', 'tag2', 'tag3'], - tags_filter(['@@@tag1@@@', '@', 'tag2', '@@@', 'tag3@@@'], $separator) - ); - static::assertSame(['tag1'], tags_filter(['@@@@tag1@@@@'], $separator)); - static::assertSame([], tags_filter(['@@@'], $separator)); - static::assertSame([], tags_filter([], $separator)); - static::assertSame([], tags_filter(null, $separator)); - } - - /** - * Util function to build an hashtag link. - * - * @param string $hashtag Hashtag name. - * @param string $index Index URL. - * - * @return string HTML hashtag link. - */ - private function getHashtagLink($hashtag, $index = '') - { - $hashtagLink = '#$1'; - return str_replace('$1', $hashtag, $hashtagLink); - } -} diff --git a/tests/bookmark/SearchResultTest.php b/tests/bookmark/SearchResultTest.php deleted file mode 100644 index 12854c1f..00000000 --- a/tests/bookmark/SearchResultTest.php +++ /dev/null @@ -1,125 +0,0 @@ -getBookmarks()); - static::assertSame(6, $searchResult->getResultCount()); - static::assertSame(6, $searchResult->getTotalCount()); - static::assertSame(null, $searchResult->getLimit()); - static::assertSame(0, $searchResult->getOffset()); - static::assertSame(1, $searchResult->getPage()); - static::assertSame(1, $searchResult->getLastPage()); - static::assertTrue($searchResult->isFirstPage()); - static::assertTrue($searchResult->isLastPage()); - } - - /** Create a SearchResult with only an offset parameter */ - public function testResultWithOffset(): void - { - $searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 2); - - static::assertSame([2 => 'c', 3 => 'd', 4 => 'e', 5 => 'f'], $searchResult->getBookmarks()); - static::assertSame(4, $searchResult->getResultCount()); - static::assertSame(6, $searchResult->getTotalCount()); - static::assertSame(null, $searchResult->getLimit()); - static::assertSame(2, $searchResult->getOffset()); - static::assertSame(2, $searchResult->getPage()); - static::assertSame(2, $searchResult->getLastPage()); - static::assertFalse($searchResult->isFirstPage()); - static::assertTrue($searchResult->isLastPage()); - } - - /** Create a SearchResult with only a limit parameter */ - public function testResultWithLimit(): void - { - $searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 0, 2); - - static::assertSame([0 => 'a', 1 => 'b'], $searchResult->getBookmarks()); - static::assertSame(2, $searchResult->getResultCount()); - static::assertSame(6, $searchResult->getTotalCount()); - static::assertSame(2, $searchResult->getLimit()); - static::assertSame(0, $searchResult->getOffset()); - static::assertSame(1, $searchResult->getPage()); - static::assertSame(3, $searchResult->getLastPage()); - static::assertTrue($searchResult->isFirstPage()); - static::assertFalse($searchResult->isLastPage()); - } - - /** Create a SearchResult with offset and limit parameters */ - public function testResultWithLimitAndOffset(): void - { - $searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 2, 2); - - static::assertSame([2 => 'c', 3 => 'd'], $searchResult->getBookmarks()); - static::assertSame(2, $searchResult->getResultCount()); - static::assertSame(6, $searchResult->getTotalCount()); - static::assertSame(2, $searchResult->getLimit()); - static::assertSame(2, $searchResult->getOffset()); - static::assertSame(2, $searchResult->getPage()); - static::assertSame(3, $searchResult->getLastPage()); - static::assertFalse($searchResult->isFirstPage()); - static::assertFalse($searchResult->isLastPage()); - } - - /** Create a SearchResult with offset and limit parameters displaying the last page */ - public function testResultWithLimitAndOffsetLastPage(): void - { - $searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 4, 2); - - static::assertSame([4 => 'e', 5 => 'f'], $searchResult->getBookmarks()); - static::assertSame(2, $searchResult->getResultCount()); - static::assertSame(6, $searchResult->getTotalCount()); - static::assertSame(2, $searchResult->getLimit()); - static::assertSame(4, $searchResult->getOffset()); - static::assertSame(3, $searchResult->getPage()); - static::assertSame(3, $searchResult->getLastPage()); - static::assertFalse($searchResult->isFirstPage()); - static::assertTrue($searchResult->isLastPage()); - } - - /** Create a SearchResult with offset and limit parameters out of bound (display the last page) */ - public function testResultWithLimitAndOffsetOutOfBounds(): void - { - $searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 12, 2); - - static::assertSame([4 => 'e', 5 => 'f'], $searchResult->getBookmarks()); - static::assertSame(2, $searchResult->getResultCount()); - static::assertSame(6, $searchResult->getTotalCount()); - static::assertSame(2, $searchResult->getLimit()); - static::assertSame(-2, $searchResult->getOffset()); - static::assertSame(3, $searchResult->getPage()); - static::assertSame(3, $searchResult->getLastPage()); - static::assertFalse($searchResult->isFirstPage()); - static::assertTrue($searchResult->isLastPage()); - } - - /** Create a SearchResult with offset and limit parameters out of bound (no result) */ - public function testResultWithLimitAndOffsetOutOfBoundsNoResult(): void - { - $searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 12, 2, true); - - static::assertSame([], $searchResult->getBookmarks()); - static::assertSame(0, $searchResult->getResultCount()); - static::assertSame(6, $searchResult->getTotalCount()); - static::assertSame(2, $searchResult->getLimit()); - static::assertSame(12, $searchResult->getOffset()); - static::assertSame(7, $searchResult->getPage()); - static::assertSame(3, $searchResult->getLastPage()); - static::assertFalse($searchResult->isFirstPage()); - static::assertFalse($searchResult->isLastPage()); - } -} diff --git a/tests/bootstrap.php b/tests/bootstrap.php deleted file mode 100644 index e0403296..00000000 --- a/tests/bootstrap.php +++ /dev/null @@ -1,45 +0,0 @@ -configIO = new ConfigJson(); - } - - /** - * Read a simple existing config file. - */ - public function testRead() - { - $conf = $this->configIO->read('tests/utils/config/configJson.json.php'); - $this->assertEquals('root', $conf['credentials']['login']); - $this->assertEquals('lala', $conf['redirector']['url']); - $this->assertEquals('sandbox/datastore.php', $conf['resource']['datastore']); - $this->assertEquals('1', $conf['plugins']['WALLABAG_VERSION']); - } - - /** - * Read a non existent config file -> empty array. - */ - public function testReadNonExistent() - { - $this->assertEquals(array(), $this->configIO->read('nope')); - } - - /** - * Read a non existent config file -> empty array. - */ - public function testReadInvalidJson() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessageRegExp('/An error occurred while parsing JSON configuration file \\([\\w\\/\\.]+\\): error code #4/'); - - $this->configIO->read('tests/utils/config/configInvalid.json.php'); - } - - /** - * Write a new config file. - */ - public function testWriteNew() - { - $dataFile = 'tests/utils/config/configWrite.json.php'; - $data = array( - 'credentials' => array( - 'login' => 'root', - ), - 'resource' => array( - 'datastore' => 'data/datastore.php', - ), - 'redirector' => array( - 'url' => 'lala', - ), - 'plugins' => array( - 'WALLABAG_VERSION' => '1', - ) - ); - $this->configIO->write($dataFile, $data); - // PHP 5.3 doesn't support json pretty print. - if (defined('JSON_PRETTY_PRINT')) { - $expected = '{ - "credentials": { - "login": "root" - }, - "resource": { - "datastore": "data\/datastore.php" - }, - "redirector": { - "url": "lala" - }, - "plugins": { - "WALLABAG_VERSION": "1" - } -}'; - } else { - $expected = '{"credentials":{"login":"root"},"resource":{"datastore":"data\/datastore.php"},"redirector":{"url":"lala"},"plugins":{"WALLABAG_VERSION":"1"}}'; - } - $expected = ConfigJson::getPhpHeaders() . $expected . ConfigJson::getPhpSuffix(); - $this->assertEquals($expected, file_get_contents($dataFile)); - unlink($dataFile); - } - - /** - * Overwrite an existing setting. - */ - public function testOverwrite() - { - $source = 'tests/utils/config/configJson.json.php'; - $dest = 'tests/utils/config/configOverwrite.json.php'; - copy($source, $dest); - $conf = $this->configIO->read($dest); - $conf['redirector']['url'] = 'blabla'; - $this->configIO->write($dest, $conf); - $conf = $this->configIO->read($dest); - $this->assertEquals('blabla', $conf['redirector']['url']); - unlink($dest); - } - - /** - * Write to invalid path. - */ - public function testWriteInvalidBlank() - { - $this->expectException(\Shaarli\Exceptions\IOException::class); - - $conf = array('conf' => 'value'); - @$this->configIO->write('', $conf); - } -} diff --git a/tests/config/ConfigManagerTest.php b/tests/config/ConfigManagerTest.php deleted file mode 100644 index 51e688cb..00000000 --- a/tests/config/ConfigManagerTest.php +++ /dev/null @@ -1,197 +0,0 @@ -conf = new ConfigManager('tests/utils/config/configJson'); - } - - /** - * Simple config test: - * 1. Set settings. - * 2. Check settings value. - */ - public function testSetGet() - { - $this->conf->set('paramInt', 42); - $this->conf->set('paramString', 'value1'); - $this->conf->set('paramBool', false); - $this->conf->set('paramArray', ['foo' => 'bar']); - $this->conf->set('paramNull', null); - - $this->assertEquals(42, $this->conf->get('paramInt')); - $this->assertEquals('value1', $this->conf->get('paramString')); - $this->assertFalse($this->conf->get('paramBool')); - $this->assertEquals(['foo' => 'bar'], $this->conf->get('paramArray')); - $this->assertEquals(null, $this->conf->get('paramNull')); - } - - /** - * Set/write/get config test: - * 1. Set settings. - * 2. Write it to the config file. - * 3. Read the file. - * 4. Check settings value. - */ - public function testSetWriteGet() - { - $this->conf->set('paramInt', 42); - $this->conf->set('paramString', 'value1'); - $this->conf->set('paramBool', false); - $this->conf->set('paramArray', ['foo' => 'bar']); - $this->conf->set('paramNull', null); - - $this->conf->setConfigFile('tests/utils/config/configTmp'); - $this->conf->write(true); - $this->conf->reload(); - unlink($this->conf->getConfigFileExt()); - - $this->assertEquals(42, $this->conf->get('paramInt')); - $this->assertEquals('value1', $this->conf->get('paramString')); - $this->assertFalse($this->conf->get('paramBool')); - $this->assertEquals(['foo' => 'bar'], $this->conf->get('paramArray')); - $this->assertEquals(null, $this->conf->get('paramNull')); - } - - /** - * Test set/write/get with nested keys. - */ - public function testSetWriteGetNested() - { - $this->conf->set('foo.bar.key.stuff', 'testSetWriteGetNested'); - - $this->conf->setConfigFile('tests/utils/config/configTmp'); - $this->conf->write(true); - $this->conf->reload(); - unlink($this->conf->getConfigFileExt()); - - $this->assertEquals('testSetWriteGetNested', $this->conf->get('foo.bar.key.stuff')); - } - - public function testSetDeleteNested() - { - $this->conf->set('foo.bar.key.stuff', 'testSetDeleteNested'); - $this->assertTrue($this->conf->exists('foo.bar')); - $this->assertTrue($this->conf->exists('foo.bar.key.stuff')); - $this->assertEquals('testSetDeleteNested', $this->conf->get('foo.bar.key.stuff')); - - $this->conf->remove('foo.bar'); - $this->assertFalse($this->conf->exists('foo.bar.key.stuff')); - $this->assertFalse($this->conf->exists('foo.bar')); - } - - /** - * Set with an empty key. - */ - public function testSetEmptyKey() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessageRegExp('#^Invalid setting key parameter. String expected, got.*#'); - - $this->conf->set('', 'stuff'); - } - - /** - * Set with an array key. - */ - public function testSetArrayKey() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessageRegExp('#^Invalid setting key parameter. String expected, got.*#'); - - $this->conf->set(['foo' => 'bar'], 'stuff'); - } - - /** - * Remove with an empty key. - */ - public function testRmoveEmptyKey() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessageRegExp('#^Invalid setting key parameter. String expected, got.*#'); - - $this->conf->remove(''); - } - - /** - * Try to write the config without mandatory parameter (e.g. 'login'). - */ - public function testWriteMissingParameter() - { - $this->expectException(\Shaarli\Config\Exception\MissingFieldConfigException::class); - - $this->conf->setConfigFile('tests/utils/config/configTmp'); - $this->assertFalse(file_exists($this->conf->getConfigFileExt())); - $this->conf->reload(); - - $this->conf->write(true); - } - - /** - * Try to get non existent config keys. - */ - public function testGetNonExistent() - { - $this->assertEquals('', $this->conf->get('nope.test')); - $this->assertEquals('default', $this->conf->get('nope.test', 'default')); - } - - /** - * Test the 'exists' method with existent values. - */ - public function testExistsOk() - { - $this->assertTrue($this->conf->exists('credentials.login')); - $this->assertTrue($this->conf->exists('config.foo')); - } - - /** - * Test the 'exists' method with non existent or invalid values. - */ - public function testExistsKo() - { - $this->assertFalse($this->conf->exists('nope')); - $this->assertFalse($this->conf->exists('nope.nope')); - $this->assertFalse($this->conf->exists('')); - $this->assertFalse($this->conf->exists(false)); - } - - /** - * Reset the ConfigManager instance. - */ - public function testReset() - { - $confIO = $this->conf->getConfigIO(); - $this->conf->reset(); - $this->assertFalse($confIO === $this->conf->getConfigIO()); - } - - /** - * Reload the config from file. - */ - public function testReload() - { - $this->conf->setConfigFile('tests/utils/config/configTmp'); - $newConf = ConfigJson::getPhpHeaders() . '{ "key": "value" }'; - file_put_contents($this->conf->getConfigFileExt(), $newConf); - $this->conf->reload(); - unlink($this->conf->getConfigFileExt()); - // Previous conf no longer exists, and new values have been loaded. - $this->assertFalse($this->conf->exists('credentials.login')); - $this->assertEquals('value', $this->conf->get('key')); - } -} diff --git a/tests/config/ConfigPhpTest.php b/tests/config/ConfigPhpTest.php deleted file mode 100644 index 7489fdea..00000000 --- a/tests/config/ConfigPhpTest.php +++ /dev/null @@ -1,100 +0,0 @@ -configIO = new ConfigPhp(); - } - - /** - * Read a simple existing config file. - */ - public function testRead() - { - $conf = $this->configIO->read('tests/utils/config/configPhp.php'); - $this->assertEquals('root', $conf['login']); - $this->assertEquals('lala', $conf['redirector']); - $this->assertEquals('data/datastore.php', $conf['config']['DATASTORE']); - $this->assertEquals('1', $conf['plugins']['WALLABAG_VERSION']); - } - - /** - * Read a non existent config file -> empty array. - */ - public function testReadNonExistent() - { - $this->assertEquals([], $this->configIO->read('nope')); - } - - /** - * Read an empty existent config file -> array with blank default values. - */ - public function testReadEmpty() - { - $dataFile = 'tests/utils/config/emptyConfigPhp.php'; - $conf = $this->configIO->read($dataFile); - $this->assertEmpty($conf['login']); - $this->assertEmpty($conf['title']); - $this->assertEmpty($conf['titleLink']); - $this->assertEmpty($conf['config']); - $this->assertEmpty($conf['plugins']); - } - - /** - * Write a new config file. - */ - public function testWriteNew() - { - $dataFile = 'tests/utils/config/configWrite.php'; - $data = [ - 'login' => 'root', - 'redirector' => 'lala', - 'config' => [ - 'DATASTORE' => 'data/datastore.php', - ], - 'plugins' => [ - 'WALLABAG_VERSION' => '1', - ] - ]; - $this->configIO->write($dataFile, $data); - $expected = 'assertEquals($expected, file_get_contents($dataFile)); - unlink($dataFile); - } - - /** - * Overwrite an existing setting. - */ - public function testOverwrite() - { - $source = 'tests/utils/config/configPhp.php'; - $dest = 'tests/utils/config/configOverwrite.php'; - copy($source, $dest); - $conf = $this->configIO->read($dest); - $conf['redirector'] = 'blabla'; - $this->configIO->write($dest, $conf); - $conf = $this->configIO->read($dest); - $this->assertEquals('blabla', $conf['redirector']); - unlink($dest); - } -} diff --git a/tests/config/ConfigPluginTest.php b/tests/config/ConfigPluginTest.php deleted file mode 100644 index 850b3546..00000000 --- a/tests/config/ConfigPluginTest.php +++ /dev/null @@ -1,133 +0,0 @@ - 2, // no plugin related - 'plugin2' => 0, // new - at the end - 'plugin3' => 0, // 2nd - 'order_plugin3' => 8, - 'plugin4' => 0, // 1st - 'order_plugin4' => 5, - ]; - - $expected = [ - 'plugin3', - 'plugin4', - 'plugin2', - ]; - - mkdir($path = __DIR__ . '/folder'); - PluginManager::$PLUGINS_PATH = $path; - array_map(function (string $plugin) use ($path) { - touch($path . '/' . $plugin); - }, $expected); - - $out = save_plugin_config($data); - $this->assertEquals($expected, $out); - - array_map(function (string $plugin) use ($path) { - unlink($path . '/' . $plugin); - }, $expected); - rmdir($path); - } - - /** - * Test save_plugin_config with invalid data. - */ - public function testSavePluginConfigInvalid() - { - $this->expectException(PluginConfigOrderException::class); - - $data = [ - 'plugin2' => 0, - 'plugin3' => 0, - 'order_plugin3' => 0, - 'plugin4' => 0, - 'order_plugin4' => 0, - ]; - - save_plugin_config($data); - } - - /** - * Test save_plugin_config without data. - */ - public function testSavePluginConfigEmpty() - { - $this->assertEquals([], save_plugin_config([])); - } - - /** - * Test validate_plugin_order with valid data. - */ - public function testValidatePluginOrderValid() - { - $data = [ - 'order_plugin1' => 2, - 'plugin2' => 0, - 'plugin3' => 0, - 'order_plugin3' => 1, - 'plugin4' => 0, - 'order_plugin4' => 5, - ]; - - $this->assertTrue(validate_plugin_order($data)); - } - - /** - * Test validate_plugin_order with invalid data. - */ - public function testValidatePluginOrderInvalid() - { - $data = [ - 'order_plugin1' => 2, - 'order_plugin3' => 1, - 'order_plugin4' => 1, - ]; - - $this->assertFalse(validate_plugin_order($data)); - } - - /** - * Test load_plugin_parameter_values. - */ - public function testLoadPluginParameterValues() - { - $plugins = [ - 'plugin_name' => [ - 'parameters' => [ - 'param1' => ['value' => true], - 'param2' => ['value' => false], - 'param3' => ['value' => ''], - ] - ] - ]; - - $parameters = [ - 'param1' => 'value1', - 'param2' => 'value2', - ]; - - $result = load_plugin_parameter_values($plugins, $parameters); - $this->assertEquals('value1', $result['plugin_name']['parameters']['param1']['value']); - $this->assertEquals('value2', $result['plugin_name']['parameters']['param2']['value']); - $this->assertEquals('', $result['plugin_name']['parameters']['param3']['value']); - } -} diff --git a/tests/container/ContainerBuilderTest.php b/tests/container/ContainerBuilderTest.php deleted file mode 100644 index 04d4ef01..00000000 --- a/tests/container/ContainerBuilderTest.php +++ /dev/null @@ -1,98 +0,0 @@ -conf = new ConfigManager('tests/utils/config/configJson'); - $this->sessionManager = $this->createMock(SessionManager::class); - $this->cookieManager = $this->createMock(CookieManager::class); - $this->pluginManager = $this->createMock(PluginManager::class); - - $this->loginManager = $this->createMock(LoginManager::class); - $this->loginManager->method('isLoggedIn')->willReturn(true); - - $this->containerBuilder = new ContainerBuilder( - $this->conf, - $this->sessionManager, - $this->cookieManager, - $this->loginManager, - $this->pluginManager, - $this->createMock(LoggerInterface::class) - ); - } - - public function testBuildContainer(): void - { - $container = $this->containerBuilder->build(); - - static::assertInstanceOf(BookmarkServiceInterface::class, $container->bookmarkService); - static::assertInstanceOf(CookieManager::class, $container->cookieManager); - static::assertInstanceOf(ConfigManager::class, $container->conf); - static::assertInstanceOf(ErrorController::class, $container->errorHandler); - static::assertInstanceOf(Environment::class, $container->environment); - static::assertInstanceOf(FeedBuilder::class, $container->feedBuilder); - static::assertInstanceOf(FormatterFactory::class, $container->formatterFactory); - static::assertInstanceOf(History::class, $container->history); - static::assertInstanceOf(HttpAccess::class, $container->httpAccess); - static::assertInstanceOf(LoginManager::class, $container->loginManager); - static::assertInstanceOf(LoggerInterface::class, $container->logger); - static::assertInstanceOf(MetadataRetriever::class, $container->metadataRetriever); - static::assertInstanceOf(NetscapeBookmarkUtils::class, $container->netscapeBookmarkUtils); - static::assertInstanceOf(PageBuilder::class, $container->pageBuilder); - static::assertInstanceOf(PageCacheManager::class, $container->pageCacheManager); - static::assertInstanceOf(ErrorController::class, $container->phpErrorHandler); - static::assertInstanceOf(ErrorNotFoundController::class, $container->notFoundHandler); - static::assertInstanceOf(PluginManager::class, $container->pluginManager); - static::assertInstanceOf(SessionManager::class, $container->sessionManager); - static::assertInstanceOf(Thumbnailer::class, $container->thumbnailer); - static::assertInstanceOf(Updater::class, $container->updater); - - // Set by the middleware - static::assertNull($container->basePath); - } -} diff --git a/tests/container/ShaarliTestContainer.php b/tests/container/ShaarliTestContainer.php deleted file mode 100644 index 60d339a0..00000000 --- a/tests/container/ShaarliTestContainer.php +++ /dev/null @@ -1,41 +0,0 @@ -addToAssertionCount(1); - } - - /** - * Cache a page's content - */ - public function testCache() - { - $page = new CachedPage(self::$testCacheDir, self::$url, true, null); - - $this->assertFileNotExists(self::$filename); - $page->cache('

Some content

'); - $this->assertFileExists(self::$filename); - $this->assertEquals( - '

Some content

', - file_get_contents(self::$filename) - ); - } - - /** - * "Cache" a page's content - the page is not to be cached - */ - public function testShouldNotCache() - { - $page = new CachedPage(self::$testCacheDir, self::$url, false, null); - - $this->assertFileNotExists(self::$filename); - $page->cache('

Some content

'); - $this->assertFileNotExists(self::$filename); - } - - /** - * Return a page's cached content - */ - public function testCachedVersion() - { - $page = new CachedPage(self::$testCacheDir, self::$url, true, null); - - $this->assertFileNotExists(self::$filename); - $page->cache('

Some content

'); - $this->assertFileExists(self::$filename); - $this->assertEquals( - '

Some content

', - $page->cachedVersion() - ); - } - - /** - * Return a page's cached content - the file does not exist - */ - public function testCachedVersionNoFile() - { - $page = new CachedPage(self::$testCacheDir, self::$url, true, null); - - $this->assertFileNotExists(self::$filename); - $this->assertEquals( - null, - $page->cachedVersion() - ); - } - - /** - * Return a page's cached content - the page is not to be cached - */ - public function testNoCachedVersion() - { - $page = new CachedPage(self::$testCacheDir, self::$url, false, null); - - $this->assertFileNotExists(self::$filename); - $this->assertEquals( - null, - $page->cachedVersion() - ); - } - - /** - * Return a page's cached content within date period - */ - public function testCachedVersionInDatePeriod() - { - $period = new \DatePeriod( - new \DateTime('yesterday'), - new \DateInterval('P1D'), - new \DateTime('tomorrow') - ); - $page = new CachedPage(self::$testCacheDir, self::$url, true, $period); - - $this->assertFileNotExists(self::$filename); - $page->cache('

Some content

'); - $this->assertFileExists(self::$filename); - $this->assertEquals( - '

Some content

', - $page->cachedVersion() - ); - } - - /** - * Return a page's cached content outside of date period - */ - public function testCachedVersionNotInDatePeriod() - { - $period = new \DatePeriod( - new \DateTime('yesterday noon'), - new \DateInterval('P1D'), - new \DateTime('yesterday midnight') - ); - $page = new CachedPage(self::$testCacheDir, self::$url, true, $period); - - $this->assertFileNotExists(self::$filename); - $page->cache('

Some content

'); - $this->assertFileExists(self::$filename); - $this->assertNull($page->cachedVersion()); - } -} diff --git a/tests/feed/FeedBuilderTest.php b/tests/feed/FeedBuilderTest.php deleted file mode 100644 index bd6a9cd8..00000000 --- a/tests/feed/FeedBuilderTest.php +++ /dev/null @@ -1,304 +0,0 @@ -set('resource.datastore', self::$testDatastore); - $refLinkDB = new ReferenceLinkDB(); - $refLinkDB->write(self::$testDatastore); - $history = new History('sandbox/history.php'); - $factory = new FormatterFactory($conf, true); - $pluginManager = new PluginManager($conf); - self::$formatter = $factory->getFormatter(); - self::$bookmarkService = new BookmarkFileService( - $conf, - $pluginManager, - $history, - $mutex, - true - ); - - self::$serverInfo = [ - 'HTTPS' => 'Off', - 'SERVER_NAME' => 'host.tld', - 'SERVER_PORT' => '80', - 'SCRIPT_NAME' => '/index.php', - 'REQUEST_URI' => '/feed/atom', - ]; - } - - /** - * Test buildData with RSS feed. - */ - public function testRSSBuildData() - { - $feedBuilder = new FeedBuilder( - self::$bookmarkService, - self::$formatter, - static::$serverInfo, - false - ); - $feedBuilder->setLocale(self::$LOCALE); - $data = $feedBuilder->buildData(FeedBuilder::$FEED_RSS, null); - // Test headers (RSS) - $this->assertEquals(self::$RSS_LANGUAGE, $data['language']); - $this->assertRegExp('/Wed, 03 Aug 2016 09:30:33 \+\d{4}/', $data['last_update']); - $this->assertEquals(true, $data['show_dates']); - $this->assertEquals('http://host.tld/feed/atom', $data['self_link']); - $this->assertEquals('http://host.tld/', $data['index_url']); - $this->assertFalse($data['usepermalinks']); - $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); - - // Test first not pinned link (note link) - $link = $data['links'][array_keys($data['links'])[0]]; - $this->assertEquals(41, $link['id']); - $this->assertEquals( - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), - $link['created'] - ); - $this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']); - $this->assertEquals('http://host.tld/shaare/WDWyig', $link['url']); - $this->assertRegExp('/Tue, 10 Mar 2015 11:46:51 \+\d{4}/', $link['pub_iso_date']); - $pub = DateTime::createFromFormat(DateTime::RSS, $link['pub_iso_date']); - $up = DateTime::createFromFormat(DateTime::ATOM, $link['up_iso_date']); - $this->assertEquals($pub, $up); - $this->assertContainsPolyfill('Stallman has a beard', $link['description']); - $this->assertContainsPolyfill('Permalink', $link['description']); - $this->assertContainsPolyfill('http://host.tld/shaare/WDWyig', $link['description']); - $this->assertEquals(1, count($link['taglist'])); - $this->assertEquals('sTuff', $link['taglist'][0]); - - // Test URL with external link. - $this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $data['links'][8]['url']); - - // Test multitags. - $this->assertEquals(5, count($data['links'][6]['taglist'])); - $this->assertEquals('css', $data['links'][6]['taglist'][0]); - - // Test update date - $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']); - } - - /** - * Test buildData with ATOM feed (test only specific to ATOM). - */ - public function testAtomBuildData() - { - $feedBuilder = new FeedBuilder( - self::$bookmarkService, - self::$formatter, - static::$serverInfo, - false - ); - $feedBuilder->setLocale(self::$LOCALE); - $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null); - $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); - $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']); - $link = $data['links'][array_keys($data['links'])[0]]; - $this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']); - $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']); - } - - /** - * Test buildData with search criteria. - */ - public function testBuildDataFiltered() - { - $criteria = [ - 'searchtags' => 'stuff', - 'searchterm' => 'beard', - ]; - $feedBuilder = new FeedBuilder( - self::$bookmarkService, - self::$formatter, - static::$serverInfo, - false - ); - $feedBuilder->setLocale(self::$LOCALE); - $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, $criteria); - $this->assertEquals(1, count($data['links'])); - $link = array_shift($data['links']); - $this->assertEquals(41, $link['id']); - $this->assertEquals( - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), - $link['created'] - ); - } - - /** - * Test buildData with nb limit. - */ - public function testBuildDataCount() - { - $criteria = [ - 'nb' => '3', - ]; - $feedBuilder = new FeedBuilder( - self::$bookmarkService, - self::$formatter, - static::$serverInfo, - false - ); - $feedBuilder->setLocale(self::$LOCALE); - $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, $criteria); - $this->assertEquals(3, count($data['links'])); - $link = $data['links'][array_keys($data['links'])[0]]; - $this->assertEquals(41, $link['id']); - $this->assertEquals( - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), - $link['created'] - ); - } - - /** - * Test buildData with permalinks on. - */ - public function testBuildDataPermalinks() - { - $feedBuilder = new FeedBuilder( - self::$bookmarkService, - self::$formatter, - static::$serverInfo, - false - ); - $feedBuilder->setLocale(self::$LOCALE); - $feedBuilder->setUsePermalinks(true); - $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null); - $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); - $this->assertTrue($data['usepermalinks']); - // First link is a permalink - $link = $data['links'][array_keys($data['links'])[0]]; - $this->assertEquals(41, $link['id']); - $this->assertEquals( - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), - $link['created'] - ); - $this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']); - $this->assertEquals('http://host.tld/shaare/WDWyig', $link['url']); - $this->assertContainsPolyfill('Direct link', $link['description']); - $this->assertContainsPolyfill('http://host.tld/shaare/WDWyig', $link['description']); - // Second link is a direct link - $link = $data['links'][array_keys($data['links'])[1]]; - $this->assertEquals(8, $link['id']); - $this->assertEquals( - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114633'), - $link['created'] - ); - $this->assertEquals('http://host.tld/shaare/RttfEw', $link['guid']); - $this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['url']); - $this->assertContainsPolyfill('Direct link', $link['description']); - $this->assertContainsPolyfill('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['description']); - } - - /** - * Test buildData with hide dates settings. - */ - public function testBuildDataHideDates() - { - $feedBuilder = new FeedBuilder( - self::$bookmarkService, - self::$formatter, - static::$serverInfo, - false - ); - $feedBuilder->setLocale(self::$LOCALE); - $feedBuilder->setHideDates(true); - $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null); - $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); - $this->assertFalse($data['show_dates']); - - // Show dates while logged in - $feedBuilder = new FeedBuilder( - self::$bookmarkService, - self::$formatter, - static::$serverInfo, - true - ); - $feedBuilder->setLocale(self::$LOCALE); - $feedBuilder->setHideDates(true); - $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null); - $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); - $this->assertTrue($data['show_dates']); - } - - /** - * Test buildData when Shaarli is served from a subdirectory - */ - public function testBuildDataServerSubdir() - { - $serverInfo = [ - 'HTTPS' => 'Off', - 'SERVER_NAME' => 'host.tld', - 'SERVER_PORT' => '8080', - 'SCRIPT_NAME' => '/~user/shaarli/index.php', - 'REQUEST_URI' => '/~user/shaarli/feed/atom', - ]; - $feedBuilder = new FeedBuilder( - self::$bookmarkService, - self::$formatter, - $serverInfo, - false - ); - $feedBuilder->setLocale(self::$LOCALE); - $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null); - - $this->assertEquals( - 'http://host.tld:8080/~user/shaarli/feed/atom', - $data['self_link'] - ); - - // Test first link (note link) - $link = $data['links'][array_keys($data['links'])[0]]; - $this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['guid']); - $this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['url']); - $this->assertContainsPolyfill('http://host.tld:8080/~user/shaarli/./add-tag/hashtag', $link['description']); - } -} diff --git a/tests/formatter/BookmarkDefaultFormatterTest.php b/tests/formatter/BookmarkDefaultFormatterTest.php deleted file mode 100644 index c0ba83f3..00000000 --- a/tests/formatter/BookmarkDefaultFormatterTest.php +++ /dev/null @@ -1,319 +0,0 @@ -conf = new ConfigManager(self::$testConf); - $this->formatter = new BookmarkDefaultFormatter($this->conf, true); - } - - /** - * Test formatting a bookmark with all its attribute filled. - */ - public function testFormatFull() - { - $bookmark = new Bookmark(); - $bookmark->setId($id = 11); - $bookmark->setShortUrl($short = 'abcdef'); - $bookmark->setUrl('https://sub.domain.tld?query=here&for=real#hash'); - $bookmark->setTitle($title = 'This is a bookmark'); - $bookmark->setDescription($desc = '

Content

`Here is some content

'); - $bookmark->setTags($tags = ['tag1', 'bookmark', 'other', '']); - $bookmark->setThumbnail('http://domain2.tdl2/?type=img&name=file.png'); - $bookmark->setSticky(true); - $bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190521_190412')); - $bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190521_191213')); - $bookmark->setPrivate(true); - - $link = $this->formatter->format($bookmark); - $this->assertEquals($id, $link['id']); - $this->assertEquals($short, $link['shorturl']); - $this->assertEquals('https://sub.domain.tld?query=here&for=real#hash', $link['url']); - $this->assertEquals( - 'https://sub.domain.tld?query=here&for=real#hash', - $link['real_url'] - ); - $this->assertEquals('This is a <strong>bookmark</strong>', $link['title']); - $this->assertEquals( - '<h2>Content</h2><p>`Here is some content</p>', - $link['description'] - ); - $tags[3] = '<script>alert("xss");</script>'; - $this->assertEquals($tags, $link['taglist']); - $this->assertEquals(implode(' ', $tags), $link['tags']); - $this->assertEquals( - 'http://domain2.tdl2/?type=img&name=file.png', - $link['thumbnail'] - ); - $this->assertEquals($created, $link['created']); - $this->assertEquals($created->getTimestamp(), $link['timestamp']); - $this->assertEquals($updated, $link['updated']); - $this->assertEquals($updated->getTimestamp(), $link['updated_timestamp']); - $this->assertTrue($link['private']); - $this->assertTrue($link['sticky']); - $this->assertEquals('private', $link['class']); - } - - /** - * Test formatting a bookmark with all its attribute filled. - */ - public function testFormatMinimal() - { - $bookmark = new Bookmark(); - - $link = $this->formatter->format($bookmark); - $this->assertEmpty($link['id']); - $this->assertEmpty($link['shorturl']); - $this->assertEmpty($link['url']); - $this->assertEmpty($link['real_url']); - $this->assertEmpty($link['title']); - $this->assertEmpty($link['description']); - $this->assertEmpty($link['taglist']); - $this->assertEmpty($link['tags']); - $this->assertEmpty($link['thumbnail']); - $this->assertEmpty($link['created']); - $this->assertEmpty($link['timestamp']); - $this->assertEmpty($link['updated']); - $this->assertEmpty($link['updated_timestamp']); - $this->assertFalse($link['private']); - $this->assertFalse($link['sticky']); - $this->assertEmpty($link['class']); - } - - /** - * Make sure that the description is properly formatted by the default formatter. - */ - public function testFormatDescription() - { - $description = []; - $description[] = 'This a description' . PHP_EOL; - $description[] = 'text https://sub.domain.tld?query=here&for=real#hash more text' . PHP_EOL; - $description[] = 'Also, there is an #hashtag added' . PHP_EOL; - $description[] = ' A N D KEEP SPACES ! ' . PHP_EOL; - - $bookmark = new Bookmark(); - $bookmark->setDescription(implode('', $description)); - $link = $this->formatter->format($bookmark); - - $description[0] = 'This a <strong>description</strong>
'; - $url = 'https://sub.domain.tld?query=here&for=real#hash'; - $description[1] = 'text ' . $url . ' more text
'; - $description[2] = 'Also, there is an #hashtag added
'; - $description[3] = '    A  N  D KEEP     ' . - 'SPACES    !  
'; - - $this->assertEquals(implode(PHP_EOL, $description) . PHP_EOL, $link['description']); - } - - /** - * Test formatting URL with an index_url set - * It should prepend relative links. - */ - public function testFormatNoteWithIndexUrl() - { - $bookmark = new Bookmark(); - $bookmark->setUrl($short = '?abcdef'); - $description = 'Text #hashtag more text'; - $bookmark->setDescription($description); - - $this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/'); - - $link = $this->formatter->format($bookmark); - $this->assertEquals($root . $short, $link['url']); - $this->assertEquals($root . $short, $link['real_url']); - $this->assertEquals( - 'Text ' . - '#hashtag more text', - $link['description'] - ); - } - - /** - * Make sure that private tags are properly filtered out when the user is logged out. - */ - public function testFormatTagListRemovePrivate(): void - { - $this->formatter = new BookmarkDefaultFormatter($this->conf, false); - - $bookmark = new Bookmark(); - $bookmark->setId($id = 11); - $bookmark->setTags($tags = ['bookmark', '.private', 'othertag']); - - $link = $this->formatter->format($bookmark); - - unset($tags[1]); - $tags = array_values($tags); - - $this->assertSame(11, $link['id']); - $this->assertSame($tags, $link['taglist']); - $this->assertSame(implode(' ', $tags), $link['tags']); - } - - /** - * Test formatTitleHtml with search result highlight. - */ - public function testFormatTitleHtmlWithSearchHighlight(): void - { - $this->formatter = new BookmarkDefaultFormatter($this->conf, false); - - $bookmark = new Bookmark(); - $bookmark->setTitle('PSR-2: Coding Style Guide'); - $bookmark->setAdditionalContentEntry( - 'search_highlight', - ['title' => [ - ['start' => 0, 'end' => 5], // "psr-2" - ['start' => 7, 'end' => 13], // coding - ['start' => 20, 'end' => 25], // guide - ]] - ); - - $link = $this->formatter->format($bookmark); - - $this->assertSame( - 'PSR-2: ' . - 'Coding Style ' . - 'Guide', - $link['title_html'] - ); - } - - /** - * Test formatDescription with search result highlight. - */ - public function testFormatDescriptionWithSearchHighlight(): void - { - $this->formatter = new BookmarkDefaultFormatter($this->conf, false); - - $bookmark = new Bookmark(); - $bookmark->setDescription( - 'This guide extends and expands on PSR-1, the basic coding standard.' . PHP_EOL . - 'https://www.php-fig.org/psr/psr-1/' - ); - $bookmark->setAdditionalContentEntry( - 'search_highlight', - ['description' => [ - ['start' => 0, 'end' => 10], // "This guide" - ['start' => 45, 'end' => 50], // basic - ['start' => 58, 'end' => 67], // standard. - ['start' => 84, 'end' => 87], // fig - ]] - ); - - $link = $this->formatter->format($bookmark); - - $this->assertSame( - 'This guide extends and expands on PSR-1, the ' . - 'basic coding ' . - 'standard.
' . PHP_EOL . - '' . - 'https://www.php-fig.org/psr/psr-1/' . - '', - $link['description'] - ); - } - - /** - * Test formatUrlHtml with search result highlight. - */ - public function testFormatUrlHtmlWithSearchHighlight(): void - { - $this->formatter = new BookmarkDefaultFormatter($this->conf, false); - - $bookmark = new Bookmark(); - $bookmark->setUrl('http://www.php-fig.org/psr/psr-2/'); - $bookmark->setAdditionalContentEntry( - 'search_highlight', - ['url' => [ - ['start' => 0, 'end' => 4], // http - ['start' => 15, 'end' => 18], // fig - ['start' => 27, 'end' => 33], // "psr-2/" - ]] - ); - - $link = $this->formatter->format($bookmark); - - $this->assertSame( - 'http://www.php-' . - 'fig.org/psr/' . - 'psr-2/', - $link['url_html'] - ); - } - - /** - * Test formatTagListHtml with search result highlight. - */ - public function testFormatTagListHtmlWithSearchHighlight(): void - { - $this->formatter = new BookmarkDefaultFormatter($this->conf, false); - - $bookmark = new Bookmark(); - $bookmark->setTagsString('coding-style standards quality assurance'); - $bookmark->setAdditionalContentEntry( - 'search_highlight', - ['tags' => [ - ['start' => 0, 'end' => 12], // coding-style - ['start' => 23, 'end' => 30], // quality - ['start' => 31, 'end' => 40], // assurance - ],] - ); - - $link = $this->formatter->format($bookmark); - - $this->assertSame( - [ - 'coding-style', - 'standards', - 'quality', - 'assurance', - ], - $link['taglist_html'] - ); - } - - /** - * Test default formatting with formatter_settings.autolink set to false: - * URLs and hashtags should not be transformed - */ - public function testFormatDescriptionWithoutLinkification(): void - { - $this->conf->set('formatter_settings.autolink', false); - $this->formatter = new BookmarkDefaultFormatter($this->conf, false); - - $bookmark = new Bookmark(); - $bookmark->setDescription('Hi!' . PHP_EOL . 'https://thisisaurl.tld #hashtag'); - - $link = $this->formatter->format($bookmark); - - static::assertSame( - 'Hi!
' . PHP_EOL . 'https://thisisaurl.tld  #hashtag', - $link['description'] - ); - } -} diff --git a/tests/formatter/BookmarkMarkdownExtraFormatterTest.php b/tests/formatter/BookmarkMarkdownExtraFormatterTest.php deleted file mode 100644 index c9c73a93..00000000 --- a/tests/formatter/BookmarkMarkdownExtraFormatterTest.php +++ /dev/null @@ -1,162 +0,0 @@ -conf = new ConfigManager(self::$testConf); - $this->formatter = new BookmarkMarkdownExtraFormatter($this->conf, true); - } - - /** - * Test formatting a bookmark with all its attribute filled. - */ - public function testFormatExtra(): void - { - $bookmark = new Bookmark(); - $bookmark->setId($id = 11); - $bookmark->setShortUrl($short = 'abcdef'); - $bookmark->setUrl('https://sub.domain.tld?query=here&for=real#hash'); - $bookmark->setTitle($title = 'This is a bookmark'); - $bookmark->setDescription('

Content

`Here is some content

'); - $bookmark->setTags($tags = ['tag1', 'bookmark', 'other', '']); - $bookmark->setThumbnail('http://domain2.tdl2/?type=img&name=file.png'); - $bookmark->setSticky(true); - $bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190521_190412')); - $bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190521_191213')); - $bookmark->setPrivate(true); - - $link = $this->formatter->format($bookmark); - $this->assertEquals($id, $link['id']); - $this->assertEquals($short, $link['shorturl']); - $this->assertEquals('https://sub.domain.tld?query=here&for=real#hash', $link['url']); - $this->assertEquals( - 'https://sub.domain.tld?query=here&for=real#hash', - $link['real_url'] - ); - $this->assertEquals('This is a <strong>bookmark</strong>', $link['title']); - $this->assertEquals( - '

' . - '<h2>Content</h2><p>`Here is some content</p>' . - '

', - $link['description'] - ); - $tags[3] = '<script>alert("xss");</script>'; - $this->assertEquals($tags, $link['taglist']); - $this->assertEquals(implode(' ', $tags), $link['tags']); - $this->assertEquals( - 'http://domain2.tdl2/?type=img&name=file.png', - $link['thumbnail'] - ); - $this->assertEquals($created, $link['created']); - $this->assertEquals($created->getTimestamp(), $link['timestamp']); - $this->assertEquals($updated, $link['updated']); - $this->assertEquals($updated->getTimestamp(), $link['updated_timestamp']); - $this->assertTrue($link['private']); - $this->assertTrue($link['sticky']); - $this->assertEquals('private', $link['class']); - } - - /** - * Test formatting a bookmark with all its attribute filled. - */ - public function testFormatExtraMinimal(): void - { - $bookmark = new Bookmark(); - - $link = $this->formatter->format($bookmark); - $this->assertEmpty($link['id']); - $this->assertEmpty($link['shorturl']); - $this->assertEmpty($link['url']); - $this->assertEmpty($link['real_url']); - $this->assertEmpty($link['title']); - $this->assertEmpty($link['description']); - $this->assertEmpty($link['taglist']); - $this->assertEmpty($link['tags']); - $this->assertEmpty($link['thumbnail']); - $this->assertEmpty($link['created']); - $this->assertEmpty($link['timestamp']); - $this->assertEmpty($link['updated']); - $this->assertEmpty($link['updated_timestamp']); - $this->assertFalse($link['private']); - $this->assertFalse($link['sticky']); - $this->assertEmpty($link['class']); - } - - /** - * Make sure that the description is properly formatted by the default formatter. - */ - public function testFormatExtrraDescription(): void - { - $description = 'This a description' . PHP_EOL; - $description .= 'text https://sub.domain.tld?query=here&for=real#hash more text' . PHP_EOL; - $description .= 'Also, there is an #hashtag added' . PHP_EOL; - $description .= ' A N D KEEP SPACES ! ' . PHP_EOL; - $description .= '# Header {.class}' . PHP_EOL; - - $bookmark = new Bookmark(); - $bookmark->setDescription($description); - $link = $this->formatter->format($bookmark); - - $description = '

'; - $description .= 'This a <strong>description</strong>
' . PHP_EOL; - $url = 'https://sub.domain.tld?query=here&for=real#hash'; - $description .= 'text ' . $url . ' more text
' . PHP_EOL; - $description .= 'Also, there is an #hashtag added
' . PHP_EOL; - $description .= 'A N D KEEP SPACES !

' . PHP_EOL; - $description .= '

Header

'; - $description .= '
'; - - $this->assertEquals($description, $link['description']); - } - - /** - * Test formatting URL with an index_url set - * It should prepend relative links. - */ - public function testFormatExtraNoteWithIndexUrl(): void - { - $bookmark = new Bookmark(); - $bookmark->setUrl($short = '?abcdef'); - $description = 'Text #hashtag more text'; - $bookmark->setDescription($description); - - $this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/'); - - $description = '

'; - $description .= 'Text #hashtag more text'; - $description .= '

'; - - $link = $this->formatter->format($bookmark); - $this->assertEquals($root . $short, $link['url']); - $this->assertEquals($root . $short, $link['real_url']); - $this->assertEquals( - $description, - $link['description'] - ); - } -} diff --git a/tests/formatter/BookmarkMarkdownFormatterTest.php b/tests/formatter/BookmarkMarkdownFormatterTest.php deleted file mode 100644 index cd471756..00000000 --- a/tests/formatter/BookmarkMarkdownFormatterTest.php +++ /dev/null @@ -1,203 +0,0 @@ -conf = new ConfigManager(self::$testConf); - $this->formatter = new BookmarkMarkdownFormatter($this->conf, true); - } - - /** - * Test formatting a bookmark with all its attribute filled. - */ - public function testFormatFull() - { - $bookmark = new Bookmark(); - $bookmark->setId($id = 11); - $bookmark->setShortUrl($short = 'abcdef'); - $bookmark->setUrl('https://sub.domain.tld?query=here&for=real#hash'); - $bookmark->setTitle($title = 'This is a bookmark'); - $bookmark->setDescription('

Content

`Here is some content

'); - $bookmark->setTags($tags = ['tag1', 'bookmark', 'other', '']); - $bookmark->setThumbnail('http://domain2.tdl2/?type=img&name=file.png'); - $bookmark->setSticky(true); - $bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190521_190412')); - $bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190521_191213')); - $bookmark->setPrivate(true); - - $link = $this->formatter->format($bookmark); - $this->assertEquals($id, $link['id']); - $this->assertEquals($short, $link['shorturl']); - $this->assertEquals('https://sub.domain.tld?query=here&for=real#hash', $link['url']); - $this->assertEquals( - 'https://sub.domain.tld?query=here&for=real#hash', - $link['real_url'] - ); - $this->assertEquals('This is a <strong>bookmark</strong>', $link['title']); - $this->assertEquals( - '

' . - '<h2>Content</h2><p>`Here is some content</p>' . - '

', - $link['description'] - ); - $tags[3] = '<script>alert("xss");</script>'; - $this->assertEquals($tags, $link['taglist']); - $this->assertEquals(implode(' ', $tags), $link['tags']); - $this->assertEquals( - 'http://domain2.tdl2/?type=img&name=file.png', - $link['thumbnail'] - ); - $this->assertEquals($created, $link['created']); - $this->assertEquals($created->getTimestamp(), $link['timestamp']); - $this->assertEquals($updated, $link['updated']); - $this->assertEquals($updated->getTimestamp(), $link['updated_timestamp']); - $this->assertTrue($link['private']); - $this->assertTrue($link['sticky']); - $this->assertEquals('private', $link['class']); - } - - /** - * Test formatting a bookmark with all its attribute filled. - */ - public function testFormatMinimal() - { - $bookmark = new Bookmark(); - - $link = $this->formatter->format($bookmark); - $this->assertEmpty($link['id']); - $this->assertEmpty($link['shorturl']); - $this->assertEmpty($link['url']); - $this->assertEmpty($link['real_url']); - $this->assertEmpty($link['title']); - $this->assertEmpty($link['description']); - $this->assertEmpty($link['taglist']); - $this->assertEmpty($link['tags']); - $this->assertEmpty($link['thumbnail']); - $this->assertEmpty($link['created']); - $this->assertEmpty($link['timestamp']); - $this->assertEmpty($link['updated']); - $this->assertEmpty($link['updated_timestamp']); - $this->assertFalse($link['private']); - $this->assertFalse($link['sticky']); - $this->assertEmpty($link['class']); - } - - /** - * Make sure that the description is properly formatted by the default formatter. - */ - public function testFormatDescription() - { - $description = 'This a description' . PHP_EOL; - $description .= 'text https://sub.domain.tld?query=here&for=real#hash more text' . PHP_EOL; - $description .= 'Also, there is an #hashtag added' . PHP_EOL; - $description .= ' A N D KEEP SPACES ! ' . PHP_EOL; - - $bookmark = new Bookmark(); - $bookmark->setDescription($description); - $link = $this->formatter->format($bookmark); - - $description = '

'; - $description .= 'This a <strong>description</strong>
' . PHP_EOL; - $url = 'https://sub.domain.tld?query=here&for=real#hash'; - $description .= 'text ' . $url . ' more text
' . PHP_EOL; - $description .= 'Also, there is an #hashtag added
' . PHP_EOL; - $description .= 'A N D KEEP SPACES ! '; - $description .= '

'; - - $this->assertEquals($description, $link['description']); - } - - /** - * Make sure that the description is properly formatted by the default formatter. - */ - public function testFormatDescriptionWithSearchHighlight() - { - $description = 'This a description' . PHP_EOL; - $description .= 'text https://sub.domain.tld?query=here&for=real#hash more text' . PHP_EOL; - $description .= 'Also, there is an #hashtag added' . PHP_EOL; - $description .= ' A N D KEEP SPACES ! ' . PHP_EOL; - $description .= 'And [yet another link](https://other.domain.tld)' . PHP_EOL; - - $bookmark = new Bookmark(); - $bookmark->setDescription($description); - $bookmark->setAdditionalContentEntry( - 'search_highlight', - ['description' => [ - ['start' => 18, 'end' => 26], // cription - ['start' => 49, 'end' => 52], // sub - ['start' => 84, 'end' => 88], // hash - ['start' => 118, 'end' => 123], // hasht - ['start' => 203, 'end' => 215], // other.domain - ]] - ); - - $link = $this->formatter->format($bookmark); - - $description = '

'; - $description .= 'This a <strong>description</strong>
' . - PHP_EOL; - $url = 'https://sub.domain.tld?query=here&for=real#hash'; - $highlighted = 'https://sub.domain.tld'; - $highlighted .= '?query=here&for=real#hash'; - $description .= 'text ' . $highlighted . ' more text
' . PHP_EOL; - $description .= 'Also, there is an #hasht' . - 'ag added
' . PHP_EOL; - $description .= 'A N D KEEP SPACES !
' . PHP_EOL; - $description .= 'And ' . - 'yet another link'; - $description .= '

'; - - $this->assertEquals($description, $link['description']); - } - - /** - * Test formatting URL with an index_url set - * It should prepend relative links. - */ - public function testFormatNoteWithIndexUrl() - { - $bookmark = new Bookmark(); - $bookmark->setUrl($short = '?abcdef'); - $description = 'Text #hashtag more text'; - $bookmark->setDescription($description); - - $this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/'); - - $description = '

'; - $description .= 'Text #hashtag more text'; - $description .= '

'; - - $link = $this->formatter->format($bookmark); - $this->assertEquals($root . $short, $link['url']); - $this->assertEquals($root . $short, $link['real_url']); - $this->assertEquals( - $description, - $link['description'] - ); - } -} diff --git a/tests/formatter/BookmarkRawFormatterTest.php b/tests/formatter/BookmarkRawFormatterTest.php deleted file mode 100644 index 90c9fcf0..00000000 --- a/tests/formatter/BookmarkRawFormatterTest.php +++ /dev/null @@ -1,97 +0,0 @@ -conf = new ConfigManager(self::$testConf); - $this->formatter = new BookmarkRawFormatter($this->conf, true); - } - - /** - * Test formatting a bookmark with all its attribute filled. - */ - public function testFormatFull() - { - $bookmark = new Bookmark(); - $bookmark->setId($id = 11); - $bookmark->setShortUrl($short = 'abcdef'); - $bookmark->setUrl($url = 'https://sub.domain.tld?query=here&for=real#hash'); - $bookmark->setTitle($title = 'This is a bookmark'); - $bookmark->setDescription($desc = '

Content

`Here is some content

'); - $bookmark->setTags($tags = ['tag1', 'bookmark', 'other', '']); - $bookmark->setThumbnail($thumb = 'http://domain2.tdl2/file.png'); - $bookmark->setSticky(true); - $bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190521_190412')); - $bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190521_191213')); - $bookmark->setPrivate(true); - - $link = $this->formatter->format($bookmark); - $this->assertEquals($id, $link['id']); - $this->assertEquals($short, $link['shorturl']); - $this->assertEquals($url, $link['url']); - $this->assertEquals($url, $link['real_url']); - $this->assertEquals($title, $link['title']); - $this->assertEquals($desc, $link['description']); - $this->assertEquals($tags, $link['taglist']); - $this->assertEquals(implode(' ', $tags), $link['tags']); - $this->assertEquals($thumb, $link['thumbnail']); - $this->assertEquals($created, $link['created']); - $this->assertEquals($created->getTimestamp(), $link['timestamp']); - $this->assertEquals($updated, $link['updated']); - $this->assertEquals($updated->getTimestamp(), $link['updated_timestamp']); - $this->assertTrue($link['private']); - $this->assertTrue($link['sticky']); - $this->assertEquals('private', $link['class']); - } - - /** - * Test formatting a bookmark with all its attribute filled. - */ - public function testFormatMinimal() - { - $bookmark = new Bookmark(); - - $link = $this->formatter->format($bookmark); - $this->assertEmpty($link['id']); - $this->assertEmpty($link['shorturl']); - $this->assertEmpty($link['url']); - $this->assertEmpty($link['real_url']); - $this->assertEmpty($link['title']); - $this->assertEmpty($link['description']); - $this->assertEmpty($link['taglist']); - $this->assertEmpty($link['tags']); - $this->assertEmpty($link['thumbnail']); - $this->assertEmpty($link['created']); - $this->assertEmpty($link['timestamp']); - $this->assertEmpty($link['updated']); - $this->assertEmpty($link['updated_timestamp']); - $this->assertFalse($link['private']); - $this->assertFalse($link['sticky']); - $this->assertEmpty($link['class']); - } -} diff --git a/tests/formatter/FormatterFactoryTest.php b/tests/formatter/FormatterFactoryTest.php deleted file mode 100644 index c6b493eb..00000000 --- a/tests/formatter/FormatterFactoryTest.php +++ /dev/null @@ -1,101 +0,0 @@ -conf = new ConfigManager(self::$testConf); - $this->factory = new FormatterFactory($this->conf, true); - } - - /** - * Test creating an instance of BookmarkFormatter without any setting -> default formatter - */ - public function testCreateInstanceDefault() - { - $this->assertInstanceOf(BookmarkDefaultFormatter::class, $this->factory->getFormatter()); - } - - /** - * Test creating an instance of BookmarkDefaultFormatter from settings - */ - public function testCreateInstanceDefaultSetting() - { - $this->conf->set('formatter', 'default'); - $this->assertInstanceOf(BookmarkDefaultFormatter::class, $this->factory->getFormatter()); - } - - /** - * Test creating an instance of BookmarkDefaultFormatter from parameter - */ - public function testCreateInstanceDefaultParameter() - { - $this->assertInstanceOf( - BookmarkDefaultFormatter::class, - $this->factory->getFormatter('default') - ); - } - - /** - * Test creating an instance of BookmarkRawFormatter from settings - */ - public function testCreateInstanceRawSetting() - { - $this->conf->set('formatter', 'raw'); - $this->assertInstanceOf(BookmarkRawFormatter::class, $this->factory->getFormatter()); - } - - /** - * Test creating an instance of BookmarkRawFormatter from parameter - */ - public function testCreateInstanceRawParameter() - { - $this->assertInstanceOf( - BookmarkRawFormatter::class, - $this->factory->getFormatter('raw') - ); - } - - /** - * Test creating an instance of BookmarkMarkdownFormatter from settings - */ - public function testCreateInstanceMarkdownSetting() - { - $this->conf->set('formatter', 'markdown'); - $this->assertInstanceOf(BookmarkMarkdownFormatter::class, $this->factory->getFormatter()); - } - - /** - * Test creating an instance of BookmarkMarkdownFormatter from parameter - */ - public function testCreateInstanceMarkdownParameter() - { - $this->assertInstanceOf( - BookmarkMarkdownFormatter::class, - $this->factory->getFormatter('markdown') - ); - } -} diff --git a/tests/front/ShaarliAdminMiddlewareTest.php b/tests/front/ShaarliAdminMiddlewareTest.php deleted file mode 100644 index 4d072612..00000000 --- a/tests/front/ShaarliAdminMiddlewareTest.php +++ /dev/null @@ -1,101 +0,0 @@ -container = $this->createMock(ShaarliContainer::class); - - touch(static::TMP_MOCK_FILE); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('getConfigFileExt')->willReturn(static::TMP_MOCK_FILE); - - $this->container->loginManager = $this->createMock(LoginManager::class); - $this->container->updater = $this->createMock(Updater::class); - - $this->container->environment = ['REQUEST_URI' => 'http://shaarli/subfolder/path']; - - $this->middleware = new ShaarliAdminMiddleware($this->container); - } - - public function tearDown(): void - { - unlink(static::TMP_MOCK_FILE); - } - - /** - * Try to access an admin controller while logged out -> redirected to login page. - */ - public function testMiddlewareWhileLoggedOut(): void - { - $this->container->loginManager->expects(static::once())->method('isLoggedIn')->willReturn(false); - - $request = $this->createMock(Request::class); - $request->method('getUri')->willReturnCallback(function (): Uri { - $uri = $this->createMock(Uri::class); - $uri->method('getBasePath')->willReturn('/subfolder'); - - return $uri; - }); - - $response = new Response(); - - /** @var Response $result */ - $result = $this->middleware->__invoke($request, $response, function () { - }); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame( - '/subfolder/login?returnurl=' . urlencode('http://shaarli/subfolder/path'), - $result->getHeader('location')[0] - ); - } - - /** - * Process controller while logged in. - */ - public function testMiddlewareWhileLoggedIn(): void - { - $this->container->loginManager->method('isLoggedIn')->willReturn(true); - - $request = $this->createMock(Request::class); - $request->method('getUri')->willReturnCallback(function (): Uri { - $uri = $this->createMock(Uri::class); - $uri->method('getBasePath')->willReturn('/subfolder'); - - return $uri; - }); - - $response = new Response(); - $controller = function (Request $request, Response $response): Response { - return $response->withStatus(418); // I'm a tea pot - }; - - /** @var Response $result */ - $result = $this->middleware->__invoke($request, $response, $controller); - - static::assertSame(418, $result->getStatusCode()); - } -} diff --git a/tests/front/ShaarliMiddlewareTest.php b/tests/front/ShaarliMiddlewareTest.php deleted file mode 100644 index 2fc1e69b..00000000 --- a/tests/front/ShaarliMiddlewareTest.php +++ /dev/null @@ -1,222 +0,0 @@ -container = $this->createMock(ShaarliContainer::class); - - touch(static::TMP_MOCK_FILE); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('getConfigFileExt')->willReturn(static::TMP_MOCK_FILE); - - $this->container->loginManager = $this->createMock(LoginManager::class); - - $this->container->environment = ['REQUEST_URI' => 'http://shaarli/subfolder/path']; - - $this->middleware = new ShaarliMiddleware($this->container); - } - - public function tearDown(): void - { - unlink(static::TMP_MOCK_FILE); - } - - /** - * Test middleware execution with valid controller call - */ - public function testMiddlewareExecution(): void - { - $request = $this->createMock(Request::class); - $request->method('getUri')->willReturnCallback(function (): Uri { - $uri = $this->createMock(Uri::class); - $uri->method('getBasePath')->willReturn('/subfolder'); - - return $uri; - }); - - $response = new Response(); - $controller = function (Request $request, Response $response): Response { - return $response->withStatus(418); // I'm a tea pot - }; - - /** @var Response $result */ - $result = $this->middleware->__invoke($request, $response, $controller); - - static::assertInstanceOf(Response::class, $result); - static::assertSame(418, $result->getStatusCode()); - } - - /** - * Test middleware execution with controller throwing a known front exception. - * The exception should be thrown to be later handled by the error handler. - */ - public function testMiddlewareExecutionWithFrontException(): void - { - $request = $this->createMock(Request::class); - $request->method('getUri')->willReturnCallback(function (): Uri { - $uri = $this->createMock(Uri::class); - $uri->method('getBasePath')->willReturn('/subfolder'); - - return $uri; - }); - - $response = new Response(); - $controller = function (): void { - $exception = new LoginBannedException(); - - throw new $exception(); - }; - - $pageBuilder = $this->createMock(PageBuilder::class); - $pageBuilder->method('render')->willReturnCallback(function (string $message): string { - return $message; - }); - $this->container->pageBuilder = $pageBuilder; - - $this->expectException(LoginBannedException::class); - - $this->middleware->__invoke($request, $response, $controller); - } - - /** - * Test middleware execution with controller throwing a not authorized exception - * The middle should send a redirection response to the login page. - */ - public function testMiddlewareExecutionWithUnauthorizedException(): void - { - $request = $this->createMock(Request::class); - $request->method('getUri')->willReturnCallback(function (): Uri { - $uri = $this->createMock(Uri::class); - $uri->method('getBasePath')->willReturn('/subfolder'); - - return $uri; - }); - - $response = new Response(); - $controller = function (): void { - throw new UnauthorizedException(); - }; - - /** @var Response $result */ - $result = $this->middleware->__invoke($request, $response, $controller); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame( - '/subfolder/login?returnurl=' . urlencode('http://shaarli/subfolder/path'), - $result->getHeader('location')[0] - ); - } - - /** - * Test middleware execution with controller throwing a not authorized exception. - * The exception should be thrown to be later handled by the error handler. - */ - public function testMiddlewareExecutionWithServerException(): void - { - $request = $this->createMock(Request::class); - $request->method('getUri')->willReturnCallback(function (): Uri { - $uri = $this->createMock(Uri::class); - $uri->method('getBasePath')->willReturn('/subfolder'); - - return $uri; - }); - - $dummyException = new class () extends \Exception { - }; - - $response = new Response(); - $controller = function () use ($dummyException): void { - throw $dummyException; - }; - - $parameters = []; - $this->container->pageBuilder = $this->createMock(PageBuilder::class); - $this->container->pageBuilder->method('render')->willReturnCallback(function (string $message): string { - return $message; - }); - $this->container->pageBuilder - ->method('assign') - ->willReturnCallback(function (string $key, string $value) use (&$parameters): void { - $parameters[$key] = $value; - }) - ; - - $this->expectException(get_class($dummyException)); - - $this->middleware->__invoke($request, $response, $controller); - } - - public function testMiddlewareExecutionWithUpdates(): void - { - $request = $this->createMock(Request::class); - $request->method('getUri')->willReturnCallback(function (): Uri { - $uri = $this->createMock(Uri::class); - $uri->method('getBasePath')->willReturn('/subfolder'); - - return $uri; - }); - - $response = new Response(); - $controller = function (Request $request, Response $response): Response { - return $response->withStatus(418); // I'm a tea pot - }; - - $this->container->loginManager = $this->createMock(LoginManager::class); - $this->container->loginManager->method('isLoggedIn')->willReturn(true); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('get')->willReturnCallback(function (string $key): string { - return $key; - }); - $this->container->conf->method('getConfigFileExt')->willReturn(static::TMP_MOCK_FILE); - - $this->container->pageCacheManager = $this->createMock(PageCacheManager::class); - $this->container->pageCacheManager->expects(static::once())->method('invalidateCaches'); - - $this->container->updater = $this->createMock(Updater::class); - $this->container->updater - ->expects(static::once()) - ->method('update') - ->willReturn(['update123']) - ; - $this->container->updater->method('getDoneUpdates')->willReturn($updates = ['update123', 'other']); - $this->container->updater - ->expects(static::once()) - ->method('writeUpdates') - ->with('resource.updates', $updates) - ; - - /** @var Response $result */ - $result = $this->middleware->__invoke($request, $response, $controller); - - static::assertInstanceOf(Response::class, $result); - static::assertSame(418, $result->getStatusCode()); - } -} diff --git a/tests/front/controller/admin/ConfigureControllerTest.php b/tests/front/controller/admin/ConfigureControllerTest.php deleted file mode 100644 index 822429d7..00000000 --- a/tests/front/controller/admin/ConfigureControllerTest.php +++ /dev/null @@ -1,250 +0,0 @@ -createContainer(); - - $this->controller = new ConfigureController($this->container); - } - - /** - * Test displaying configure page - it should display all config variables - */ - public function testIndex(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('get')->willReturnCallback(function (string $key) { - return $key; - }); - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('configure', (string) $result->getBody()); - - static::assertSame('Configure - general.title', $assignedVariables['pagetitle']); - static::assertSame('general.title', $assignedVariables['title']); - static::assertSame('resource.theme', $assignedVariables['theme']); - static::assertEmpty($assignedVariables['theme_available']); - static::assertSame(['default', 'markdown', 'markdownExtra'], $assignedVariables['formatter_available']); - static::assertNotEmpty($assignedVariables['continents']); - static::assertNotEmpty($assignedVariables['cities']); - static::assertSame('general.retrieve_description', $assignedVariables['retrieve_description']); - static::assertSame('privacy.default_private_links', $assignedVariables['private_links_default']); - static::assertSame('security.session_protection_disabled', $assignedVariables['session_protection_disabled']); - static::assertSame('feed.rss_permalinks', $assignedVariables['enable_rss_permalinks']); - static::assertSame('updates.check_updates', $assignedVariables['enable_update_check']); - static::assertSame('privacy.hide_public_links', $assignedVariables['hide_public_links']); - static::assertSame('api.enabled', $assignedVariables['api_enabled']); - static::assertSame('api.secret', $assignedVariables['api_secret']); - static::assertCount(7, $assignedVariables['languages']); - static::assertArrayHasKey('gd_enabled', $assignedVariables); - static::assertSame('thumbnails.mode', $assignedVariables['thumbnails_mode']); - } - - /** - * Test posting a new config - make sure that everything is saved properly, without errors. - */ - public function testSaveNewConfig(): void - { - $session = []; - $this->assignSessionVars($session); - - $parameters = [ - 'token' => 'token', - 'continent' => 'Europe', - 'city' => 'Moscow', - 'title' => 'Shaarli', - 'titleLink' => './', - 'retrieveDescription' => 'on', - 'theme' => 'vintage', - 'disablesessionprotection' => null, - 'privateLinkByDefault' => true, - 'enableRssPermalinks' => true, - 'updateCheck' => false, - 'hidePublicLinks' => 'on', - 'enableApi' => 'on', - 'apiSecret' => 'abcdef', - 'formatter' => 'markdown', - 'language' => 'fr', - 'enableThumbnails' => Thumbnailer::MODE_NONE, - ]; - - $parametersConfigMapping = [ - 'general.timezone' => $parameters['continent'] . '/' . $parameters['city'], - 'general.title' => $parameters['title'], - 'general.header_link' => $parameters['titleLink'], - 'general.retrieve_description' => !!$parameters['retrieveDescription'], - 'resource.theme' => $parameters['theme'], - 'security.session_protection_disabled' => !!$parameters['disablesessionprotection'], - 'privacy.default_private_links' => !!$parameters['privateLinkByDefault'], - 'feed.rss_permalinks' => !!$parameters['enableRssPermalinks'], - 'updates.check_updates' => !!$parameters['updateCheck'], - 'privacy.hide_public_links' => !!$parameters['hidePublicLinks'], - 'api.enabled' => !!$parameters['enableApi'], - 'api.secret' => $parameters['apiSecret'], - 'formatter' => $parameters['formatter'], - 'translation.language' => $parameters['language'], - 'thumbnails.mode' => $parameters['enableThumbnails'], - ]; - - $request = $this->createMock(Request::class); - $request - ->expects(static::atLeastOnce()) - ->method('getParam')->willReturnCallback(function (string $key) use ($parameters) { - if (false === array_key_exists($key, $parameters)) { - static::fail('unknown key: ' . $key); - } - - return $parameters[$key]; - }); - - $response = new Response(); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf - ->expects(static::atLeastOnce()) - ->method('set') - ->willReturnCallback(function (string $key, $value) use ($parametersConfigMapping): void { - if (false === array_key_exists($key, $parametersConfigMapping)) { - static::fail('unknown key: ' . $key); - } - - static::assertSame($parametersConfigMapping[$key], $value); - }); - - $result = $this->controller->save($request, $response); - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/configure'], $result->getHeader('Location')); - - static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); - static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); - static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session); - static::assertSame(['Configuration was saved.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]); - } - - /** - * Test posting a new config - wrong token. - */ - public function testSaveNewConfigWrongToken(): void - { - $this->container->sessionManager = $this->createMock(SessionManager::class); - $this->container->sessionManager->method('checkToken')->willReturn(false); - - $this->container->conf->expects(static::never())->method('set'); - $this->container->conf->expects(static::never())->method('write'); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->expectException(WrongTokenException::class); - - $this->controller->save($request, $response); - } - - /** - * Test posting a new config - thumbnail activation. - */ - public function testSaveNewConfigThumbnailsActivation(): void - { - $session = []; - $this->assignSessionVars($session); - - $request = $this->createMock(Request::class); - $request - ->expects(static::atLeastOnce()) - ->method('getParam')->willReturnCallback(function (string $key) { - if ('enableThumbnails' === $key) { - return Thumbnailer::MODE_ALL; - } - - return $key; - }) - ; - $response = new Response(); - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/configure'], $result->getHeader('Location')); - - static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); - static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); - static::assertStringContainsString( - 'You have enabled or changed thumbnails mode', - $session[SessionManager::KEY_WARNING_MESSAGES][0] - ); - static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session); - static::assertSame(['Configuration was saved.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]); - } - - /** - * Test posting a new config - thumbnail activation. - */ - public function testSaveNewConfigThumbnailsAlreadyActive(): void - { - $session = []; - $this->assignSessionVars($session); - - $request = $this->createMock(Request::class); - $request - ->expects(static::atLeastOnce()) - ->method('getParam')->willReturnCallback(function (string $key) { - if ('enableThumbnails' === $key) { - return Thumbnailer::MODE_ALL; - } - - return $key; - }) - ; - $response = new Response(); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf - ->expects(static::atLeastOnce()) - ->method('get') - ->willReturnCallback(function (string $key): string { - if ('thumbnails.mode' === $key) { - return Thumbnailer::MODE_ALL; - } - - return $key; - }) - ; - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/configure'], $result->getHeader('Location')); - - static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); - static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); - static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session); - static::assertSame(['Configuration was saved.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]); - } -} diff --git a/tests/front/controller/admin/ExportControllerTest.php b/tests/front/controller/admin/ExportControllerTest.php deleted file mode 100644 index a8401c1f..00000000 --- a/tests/front/controller/admin/ExportControllerTest.php +++ /dev/null @@ -1,166 +0,0 @@ -createContainer(); - - $this->controller = new ExportController($this->container); - } - - /** - * Test displaying export page - */ - public function testIndex(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('export', (string) $result->getBody()); - - static::assertSame('Export - Shaarli', $assignedVariables['pagetitle']); - } - - /** - * Test posting an export request - */ - public function testExportDefault(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $parameters = [ - 'selection' => 'all', - 'prepend_note_url' => 'on', - ]; - - $request = $this->createMock(Request::class); - $request->method('getParam')->willReturnCallback(function (string $key) use ($parameters) { - return $parameters[$key] ?? null; - }); - $response = new Response(); - - $bookmarks = [ - (new Bookmark())->setUrl('http://link1.tld')->setTitle('Title 1'), - (new Bookmark())->setUrl('http://link2.tld')->setTitle('Title 2'), - ]; - - $this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class); - $this->container->netscapeBookmarkUtils - ->expects(static::once()) - ->method('filterAndFormat') - ->willReturnCallback( - function ( - BookmarkFormatter $formatter, - string $selection, - bool $prependNoteUrl, - string $indexUrl - ) use ( - $parameters, - $bookmarks - ): array { - static::assertInstanceOf(BookmarkRawFormatter::class, $formatter); - static::assertSame($parameters['selection'], $selection); - static::assertTrue($prependNoteUrl); - static::assertSame('http://shaarli/subfolder/', $indexUrl); - - return $bookmarks; - } - ) - ; - - $result = $this->controller->export($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('export.bookmarks', (string) $result->getBody()); - static::assertSame(['text/html; charset=utf-8'], $result->getHeader('content-type')); - static::assertRegExp( - '/attachment; filename=bookmarks_all_[\d]{8}_[\d]{6}\.html/', - $result->getHeader('content-disposition')[0] - ); - - static::assertNotEmpty($assignedVariables['date']); - static::assertSame(PHP_EOL, $assignedVariables['eol']); - static::assertSame('all', $assignedVariables['selection']); - static::assertSame($bookmarks, $assignedVariables['links']); - } - - /** - * Test posting an export request - without selection parameter - */ - public function testExportSelectionMissing(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Please select an export mode.']) - ; - - $result = $this->controller->export($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/export'], $result->getHeader('location')); - } - - /** - * Test posting an export request - without selection parameter - */ - public function testExportErrorEncountered(): void - { - $parameters = [ - 'selection' => 'all', - ]; - - $request = $this->createMock(Request::class); - $request->method('getParam')->willReturnCallback(function (string $key) use ($parameters) { - return $parameters[$key] ?? null; - }); - $response = new Response(); - - $this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class); - $this->container->netscapeBookmarkUtils - ->expects(static::once()) - ->method('filterAndFormat') - ->willThrowException(new \Exception($message = 'error message')); - ; - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, [$message]) - ; - - $result = $this->controller->export($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/export'], $result->getHeader('location')); - } -} diff --git a/tests/front/controller/admin/FrontAdminControllerMockHelper.php b/tests/front/controller/admin/FrontAdminControllerMockHelper.php deleted file mode 100644 index 2b9f2ef1..00000000 --- a/tests/front/controller/admin/FrontAdminControllerMockHelper.php +++ /dev/null @@ -1,56 +0,0 @@ -parentCreateContainer(); - - $this->container->history = $this->createMock(History::class); - - $this->container->loginManager->method('isLoggedIn')->willReturn(true); - $this->container->sessionManager->method('checkToken')->willReturn(true); - } - - - /** - * Pass a reference of an array which will be populated by `sessionManager->setSessionParameter` - * calls during execution. - * - * @param mixed $variables Array reference to populate. - */ - protected function assignSessionVars(array &$variables): void - { - $this->container->sessionManager - ->expects(static::atLeastOnce()) - ->method('setSessionParameter') - ->willReturnCallback(function ($key, $value) use (&$variables) { - $variables[$key] = $value; - - return $this->container->sessionManager; - }) - ; - } -} diff --git a/tests/front/controller/admin/ImportControllerTest.php b/tests/front/controller/admin/ImportControllerTest.php deleted file mode 100644 index 50513115..00000000 --- a/tests/front/controller/admin/ImportControllerTest.php +++ /dev/null @@ -1,151 +0,0 @@ -createContainer(); - - $this->controller = new ImportController($this->container); - } - - /** - * Test displaying import page - */ - public function testIndex(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('import', (string) $result->getBody()); - - static::assertSame('Import - Shaarli', $assignedVariables['pagetitle']); - static::assertIsInt($assignedVariables['maxfilesize']); - static::assertRegExp('/\d+[KM]iB/', $assignedVariables['maxfilesizeHuman']); - } - - /** - * Test importing a file with default and valid parameters - */ - public function testImportDefault(): void - { - $parameters = [ - 'abc' => 'def', - 'other' => 'param', - ]; - - $requestFile = new UploadedFile('file', 'name', 'type', 123); - - $request = $this->createMock(Request::class); - $request->method('getParams')->willReturnCallback(function () use ($parameters) { - return $parameters; - }); - $request->method('getUploadedFiles')->willReturn(['filetoupload' => $requestFile]); - $response = new Response(); - - $this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class); - $this->container->netscapeBookmarkUtils - ->expects(static::once()) - ->method('import') - ->willReturnCallback( - function ( - array $post, - UploadedFileInterface $file - ) use ( - $parameters, - $requestFile - ): string { - static::assertSame($parameters, $post); - static::assertSame($requestFile, $file); - - return 'status'; - } - ) - ; - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_SUCCESS_MESSAGES, ['status']) - ; - - $result = $this->controller->import($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/import'], $result->getHeader('location')); - } - - /** - * Test posting an import request - without import file - */ - public function testImportFileMissing(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['No import file provided.']) - ; - - $result = $this->controller->import($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/import'], $result->getHeader('location')); - } - - /** - * Test posting an import request - with an empty file - */ - public function testImportEmptyFile(): void - { - $requestFile = new UploadedFile('file', 'name', 'type', 0); - - $request = $this->createMock(Request::class); - $request->method('getUploadedFiles')->willReturn(['filetoupload' => $requestFile]); - $response = new Response(); - - $this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class); - $this->container->netscapeBookmarkUtils->expects(static::never())->method('filterAndFormat'); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->willReturnCallback(function (string $key, array $value): SessionManager { - static::assertSame(SessionManager::KEY_ERROR_MESSAGES, $key); - static::assertStringStartsWith('The file you are trying to upload is probably bigger', $value[0]); - - return $this->container->sessionManager; - }) - ; - - $result = $this->controller->import($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/import'], $result->getHeader('location')); - } -} diff --git a/tests/front/controller/admin/LogoutControllerTest.php b/tests/front/controller/admin/LogoutControllerTest.php deleted file mode 100644 index 94e53019..00000000 --- a/tests/front/controller/admin/LogoutControllerTest.php +++ /dev/null @@ -1,50 +0,0 @@ -createContainer(); - - $this->controller = new LogoutController($this->container); - } - - public function testValidControllerInvoke(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->pageCacheManager->expects(static::once())->method('invalidateCaches'); - - $this->container->sessionManager = $this->createMock(SessionManager::class); - $this->container->sessionManager->expects(static::once())->method('logout'); - - $this->container->cookieManager = $this->createMock(CookieManager::class); - $this->container->cookieManager - ->expects(static::once()) - ->method('setCookieParameter') - ->with(CookieManager::STAY_SIGNED_IN, 'false', 0, '/subfolder/') - ; - - $result = $this->controller->index($request, $response); - - static::assertInstanceOf(Response::class, $result); - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } -} diff --git a/tests/front/controller/admin/ManageTagControllerTest.php b/tests/front/controller/admin/ManageTagControllerTest.php deleted file mode 100644 index 56a64cbb..00000000 --- a/tests/front/controller/admin/ManageTagControllerTest.php +++ /dev/null @@ -1,409 +0,0 @@ -createContainer(); - - $this->controller = new ManageTagController($this->container); - } - - /** - * Test displaying manage tag page - */ - public function testIndex(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $request->method('getParam')->with('fromtag')->willReturn('fromtag'); - $response = new Response(); - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('changetag', (string) $result->getBody()); - - static::assertSame('fromtag', $assignedVariables['fromtag']); - static::assertSame('@', $assignedVariables['tags_separator']); - static::assertSame('Manage tags - Shaarli', $assignedVariables['pagetitle']); - } - - /** - * Test displaying manage tag page - */ - public function testIndexWhitespaceSeparator(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('get')->willReturnCallback(function (string $key) { - return $key === 'general.tags_separator' ? ' ' : $key; - }); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->controller->index($request, $response); - - static::assertSame(' ', $assignedVariables['tags_separator']); - static::assertSame('whitespace', $assignedVariables['tags_separator_desc']); - } - - /** - * Test posting a tag update - rename tag - valid info provided. - */ - public function testSaveRenameTagValid(): void - { - $session = []; - $this->assignSessionVars($session); - - $requestParameters = [ - 'renametag' => 'rename', - 'fromtag' => 'old-tag', - 'totag' => 'new-tag', - ]; - $request = $this->createMock(Request::class); - $request - ->expects(static::atLeastOnce()) - ->method('getParam') - ->willReturnCallback(function (string $key) use ($requestParameters): ?string { - return $requestParameters[$key] ?? null; - }) - ; - $response = new Response(); - - $bookmark1 = $this->createMock(Bookmark::class); - $bookmark2 = $this->createMock(Bookmark::class); - $this->container->bookmarkService - ->expects(static::once()) - ->method('search') - ->with(['searchtags' => 'old-tag'], BookmarkFilter::$ALL, true) - ->willReturnCallback(function () use ($bookmark1, $bookmark2): SearchResult { - $bookmark1->expects(static::once())->method('renameTag')->with('old-tag', 'new-tag'); - $bookmark2->expects(static::once())->method('renameTag')->with('old-tag', 'new-tag'); - - return SearchResult::getSearchResult([$bookmark1, $bookmark2]); - }) - ; - $this->container->bookmarkService - ->expects(static::exactly(2)) - ->method('set') - ->withConsecutive([$bookmark1, false], [$bookmark2, false]) - ; - $this->container->bookmarkService->expects(static::once())->method('save'); - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/?searchtags=new-tag'], $result->getHeader('location')); - - static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); - static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); - static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session); - static::assertSame(['The tag was renamed in 2 bookmarks.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]); - } - - /** - * Test posting a tag update - delete tag - valid info provided. - */ - public function testSaveDeleteTagValid(): void - { - $session = []; - $this->assignSessionVars($session); - - $requestParameters = [ - 'deletetag' => 'delete', - 'fromtag' => 'old-tag', - ]; - $request = $this->createMock(Request::class); - $request - ->expects(static::atLeastOnce()) - ->method('getParam') - ->willReturnCallback(function (string $key) use ($requestParameters): ?string { - return $requestParameters[$key] ?? null; - }) - ; - $response = new Response(); - - $bookmark1 = $this->createMock(Bookmark::class); - $bookmark2 = $this->createMock(Bookmark::class); - $this->container->bookmarkService - ->expects(static::once()) - ->method('search') - ->with(['searchtags' => 'old-tag'], BookmarkFilter::$ALL, true) - ->willReturnCallback(function () use ($bookmark1, $bookmark2): SearchResult { - $bookmark1->expects(static::once())->method('deleteTag')->with('old-tag'); - $bookmark2->expects(static::once())->method('deleteTag')->with('old-tag'); - - return SearchResult::getSearchResult([$bookmark1, $bookmark2]); - }) - ; - $this->container->bookmarkService - ->expects(static::exactly(2)) - ->method('set') - ->withConsecutive([$bookmark1, false], [$bookmark2, false]) - ; - $this->container->bookmarkService->expects(static::once())->method('save'); - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location')); - - static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); - static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); - static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session); - static::assertSame(['The tag was removed from 2 bookmarks.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]); - } - - /** - * Test posting a tag update - wrong token. - */ - public function testSaveWrongToken(): void - { - $this->container->sessionManager = $this->createMock(SessionManager::class); - $this->container->sessionManager->method('checkToken')->willReturn(false); - - $this->container->conf->expects(static::never())->method('set'); - $this->container->conf->expects(static::never())->method('write'); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->expectException(WrongTokenException::class); - - $this->controller->save($request, $response); - } - - /** - * Test posting a tag update - rename tag - missing "FROM" tag. - */ - public function testSaveRenameTagMissingFrom(): void - { - $session = []; - $this->assignSessionVars($session); - - $requestParameters = [ - 'renametag' => 'rename', - ]; - $request = $this->createMock(Request::class); - $request - ->expects(static::atLeastOnce()) - ->method('getParam') - ->willReturnCallback(function (string $key) use ($requestParameters): ?string { - return $requestParameters[$key] ?? null; - }) - ; - $response = new Response(); - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location')); - - static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); - static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); - static::assertArrayNotHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session); - static::assertSame(['Invalid tags provided.'], $session[SessionManager::KEY_WARNING_MESSAGES]); - } - - /** - * Test posting a tag update - delete tag - missing "FROM" tag. - */ - public function testSaveDeleteTagMissingFrom(): void - { - $session = []; - $this->assignSessionVars($session); - - $requestParameters = [ - 'deletetag' => 'delete', - ]; - $request = $this->createMock(Request::class); - $request - ->expects(static::atLeastOnce()) - ->method('getParam') - ->willReturnCallback(function (string $key) use ($requestParameters): ?string { - return $requestParameters[$key] ?? null; - }) - ; - $response = new Response(); - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location')); - - static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); - static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); - static::assertArrayNotHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session); - static::assertSame(['Invalid tags provided.'], $session[SessionManager::KEY_WARNING_MESSAGES]); - } - - /** - * Test posting a tag update - rename tag - missing "TO" tag. - */ - public function testSaveRenameTagMissingTo(): void - { - $session = []; - $this->assignSessionVars($session); - - $requestParameters = [ - 'renametag' => 'rename', - 'fromtag' => 'old-tag' - ]; - $request = $this->createMock(Request::class); - $request - ->expects(static::atLeastOnce()) - ->method('getParam') - ->willReturnCallback(function (string $key) use ($requestParameters): ?string { - return $requestParameters[$key] ?? null; - }) - ; - $response = new Response(); - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location')); - - static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); - static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); - static::assertArrayNotHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session); - static::assertSame(['Invalid tags provided.'], $session[SessionManager::KEY_WARNING_MESSAGES]); - } - - /** - * Test changeSeparator to '#': redirection + success message. - */ - public function testChangeSeparatorValid(): void - { - $toSeparator = '#'; - - $session = []; - $this->assignSessionVars($session); - - $request = $this->createMock(Request::class); - $request - ->expects(static::atLeastOnce()) - ->method('getParam') - ->willReturnCallback(function (string $key) use ($toSeparator): ?string { - return $key === 'separator' ? $toSeparator : $key; - }) - ; - $response = new Response(); - - $this->container->conf - ->expects(static::once()) - ->method('set') - ->with('general.tags_separator', $toSeparator, true, true) - ; - - $result = $this->controller->changeSeparator($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location')); - - static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); - static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); - static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session); - static::assertSame( - ['Your tags separator setting has been updated!'], - $session[SessionManager::KEY_SUCCESS_MESSAGES] - ); - } - - /** - * Test changeSeparator to '#@' (too long): redirection + error message. - */ - public function testChangeSeparatorInvalidTooLong(): void - { - $toSeparator = '#@'; - - $session = []; - $this->assignSessionVars($session); - - $request = $this->createMock(Request::class); - $request - ->expects(static::atLeastOnce()) - ->method('getParam') - ->willReturnCallback(function (string $key) use ($toSeparator): ?string { - return $key === 'separator' ? $toSeparator : $key; - }) - ; - $response = new Response(); - - $this->container->conf->expects(static::never())->method('set'); - - $result = $this->controller->changeSeparator($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location')); - - static::assertArrayNotHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session); - static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); - static::assertArrayHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); - static::assertSame( - ['Tags separator must be a single character.'], - $session[SessionManager::KEY_ERROR_MESSAGES] - ); - } - - /** - * Test changeSeparator to '#@' (too long): redirection + error message. - */ - public function testChangeSeparatorInvalidReservedCharacter(): void - { - $toSeparator = '*'; - - $session = []; - $this->assignSessionVars($session); - - $request = $this->createMock(Request::class); - $request - ->expects(static::atLeastOnce()) - ->method('getParam') - ->willReturnCallback(function (string $key) use ($toSeparator): ?string { - return $key === 'separator' ? $toSeparator : $key; - }) - ; - $response = new Response(); - - $this->container->conf->expects(static::never())->method('set'); - - $result = $this->controller->changeSeparator($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location')); - - static::assertArrayNotHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session); - static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); - static::assertArrayHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); - static::assertStringStartsWith( - 'These characters are reserved and can\'t be used as tags separator', - $session[SessionManager::KEY_ERROR_MESSAGES][0] - ); - } -} diff --git a/tests/front/controller/admin/PasswordControllerTest.php b/tests/front/controller/admin/PasswordControllerTest.php deleted file mode 100644 index e73b3711..00000000 --- a/tests/front/controller/admin/PasswordControllerTest.php +++ /dev/null @@ -1,203 +0,0 @@ -createContainer(); - $this->assignTemplateVars($this->assignedVariables); - - $this->controller = new PasswordController($this->container); - } - - /** - * Test displaying the change password page. - */ - public function testGetPage(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('changepassword', (string) $result->getBody()); - static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']); - } - - /** - * Change the password with valid parameters - */ - public function testPostNewPasswordDefault(): void - { - $request = $this->createMock(Request::class); - $request->method('getParam')->willReturnCallback(function (string $key): string { - if ('oldpassword' === $key) { - return 'old'; - } - if ('setpassword' === $key) { - return 'new'; - } - - return $key; - }); - $response = new Response(); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) { - if ('credentials.hash' === $key) { - return sha1('old' . 'credentials.login' . 'credentials.salt'); - } - - return strpos($key, 'credentials') !== false ? $key : $default; - }); - $this->container->conf->expects(static::once())->method('write')->with(true); - - $this->container->conf - ->method('set') - ->willReturnCallback(function (string $key, string $value) { - if ('credentials.hash' === $key) { - static::assertSame(sha1('new' . 'credentials.login' . 'credentials.salt'), $value); - } - }) - ; - - $result = $this->controller->change($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('changepassword', (string) $result->getBody()); - static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']); - } - - /** - * Change the password with a wrong existing password - */ - public function testPostNewPasswordWrongOldPassword(): void - { - $request = $this->createMock(Request::class); - $request->method('getParam')->willReturnCallback(function (string $key): string { - if ('oldpassword' === $key) { - return 'wrong'; - } - if ('setpassword' === $key) { - return 'new'; - } - - return $key; - }); - $response = new Response(); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) { - if ('credentials.hash' === $key) { - return sha1('old' . 'credentials.login' . 'credentials.salt'); - } - - return strpos($key, 'credentials') !== false ? $key : $default; - }); - - $this->container->conf->expects(static::never())->method('set'); - $this->container->conf->expects(static::never())->method('write'); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['The old password is not correct.']) - ; - - $result = $this->controller->change($request, $response); - - static::assertSame(400, $result->getStatusCode()); - static::assertSame('changepassword', (string) $result->getBody()); - static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']); - } - - /** - * Change the password with a wrong existing password - */ - public function testPostNewPasswordWrongToken(): void - { - $this->container->sessionManager = $this->createMock(SessionManager::class); - $this->container->sessionManager->method('checkToken')->willReturn(false); - - $this->container->conf->expects(static::never())->method('set'); - $this->container->conf->expects(static::never())->method('write'); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->expectException(WrongTokenException::class); - - $this->controller->change($request, $response); - } - - /** - * Change the password with an empty new password - */ - public function testPostNewEmptyPassword(): void - { - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['You must provide the current and new password to change it.']) - ; - - $this->container->conf->expects(static::never())->method('set'); - $this->container->conf->expects(static::never())->method('write'); - - $request = $this->createMock(Request::class); - $request->method('getParam')->willReturnCallback(function (string $key): string { - if ('oldpassword' === $key) { - return 'old'; - } - if ('setpassword' === $key) { - return ''; - } - - return $key; - }); - $response = new Response(); - - $result = $this->controller->change($request, $response); - - static::assertSame(400, $result->getStatusCode()); - static::assertSame('changepassword', (string) $result->getBody()); - static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']); - } - - /** - * Change the password on an open shaarli - */ - public function testPostNewPasswordOnOpenShaarli(): void - { - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('get')->with('security.open_shaarli')->willReturn(true); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->expectException(OpenShaarliPasswordException::class); - - $this->controller->change($request, $response); - } -} diff --git a/tests/front/controller/admin/PluginsControllerTest.php b/tests/front/controller/admin/PluginsControllerTest.php deleted file mode 100644 index fbec3197..00000000 --- a/tests/front/controller/admin/PluginsControllerTest.php +++ /dev/null @@ -1,209 +0,0 @@ -createContainer(); - - $this->controller = new PluginsController($this->container); - - mkdir($path = __DIR__ . '/folder'); - PluginManager::$PLUGINS_PATH = $path; - array_map(function (string $plugin) use ($path) { - touch($path . '/' . $plugin); - }, static::PLUGIN_NAMES); - } - - public function tearDown(): void - { - $path = __DIR__ . '/folder'; - array_map(function (string $plugin) use ($path) { - unlink($path . '/' . $plugin); - }, static::PLUGIN_NAMES); - rmdir($path); - } - - /** - * Test displaying plugins admin page - */ - public function testIndex(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $data = [ - 'plugin1' => ['order' => 2, 'other' => 'field'], - 'plugin2' => ['order' => 1], - 'plugin3' => ['order' => false, 'abc' => 'def'], - 'plugin4' => [], - ]; - - $this->container->pluginManager - ->expects(static::once()) - ->method('getPluginsMeta') - ->willReturn($data); - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('pluginsadmin', (string) $result->getBody()); - - static::assertSame('Plugin Administration - Shaarli', $assignedVariables['pagetitle']); - static::assertSame( - ['plugin2' => $data['plugin2'], 'plugin1' => $data['plugin1']], - $assignedVariables['enabledPlugins'] - ); - static::assertSame( - ['plugin3' => $data['plugin3'], 'plugin4' => $data['plugin4']], - $assignedVariables['disabledPlugins'] - ); - } - - /** - * Test save plugins admin page - */ - public function testSaveEnabledPlugins(): void - { - $parameters = [ - 'plugin1' => 'on', - 'order_plugin1' => '2', - 'plugin2' => 'on', - ]; - - $request = $this->createMock(Request::class); - $request - ->expects(static::atLeastOnce()) - ->method('getParams') - ->willReturnCallback(function () use ($parameters): array { - return $parameters; - }) - ; - $response = new Response(); - - $this->container->pluginManager - ->expects(static::once()) - ->method('executeHooks') - ->with('save_plugin_parameters', $parameters) - ; - $this->container->conf - ->expects(static::atLeastOnce()) - ->method('set') - ->with('general.enabled_plugins', ['plugin1', 'plugin2']) - ; - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/plugins'], $result->getHeader('location')); - } - - /** - * Test save plugin parameters - */ - public function testSavePluginParameters(): void - { - $parameters = [ - 'parameters_form' => true, - 'parameter1' => 'blip', - 'parameter2' => 'blop', - 'token' => 'this parameter should not be saved' - ]; - - $request = $this->createMock(Request::class); - $request - ->expects(static::atLeastOnce()) - ->method('getParams') - ->willReturnCallback(function () use ($parameters): array { - return $parameters; - }) - ; - $response = new Response(); - - $this->container->pluginManager - ->expects(static::once()) - ->method('executeHooks') - ->with('save_plugin_parameters', $parameters) - ; - $this->container->conf - ->expects(static::exactly(2)) - ->method('set') - ->withConsecutive(['plugins.parameter1', 'blip'], ['plugins.parameter2', 'blop']) - ; - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/plugins'], $result->getHeader('location')); - } - - /** - * Test save plugin parameters - error encountered - */ - public function testSaveWithError(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf - ->expects(static::atLeastOnce()) - ->method('write') - ->willThrowException(new \Exception($message = 'error message')) - ; - - $this->container->sessionManager = $this->createMock(SessionManager::class); - $this->container->sessionManager->method('checkToken')->willReturn(true); - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with( - SessionManager::KEY_ERROR_MESSAGES, - ['Error while saving plugin configuration: ' . PHP_EOL . $message] - ) - ; - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/admin/plugins'], $result->getHeader('location')); - } - - /** - * Test save plugin parameters - wrong token - */ - public function testSaveWrongToken(): void - { - $this->container->sessionManager = $this->createMock(SessionManager::class); - $this->container->sessionManager->method('checkToken')->willReturn(false); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->expectException(WrongTokenException::class); - - $this->controller->save($request, $response); - } -} diff --git a/tests/front/controller/admin/ServerControllerTest.php b/tests/front/controller/admin/ServerControllerTest.php deleted file mode 100644 index 355cce7d..00000000 --- a/tests/front/controller/admin/ServerControllerTest.php +++ /dev/null @@ -1,184 +0,0 @@ -createContainer(); - - $this->controller = new ServerController($this->container); - - // initialize dummy cache - @mkdir('sandbox/'); - foreach (['pagecache', 'tmp', 'cache'] as $folder) { - @mkdir('sandbox/' . $folder); - @touch('sandbox/' . $folder . '/.htaccess'); - @touch('sandbox/' . $folder . '/1'); - @touch('sandbox/' . $folder . '/2'); - } - } - - public function tearDown(): void - { - foreach (['pagecache', 'tmp', 'cache'] as $folder) { - @unlink('sandbox/' . $folder . '/.htaccess'); - @unlink('sandbox/' . $folder . '/1'); - @unlink('sandbox/' . $folder . '/2'); - @rmdir('sandbox/' . $folder); - } - } - - /** - * Test default display of server administration page. - */ - public function testIndex(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('server', (string) $result->getBody()); - - static::assertSame(PHP_VERSION, $assignedVariables['php_version']); - static::assertArrayHasKey('php_has_reached_eol', $assignedVariables); - static::assertArrayHasKey('php_eol', $assignedVariables); - static::assertArrayHasKey('php_extensions', $assignedVariables); - static::assertArrayHasKey('permissions', $assignedVariables); - static::assertEmpty($assignedVariables['permissions']); - - static::assertRegExp( - '#https://github\.com/shaarli/Shaarli/releases/tag/v\d+\.\d+\.\d+#', - $assignedVariables['release_url'] - ); - static::assertRegExp('#v\d+\.\d+\.\d+#', $assignedVariables['latest_version']); - static::assertRegExp('#(v\d+\.\d+\.\d+|dev)#', $assignedVariables['current_version']); - static::assertArrayHasKey('index_url', $assignedVariables); - static::assertArrayHasKey('client_ip', $assignedVariables); - static::assertArrayHasKey('trusted_proxies', $assignedVariables); - - static::assertSame('Server administration - Shaarli', $assignedVariables['pagetitle']); - } - - /** - * Test clearing the main cache - */ - public function testClearMainCache(): void - { - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) { - if ($key === 'resource.page_cache') { - return 'sandbox/pagecache'; - } elseif ($key === 'resource.raintpl_tmp') { - return 'sandbox/tmp'; - } elseif ($key === 'resource.thumbnails_cache') { - return 'sandbox/cache'; - } else { - return $default; - } - }); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_SUCCESS_MESSAGES, ['Shaarli\'s cache folder has been cleared!']) - ; - - $request = $this->createMock(Request::class); - $request->method('getQueryParam')->with('type')->willReturn('main'); - $response = new Response(); - - $result = $this->controller->clearCache($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame('/subfolder/admin/server', (string) $result->getHeaderLine('Location')); - - static::assertFileNotExists('sandbox/pagecache/1'); - static::assertFileNotExists('sandbox/pagecache/2'); - static::assertFileNotExists('sandbox/tmp/1'); - static::assertFileNotExists('sandbox/tmp/2'); - - static::assertFileExists('sandbox/pagecache/.htaccess'); - static::assertFileExists('sandbox/tmp/.htaccess'); - static::assertFileExists('sandbox/cache'); - static::assertFileExists('sandbox/cache/.htaccess'); - static::assertFileExists('sandbox/cache/1'); - static::assertFileExists('sandbox/cache/2'); - } - - /** - * Test clearing thumbnails cache - */ - public function testClearThumbnailsCache(): void - { - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) { - if ($key === 'resource.page_cache') { - return 'sandbox/pagecache'; - } elseif ($key === 'resource.raintpl_tmp') { - return 'sandbox/tmp'; - } elseif ($key === 'resource.thumbnails_cache') { - return 'sandbox/cache'; - } else { - return $default; - } - }); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->willReturnCallback(function (string $key, array $value): SessionManager { - static::assertSame(SessionManager::KEY_WARNING_MESSAGES, $key); - static::assertCount(1, $value); - static::assertStringStartsWith('Thumbnails cache has been cleared.', $value[0]); - - return $this->container->sessionManager; - }); - ; - - $request = $this->createMock(Request::class); - $request->method('getQueryParam')->with('type')->willReturn('thumbnails'); - $response = new Response(); - - $result = $this->controller->clearCache($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame('/subfolder/admin/server', (string) $result->getHeaderLine('Location')); - - static::assertFileNotExists('sandbox/cache/1'); - static::assertFileNotExists('sandbox/cache/2'); - - static::assertFileExists('sandbox/cache/.htaccess'); - static::assertFileExists('sandbox/pagecache'); - static::assertFileExists('sandbox/pagecache/.htaccess'); - static::assertFileExists('sandbox/pagecache/1'); - static::assertFileExists('sandbox/pagecache/2'); - static::assertFileExists('sandbox/tmp'); - static::assertFileExists('sandbox/tmp/.htaccess'); - static::assertFileExists('sandbox/tmp/1'); - static::assertFileExists('sandbox/tmp/2'); - } -} diff --git a/tests/front/controller/admin/SessionFilterControllerTest.php b/tests/front/controller/admin/SessionFilterControllerTest.php deleted file mode 100644 index 712a625b..00000000 --- a/tests/front/controller/admin/SessionFilterControllerTest.php +++ /dev/null @@ -1,177 +0,0 @@ -createContainer(); - - $this->controller = new SessionFilterController($this->container); - } - - /** - * Visibility - Default call for private filter while logged in without current value - */ - public function testVisibility(): void - { - $arg = ['visibility' => 'private']; - - $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc'; - - $this->container->loginManager->method('isLoggedIn')->willReturn(true); - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_VISIBILITY, 'private') - ; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $result = $this->controller->visibility($request, $response, $arg); - - static::assertInstanceOf(Response::class, $result); - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location')); - } - - /** - * Visibility - Toggle off private visibility - */ - public function testVisibilityToggleOff(): void - { - $arg = ['visibility' => 'private']; - - $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc'; - - $this->container->loginManager->method('isLoggedIn')->willReturn(true); - $this->container->sessionManager - ->method('getSessionParameter') - ->with(SessionManager::KEY_VISIBILITY) - ->willReturn('private') - ; - $this->container->sessionManager - ->expects(static::never()) - ->method('setSessionParameter') - ; - $this->container->sessionManager - ->expects(static::once()) - ->method('deleteSessionParameter') - ->with(SessionManager::KEY_VISIBILITY) - ; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $result = $this->controller->visibility($request, $response, $arg); - - static::assertInstanceOf(Response::class, $result); - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location')); - } - - /** - * Visibility - Change private to public - */ - public function testVisibilitySwitch(): void - { - $arg = ['visibility' => 'private']; - - $this->container->loginManager->method('isLoggedIn')->willReturn(true); - $this->container->sessionManager - ->method('getSessionParameter') - ->with(SessionManager::KEY_VISIBILITY) - ->willReturn('public') - ; - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_VISIBILITY, 'private') - ; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $result = $this->controller->visibility($request, $response, $arg); - - static::assertInstanceOf(Response::class, $result); - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Visibility - With invalid value - should remove any visibility setting - */ - public function testVisibilityInvalidValue(): void - { - $arg = ['visibility' => 'test']; - - $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc'; - - $this->container->loginManager->method('isLoggedIn')->willReturn(true); - $this->container->sessionManager - ->expects(static::never()) - ->method('setSessionParameter') - ; - $this->container->sessionManager - ->expects(static::once()) - ->method('deleteSessionParameter') - ->with(SessionManager::KEY_VISIBILITY) - ; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $result = $this->controller->visibility($request, $response, $arg); - - static::assertInstanceOf(Response::class, $result); - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location')); - } - - /** - * Visibility - Try to change visibility while logged out - */ - public function testVisibilityLoggedOut(): void - { - $arg = ['visibility' => 'test']; - - $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc'; - - $this->container->loginManager = $this->createMock(LoginManager::class); - $this->container->loginManager->method('isLoggedIn')->willReturn(false); - $this->container->sessionManager - ->expects(static::never()) - ->method('setSessionParameter') - ; - $this->container->sessionManager - ->expects(static::never()) - ->method('deleteSessionParameter') - ->with(SessionManager::KEY_VISIBILITY) - ; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $result = $this->controller->visibility($request, $response, $arg); - - static::assertInstanceOf(Response::class, $result); - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location')); - } -} diff --git a/tests/front/controller/admin/ShaareAddControllerTest.php b/tests/front/controller/admin/ShaareAddControllerTest.php deleted file mode 100644 index a27ebe64..00000000 --- a/tests/front/controller/admin/ShaareAddControllerTest.php +++ /dev/null @@ -1,97 +0,0 @@ -createContainer(); - - $this->container->httpAccess = $this->createMock(HttpAccess::class); - $this->controller = new ShaareAddController($this->container); - } - - /** - * Test displaying add link page - */ - public function testAddShaare(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $expectedTags = [ - 'tag1' => 32, - 'tag2' => 24, - 'tag3' => 1, - ]; - $this->container->bookmarkService - ->expects(static::once()) - ->method('bookmarksCountPerTag') - ->willReturn($expectedTags) - ; - $expectedTags = array_merge($expectedTags, [BookmarkMarkdownFormatter::NO_MD_TAG => 1]); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) { - return $key === 'formatter' ? 'markdown' : $default; - }); - - $result = $this->controller->addShaare($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('addlink', (string) $result->getBody()); - - static::assertSame('Shaare a new link - Shaarli', $assignedVariables['pagetitle']); - static::assertFalse($assignedVariables['default_private_links']); - static::assertTrue($assignedVariables['async_metadata']); - static::assertSame($expectedTags, $assignedVariables['tags']); - } - - /** - * Test displaying add link page - */ - public function testAddShaareWithoutMd(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $expectedTags = [ - 'tag1' => 32, - 'tag2' => 24, - 'tag3' => 1, - ]; - $this->container->bookmarkService - ->expects(static::once()) - ->method('bookmarksCountPerTag') - ->willReturn($expectedTags) - ; - - $result = $this->controller->addShaare($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('addlink', (string) $result->getBody()); - - static::assertSame($expectedTags, $assignedVariables['tags']); - } -} diff --git a/tests/front/controller/admin/ShaareManageControllerTest/AddOrDeleteTagTest.php b/tests/front/controller/admin/ShaareManageControllerTest/AddOrDeleteTagTest.php deleted file mode 100644 index cd76491d..00000000 --- a/tests/front/controller/admin/ShaareManageControllerTest/AddOrDeleteTagTest.php +++ /dev/null @@ -1,380 +0,0 @@ -createContainer(); - - $this->container->httpAccess = $this->createMock(HttpAccess::class); - $this->controller = new ShaareManageController($this->container); - } - - /** - * Add 1 tag to 1 bookmark - */ - public function testAddOneTagOnOneBookmark(): void - { - $parameters = ['id' => '123', 'tag' => 'newtag', 'action' => 'add']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - $bookmark = (new Bookmark()) - ->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123') - ->setTagsString('first second'); - - static::assertSame(['first', 'second'], $bookmark->getTags()); - - $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark); - $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false); - $this->container->bookmarkService->expects(static::once())->method('save'); - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->with('raw') - ->willReturnCallback(function () use ($bookmark): BookmarkFormatter { - return new BookmarkRawFormatter($this->container->conf, true); - }) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::once()) - ->method('executeHooks') - ->with('save_link') - ; - - $result = $this->controller->addOrDeleteTags($request, $response); - - static::assertSame(['first', 'second', 'newtag'], $bookmark->getTags()); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Add 2 tags to 2 bookmarks - */ - public function testAddTwoTagsOnTwoBookmarks(): void - { - $parameters = ['id' => '123 456', 'tag' => 'newtag@othertag', 'action' => 'add']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - $bookmark1 = (new Bookmark()) - ->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123') - ->setTagsString('first second'); - $bookmark2 = (new Bookmark()) - ->setId(456)->setUrl('http://domain.tld')->setTitle('Title 123'); - - static::assertSame(['first', 'second'], $bookmark1->getTags()); - static::assertSame([], $bookmark2->getTags()); - - $this->container->bookmarkService->expects(static::exactly(2))->method('get') - ->withConsecutive([123], [456]) - ->willReturnOnConsecutiveCalls($bookmark1, $bookmark2); - $this->container->bookmarkService->expects(static::exactly(2))->method('set') - ->withConsecutive([$bookmark1, false], [$bookmark2, false]); - $this->container->bookmarkService->expects(static::once())->method('save'); - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->with('raw') - ->willReturnCallback(function (): BookmarkFormatter { - return new BookmarkRawFormatter($this->container->conf, true); - }) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::exactly(2)) - ->method('executeHooks') - ->with('save_link') - ; - - $result = $this->controller->addOrDeleteTags($request, $response); - - static::assertSame(['first', 'second', 'newtag', 'othertag'], $bookmark1->getTags()); - static::assertSame(['newtag', 'othertag'], $bookmark2->getTags()); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Delete 1 tag to 1 bookmark - */ - public function testDeleteOneTagOnOneBookmark(): void - { - $parameters = ['id' => '123', 'tag' => 'second', 'action' => 'delete']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - $bookmark = (new Bookmark()) - ->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123') - ->setTagsString('first second third'); - - static::assertSame(['first', 'second', 'third'], $bookmark->getTags()); - - $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark); - $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false); - $this->container->bookmarkService->expects(static::once())->method('save'); - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->with('raw') - ->willReturnCallback(function () use ($bookmark): BookmarkFormatter { - return new BookmarkRawFormatter($this->container->conf, true); - }) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::once()) - ->method('executeHooks') - ->with('save_link') - ; - - $result = $this->controller->addOrDeleteTags($request, $response); - - static::assertSame(['first', 'third'], $bookmark->getTags()); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Delete 2 tags to 2 bookmarks - */ - public function testDeleteTwoTagOnTwoBookmarks(): void - { - $parameters = ['id' => '123 456', 'tag' => 'second@first', 'action' => 'delete']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - $bookmark1 = (new Bookmark()) - ->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123') - ->setTagsString('first second third other'); - $bookmark2 = (new Bookmark()) - ->setId(456)->setUrl('http://domain.tld')->setTitle('Title 123') - ->setTagsString('first second'); - - static::assertSame(['first', 'second', 'third', 'other'], $bookmark1->getTags()); - static::assertSame(['first', 'second'], $bookmark2->getTags()); - - $this->container->bookmarkService->expects(static::exactly(2))->method('get') - ->withConsecutive([123], [456]) - ->willReturnOnConsecutiveCalls($bookmark1, $bookmark2); - $this->container->bookmarkService->expects(static::exactly(2))->method('set') - ->withConsecutive([$bookmark1, false], [$bookmark2, false]); - $this->container->bookmarkService->expects(static::once())->method('save'); - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->with('raw') - ->willReturnCallback(function (): BookmarkFormatter { - return new BookmarkRawFormatter($this->container->conf, true); - }) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::exactly(2)) - ->method('executeHooks') - ->with('save_link') - ; - - $result = $this->controller->addOrDeleteTags($request, $response); - - static::assertSame(['third', 'other'], $bookmark1->getTags()); - static::assertSame([], $bookmark2->getTags()); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Test add a tag without passing an ID. - */ - public function testAddTagWithoutId(): void - { - $parameters = ['tag' => 'newtag', 'action' => 'add']; - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.']) - ; - - $result = $this->controller->addOrDeleteTags($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Test add a tag without passing an ID. - */ - public function testDeleteTagWithoutId(): void - { - $parameters = ['tag' => 'newtag', 'action' => 'delete']; - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.']) - ; - - $result = $this->controller->addOrDeleteTags($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Test add a tag without passing an action. - */ - public function testAddTagWithoutAction(): void - { - $parameters = ['id' => '123', 'tag' => 'newtag']; - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid action provided.']) - ; - - $result = $this->controller->addOrDeleteTags($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Test add a tag without passing a tag string value. - */ - public function testAddTagWithoutValue(): void - { - $parameters = ['id' => '123', 'tag' => '', 'action' => 'add']; - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid tag name provided.']) - ; - - $result = $this->controller->addOrDeleteTags($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Test delete a tag without passing a tag string value. - */ - public function testDeleteTagWithoutValue(): void - { - $parameters = ['id' => '123', 'tag' => '', 'action' => 'delete']; - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid tag name provided.']) - ; - - $result = $this->controller->addOrDeleteTags($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } -} diff --git a/tests/front/controller/admin/ShaareManageControllerTest/ChangeVisibilityBookmarkTest.php b/tests/front/controller/admin/ShaareManageControllerTest/ChangeVisibilityBookmarkTest.php deleted file mode 100644 index 28b1c023..00000000 --- a/tests/front/controller/admin/ShaareManageControllerTest/ChangeVisibilityBookmarkTest.php +++ /dev/null @@ -1,418 +0,0 @@ -createContainer(); - - $this->container->httpAccess = $this->createMock(HttpAccess::class); - $this->controller = new ShaareManageController($this->container); - } - - /** - * Change bookmark visibility - Set private - Single public bookmark with valid parameters - */ - public function testSetSingleBookmarkPrivate(): void - { - $parameters = ['id' => '123', 'newVisibility' => 'private']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $bookmark = (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(false); - - static::assertFalse($bookmark->isPrivate()); - - $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark); - $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false); - $this->container->bookmarkService->expects(static::once())->method('save'); - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->with('raw') - ->willReturnCallback(function () use ($bookmark): BookmarkFormatter { - return new BookmarkRawFormatter($this->container->conf, true); - }) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::once()) - ->method('executeHooks') - ->with('save_link') - ; - - $result = $this->controller->changeVisibility($request, $response); - - static::assertTrue($bookmark->isPrivate()); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Change bookmark visibility - Set public - Single private bookmark with valid parameters - */ - public function testSetSingleBookmarkPublic(): void - { - $parameters = ['id' => '123', 'newVisibility' => 'public']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $bookmark = (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(true); - - static::assertTrue($bookmark->isPrivate()); - - $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark); - $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false); - $this->container->bookmarkService->expects(static::once())->method('save'); - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->with('raw') - ->willReturn(new BookmarkRawFormatter($this->container->conf, true)) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::once()) - ->method('executeHooks') - ->with('save_link') - ; - - $result = $this->controller->changeVisibility($request, $response); - - static::assertFalse($bookmark->isPrivate()); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Change bookmark visibility - Set private on single already private bookmark - */ - public function testSetSinglePrivateBookmarkPrivate(): void - { - $parameters = ['id' => '123', 'newVisibility' => 'private']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $bookmark = (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(true); - - static::assertTrue($bookmark->isPrivate()); - - $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark); - $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false); - $this->container->bookmarkService->expects(static::once())->method('save'); - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->with('raw') - ->willReturn(new BookmarkRawFormatter($this->container->conf, true)) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::once()) - ->method('executeHooks') - ->with('save_link') - ; - - $result = $this->controller->changeVisibility($request, $response); - - static::assertTrue($bookmark->isPrivate()); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Change bookmark visibility - Set multiple bookmarks private - */ - public function testSetMultipleBookmarksPrivate(): void - { - $parameters = ['id' => '123 456 789', 'newVisibility' => 'private']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $bookmarks = [ - (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(false), - (new Bookmark())->setId(456)->setUrl('http://domain.tld')->setTitle('Title 456')->setPrivate(true), - (new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789')->setPrivate(false), - ]; - - $this->container->bookmarkService - ->expects(static::exactly(3)) - ->method('get') - ->withConsecutive([123], [456], [789]) - ->willReturnOnConsecutiveCalls(...$bookmarks) - ; - $this->container->bookmarkService - ->expects(static::exactly(3)) - ->method('set') - ->withConsecutive(...array_map(function (Bookmark $bookmark): array { - return [$bookmark, false]; - }, $bookmarks)) - ; - $this->container->bookmarkService->expects(static::once())->method('save'); - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->with('raw') - ->willReturn(new BookmarkRawFormatter($this->container->conf, true)) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::exactly(3)) - ->method('executeHooks') - ->with('save_link') - ; - - $result = $this->controller->changeVisibility($request, $response); - - static::assertTrue($bookmarks[0]->isPrivate()); - static::assertTrue($bookmarks[1]->isPrivate()); - static::assertTrue($bookmarks[2]->isPrivate()); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Change bookmark visibility - Single bookmark not found. - */ - public function testChangeVisibilitySingleBookmarkNotFound(): void - { - $parameters = ['id' => '123', 'newVisibility' => 'private']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('get') - ->willThrowException(new BookmarkNotFoundException()) - ; - $this->container->bookmarkService->expects(static::never())->method('set'); - $this->container->bookmarkService->expects(static::never())->method('save'); - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->with('raw') - ->willReturn(new BookmarkRawFormatter($this->container->conf, true)) - ; - - // Make sure that PluginManager hook is not triggered - $this->container->pluginManager - ->expects(static::never()) - ->method('executeHooks') - ->with('save_link') - ; - - $result = $this->controller->changeVisibility($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Change bookmark visibility - Multiple bookmarks with one not found. - */ - public function testChangeVisibilityMultipleBookmarksOneNotFound(): void - { - $parameters = ['id' => '123 456 789', 'newVisibility' => 'public']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $bookmarks = [ - (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(true), - (new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789')->setPrivate(false), - ]; - - $this->container->bookmarkService - ->expects(static::exactly(3)) - ->method('get') - ->withConsecutive([123], [456], [789]) - ->willReturnCallback(function (int $id) use ($bookmarks): Bookmark { - if ($id === 123) { - return $bookmarks[0]; - } - if ($id === 789) { - return $bookmarks[1]; - } - throw new BookmarkNotFoundException(); - }) - ; - $this->container->bookmarkService - ->expects(static::exactly(2)) - ->method('set') - ->withConsecutive(...array_map(function (Bookmark $bookmark): array { - return [$bookmark, false]; - }, $bookmarks)) - ; - $this->container->bookmarkService->expects(static::once())->method('save'); - - // Make sure that PluginManager hook is not triggered - $this->container->pluginManager - ->expects(static::exactly(2)) - ->method('executeHooks') - ->with('save_link') - ; - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 456 could not be found.']) - ; - - $result = $this->controller->changeVisibility($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Change bookmark visibility - Invalid ID - */ - public function testChangeVisibilityInvalidId(): void - { - $parameters = ['id' => 'nope not an ID', 'newVisibility' => 'private']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.']) - ; - - $result = $this->controller->changeVisibility($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Change bookmark visibility - Empty ID - */ - public function testChangeVisibilityEmptyId(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.']) - ; - - $result = $this->controller->changeVisibility($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Change bookmark visibility - with invalid visibility - */ - public function testChangeVisibilityWithInvalidVisibility(): void - { - $parameters = ['id' => '123', 'newVisibility' => 'invalid']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid visibility provided.']) - ; - - $result = $this->controller->changeVisibility($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } -} diff --git a/tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php b/tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php deleted file mode 100644 index 42d0c0d6..00000000 --- a/tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php +++ /dev/null @@ -1,427 +0,0 @@ -createContainer(); - - $this->container->httpAccess = $this->createMock(HttpAccess::class); - $this->controller = new ShaareManageController($this->container); - } - - /** - * Delete bookmark - Single bookmark with valid parameters - */ - public function testDeleteSingleBookmark(): void - { - $parameters = ['id' => '123']; - - $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/shaare/abcdef'; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $bookmark = (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123'); - - $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark); - $this->container->bookmarkService->expects(static::once())->method('remove')->with($bookmark, false); - $this->container->bookmarkService->expects(static::once())->method('save'); - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->with('raw') - ->willReturnCallback(function () use ($bookmark): BookmarkFormatter { - $formatter = $this->createMock(BookmarkFormatter::class); - $formatter - ->expects(static::once()) - ->method('format') - ->with($bookmark) - ->willReturn(['formatted' => $bookmark]) - ; - - return $formatter; - }) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::once()) - ->method('executeHooks') - ->with('delete_link', ['formatted' => $bookmark]) - ; - - $result = $this->controller->deleteBookmark($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Delete bookmark - Multiple bookmarks with valid parameters - */ - public function testDeleteMultipleBookmarks(): void - { - $parameters = ['id' => '123 456 789']; - - $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/?searchtags=abcdef'; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $bookmarks = [ - (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123'), - (new Bookmark())->setId(456)->setUrl('http://domain.tld')->setTitle('Title 456'), - (new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789'), - ]; - - $this->container->bookmarkService - ->expects(static::exactly(3)) - ->method('get') - ->withConsecutive([123], [456], [789]) - ->willReturnOnConsecutiveCalls(...$bookmarks) - ; - $this->container->bookmarkService - ->expects(static::exactly(3)) - ->method('remove') - ->withConsecutive(...array_map(function (Bookmark $bookmark): array { - return [$bookmark, false]; - }, $bookmarks)) - ; - $this->container->bookmarkService->expects(static::once())->method('save'); - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->with('raw') - ->willReturnCallback(function () use ($bookmarks): BookmarkFormatter { - $formatter = $this->createMock(BookmarkFormatter::class); - - $formatter - ->expects(static::exactly(3)) - ->method('format') - ->withConsecutive(...array_map(function (Bookmark $bookmark): array { - return [$bookmark]; - }, $bookmarks)) - ->willReturnOnConsecutiveCalls(...array_map(function (Bookmark $bookmark): array { - return ['formatted' => $bookmark]; - }, $bookmarks)) - ; - - return $formatter; - }) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::exactly(3)) - ->method('executeHooks') - ->with('delete_link') - ; - - $result = $this->controller->deleteBookmark($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/?searchtags=abcdef'], $result->getHeader('location')); - } - - /** - * Delete bookmark - Single bookmark not found in the data store - */ - public function testDeleteSingleBookmarkNotFound(): void - { - $parameters = ['id' => '123']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('get') - ->willThrowException(new BookmarkNotFoundException()) - ; - $this->container->bookmarkService->expects(static::never())->method('remove'); - $this->container->bookmarkService->expects(static::never())->method('save'); - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->with('raw') - ->willReturnCallback(function (): BookmarkFormatter { - $formatter = $this->createMock(BookmarkFormatter::class); - - $formatter->expects(static::never())->method('format'); - - return $formatter; - }) - ; - // Make sure that PluginManager hook is not triggered - $this->container->pluginManager - ->expects(static::never()) - ->method('executeHooks') - ->with('delete_link') - ; - - $result = $this->controller->deleteBookmark($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Delete bookmark - Multiple bookmarks with one not found in the data store - */ - public function testDeleteMultipleBookmarksOneNotFound(): void - { - $parameters = ['id' => '123 456 789']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $bookmarks = [ - (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123'), - (new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789'), - ]; - - $this->container->bookmarkService - ->expects(static::exactly(3)) - ->method('get') - ->withConsecutive([123], [456], [789]) - ->willReturnCallback(function (int $id) use ($bookmarks): Bookmark { - if ($id === 123) { - return $bookmarks[0]; - } - if ($id === 789) { - return $bookmarks[1]; - } - throw new BookmarkNotFoundException(); - }) - ; - $this->container->bookmarkService - ->expects(static::exactly(2)) - ->method('remove') - ->withConsecutive(...array_map(function (Bookmark $bookmark): array { - return [$bookmark, false]; - }, $bookmarks)) - ; - $this->container->bookmarkService->expects(static::once())->method('save'); - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->with('raw') - ->willReturnCallback(function () use ($bookmarks): BookmarkFormatter { - $formatter = $this->createMock(BookmarkFormatter::class); - - $formatter - ->expects(static::exactly(2)) - ->method('format') - ->withConsecutive(...array_map(function (Bookmark $bookmark): array { - return [$bookmark]; - }, $bookmarks)) - ->willReturnOnConsecutiveCalls(...array_map(function (Bookmark $bookmark): array { - return ['formatted' => $bookmark]; - }, $bookmarks)) - ; - - return $formatter; - }) - ; - - // Make sure that PluginManager hook is not triggered - $this->container->pluginManager - ->expects(static::exactly(2)) - ->method('executeHooks') - ->with('delete_link') - ; - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 456 could not be found.']) - ; - - $result = $this->controller->deleteBookmark($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Delete bookmark - Invalid ID - */ - public function testDeleteInvalidId(): void - { - $parameters = ['id' => 'nope not an ID']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.']) - ; - - $result = $this->controller->deleteBookmark($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Delete bookmark - Empty ID - */ - public function testDeleteEmptyId(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.']) - ; - - $result = $this->controller->deleteBookmark($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Delete bookmark - from bookmarklet - */ - public function testDeleteBookmarkFromBookmarklet(): void - { - $parameters = [ - 'id' => '123', - 'source' => 'bookmarklet', - ]; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->bookmarkService->method('get')->with('123')->willReturn( - (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123') - ); - $this->container->bookmarkService->expects(static::once())->method('remove'); - - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->willReturnCallback(function (): BookmarkFormatter { - $formatter = $this->createMock(BookmarkFormatter::class); - $formatter->method('format')->willReturn(['formatted']); - - return $formatter; - }) - ; - - $result = $this->controller->deleteBookmark($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('', (string) $result->getBody()); - } - - /** - * Delete bookmark - from batch view - */ - public function testDeleteBookmarkFromBatch(): void - { - $parameters = [ - 'id' => '123', - 'source' => 'batch', - ]; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->bookmarkService->method('get')->with('123')->willReturn( - (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123') - ); - $this->container->bookmarkService->expects(static::once())->method('remove'); - - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->expects(static::once()) - ->method('getFormatter') - ->willReturnCallback(function (): BookmarkFormatter { - $formatter = $this->createMock(BookmarkFormatter::class); - $formatter->method('format')->willReturn(['formatted']); - - return $formatter; - }) - ; - - $result = $this->controller->deleteBookmark($request, $response); - - static::assertSame(204, $result->getStatusCode()); - static::assertEmpty((string) $result->getBody()); - } -} diff --git a/tests/front/controller/admin/ShaareManageControllerTest/PinBookmarkTest.php b/tests/front/controller/admin/ShaareManageControllerTest/PinBookmarkTest.php deleted file mode 100644 index b89206ce..00000000 --- a/tests/front/controller/admin/ShaareManageControllerTest/PinBookmarkTest.php +++ /dev/null @@ -1,145 +0,0 @@ -createContainer(); - - $this->container->httpAccess = $this->createMock(HttpAccess::class); - $this->controller = new ShaareManageController($this->container); - } - - /** - * Test pin bookmark - with valid input - * - * @dataProvider initialStickyValuesProvider() - */ - public function testPinBookmarkIsStickyNull(?bool $sticky, bool $expectedValue): void - { - $id = 123; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $bookmark = (new Bookmark()) - ->setId(123) - ->setUrl('http://domain.tld') - ->setTitle('Title 123') - ->setSticky($sticky) - ; - - $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark); - $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, true); - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::once()) - ->method('executeHooks') - ->with('save_link') - ; - - $result = $this->controller->pinBookmark($request, $response, ['id' => (string) $id]); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - - static::assertSame($expectedValue, $bookmark->isSticky()); - } - - public function initialStickyValuesProvider(): array - { - // [initialStickyState, isStickyAfterPin] - return [[null, true], [false, true], [true, false]]; - } - - /** - * Test pin bookmark - invalid bookmark ID - */ - public function testDisplayEditFormInvalidId(): void - { - $id = 'invalid'; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier invalid could not be found.']) - ; - - $result = $this->controller->pinBookmark($request, $response, ['id' => $id]); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Test pin bookmark - Bookmark ID not provided - */ - public function testDisplayEditFormIdNotProvided(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier could not be found.']) - ; - - $result = $this->controller->pinBookmark($request, $response, []); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Test pin bookmark - bookmark not found - */ - public function testDisplayEditFormBookmarkNotFound(): void - { - $id = 123; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('get') - ->with($id) - ->willThrowException(new BookmarkNotFoundException()) - ; - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 123 could not be found.']) - ; - - $result = $this->controller->pinBookmark($request, $response, ['id' => (string) $id]); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } -} diff --git a/tests/front/controller/admin/ShaareManageControllerTest/SharePrivateTest.php b/tests/front/controller/admin/ShaareManageControllerTest/SharePrivateTest.php deleted file mode 100644 index deb8b50e..00000000 --- a/tests/front/controller/admin/ShaareManageControllerTest/SharePrivateTest.php +++ /dev/null @@ -1,139 +0,0 @@ -createContainer(); - - $this->container->httpAccess = $this->createMock(HttpAccess::class); - $this->controller = new ShaareManageController($this->container); - } - - /** - * Test shaare private with a private bookmark which does not have a key yet. - */ - public function testSharePrivateWithNewPrivateBookmark(): void - { - $hash = 'abcdcef'; - $request = $this->createMock(Request::class); - $response = new Response(); - - $bookmark = (new Bookmark()) - ->setId(123) - ->setUrl('http://domain.tld') - ->setTitle('Title 123') - ->setPrivate(true) - ; - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByHash') - ->with($hash) - ->willReturn($bookmark) - ; - $this->container->bookmarkService - ->expects(static::once()) - ->method('set') - ->with($bookmark, true) - ->willReturnCallback(function (Bookmark $bookmark): Bookmark { - static::assertSame(32, strlen($bookmark->getAdditionalContentEntry('private_key'))); - - return $bookmark; - }) - ; - - $result = $this->controller->sharePrivate($request, $response, ['hash' => $hash]); - - static::assertSame(302, $result->getStatusCode()); - static::assertRegExp('#/subfolder/shaare/' . $hash . '\?key=\w{32}#', $result->getHeaderLine('Location')); - } - - /** - * Test shaare private with a private bookmark which does already have a key. - */ - public function testSharePrivateWithExistingPrivateBookmark(): void - { - $hash = 'abcdcef'; - $existingKey = 'this is a private key'; - $request = $this->createMock(Request::class); - $response = new Response(); - - $bookmark = (new Bookmark()) - ->setId(123) - ->setUrl('http://domain.tld') - ->setTitle('Title 123') - ->setPrivate(true) - ->setAdditionalContentEntry('private_key', $existingKey) - ; - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByHash') - ->with($hash) - ->willReturn($bookmark) - ; - $this->container->bookmarkService - ->expects(static::never()) - ->method('set') - ; - - $result = $this->controller->sharePrivate($request, $response, ['hash' => $hash]); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame('/subfolder/shaare/' . $hash . '?key=' . $existingKey, $result->getHeaderLine('Location')); - } - - /** - * Test shaare private with a public bookmark. - */ - public function testSharePrivateWithPublicBookmark(): void - { - $hash = 'abcdcef'; - $request = $this->createMock(Request::class); - $response = new Response(); - - $bookmark = (new Bookmark()) - ->setId(123) - ->setUrl('http://domain.tld') - ->setTitle('Title 123') - ->setPrivate(false) - ; - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByHash') - ->with($hash) - ->willReturn($bookmark) - ; - $this->container->bookmarkService - ->expects(static::never()) - ->method('set') - ; - - $result = $this->controller->sharePrivate($request, $response, ['hash' => $hash]); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame('/subfolder/shaare/' . $hash, $result->getHeaderLine('Location')); - } -} diff --git a/tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateBatchFormTest.php b/tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateBatchFormTest.php deleted file mode 100644 index ce8e112b..00000000 --- a/tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateBatchFormTest.php +++ /dev/null @@ -1,63 +0,0 @@ -createContainer(); - - $this->container->httpAccess = $this->createMock(HttpAccess::class); - $this->container->metadataRetriever = $this->createMock(MetadataRetriever::class); - $this->controller = new ShaarePublishController($this->container); - } - - /** - * TODO - */ - public function testDisplayCreateFormBatch(): void - { - $urls = [ - 'https://domain1.tld/url1', - 'https://domain2.tld/url2', - ' ', - 'https://domain3.tld/url3', - ]; - - $request = $this->createMock(Request::class); - $request->method('getParam')->willReturnCallback(function (string $key) use ($urls): ?string { - return $key === 'urls' ? implode(PHP_EOL, $urls) : null; - }); - $response = new Response(); - - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $result = $this->controller->displayCreateBatchForms($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('editlink.batch', (string) $result->getBody()); - - static::assertTrue($assignedVariables['batch_mode']); - static::assertCount(3, $assignedVariables['links']); - static::assertSame($urls[0], $assignedVariables['links'][0]['link']['url']); - static::assertSame($urls[1], $assignedVariables['links'][1]['link']['url']); - static::assertSame($urls[3], $assignedVariables['links'][2]['link']['url']); - } -} diff --git a/tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateFormTest.php b/tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateFormTest.php deleted file mode 100644 index 964773da..00000000 --- a/tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateFormTest.php +++ /dev/null @@ -1,367 +0,0 @@ -createContainer(); - - $this->container->httpAccess = $this->createMock(HttpAccess::class); - $this->container->metadataRetriever = $this->createMock(MetadataRetriever::class); - $this->controller = new ShaarePublishController($this->container); - } - - /** - * Test displaying bookmark create form - * Ensure that every step of the standard workflow works properly. - */ - public function testDisplayCreateFormWithUrlAndWithMetadataRetrieval(): void - { - $this->container->environment = [ - 'HTTP_REFERER' => $referer = 'http://shaarli/subfolder/controller/?searchtag=abc' - ]; - - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $url = 'http://url.tld/other?part=3&utm_ad=pay#hash'; - $expectedUrl = str_replace('&utm_ad=pay', '', $url); - $remoteTitle = 'Remote Title'; - $remoteDesc = 'Sometimes the meta description is relevant.'; - $remoteTags = 'abc def'; - - $request = $this->createMock(Request::class); - $request->method('getParam')->willReturnCallback(function (string $key) use ($url): ?string { - return $key === 'post' ? $url : null; - }); - $response = new Response(); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('get')->willReturnCallback(function (string $param, $default) { - if ($param === 'general.enable_async_metadata') { - return false; - } - - return $default; - }); - - $this->container->metadataRetriever->expects(static::once())->method('retrieve')->willReturn([ - 'title' => $remoteTitle, - 'description' => $remoteDesc, - 'tags' => $remoteTags, - ]); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('bookmarksCountPerTag') - ->willReturn($tags = ['tag1' => 2, 'tag2' => 1]) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::atLeastOnce()) - ->method('executeHooks') - ->withConsecutive(['render_editlink'], ['render_includes']) - ->willReturnCallback(function (string $hook, array $data) use ($remoteTitle, $remoteDesc): array { - if ('render_editlink' === $hook) { - static::assertSame($remoteTitle, $data['link']['title']); - static::assertSame($remoteDesc, $data['link']['description']); - } - - return $data; - }) - ; - - $result = $this->controller->displayCreateForm($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('editlink', (string) $result->getBody()); - - static::assertSame('Shaare - Shaarli', $assignedVariables['pagetitle']); - - static::assertSame($expectedUrl, $assignedVariables['link']['url']); - static::assertSame($remoteTitle, $assignedVariables['link']['title']); - static::assertSame($remoteDesc, $assignedVariables['link']['description']); - static::assertSame($remoteTags . ' ', $assignedVariables['link']['tags']); - static::assertFalse($assignedVariables['link']['private']); - - static::assertTrue($assignedVariables['link_is_new']); - static::assertSame($referer, $assignedVariables['http_referer']); - static::assertSame($tags, $assignedVariables['tags']); - static::assertArrayHasKey('source', $assignedVariables); - static::assertArrayHasKey('default_private_links', $assignedVariables); - static::assertArrayHasKey('async_metadata', $assignedVariables); - static::assertArrayHasKey('retrieve_description', $assignedVariables); - } - - /** - * Test displaying bookmark create form without any external metadata retrieval attempt - */ - public function testDisplayCreateFormWithUrlAndWithoutMetadata(): void - { - $this->container->environment = [ - 'HTTP_REFERER' => $referer = 'http://shaarli/subfolder/controller/?searchtag=abc' - ]; - - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $url = 'http://url.tld/other?part=3&utm_ad=pay#hash'; - $expectedUrl = str_replace('&utm_ad=pay', '', $url); - - $request = $this->createMock(Request::class); - $request->method('getParam')->willReturnCallback(function (string $key) use ($url): ?string { - return $key === 'post' ? $url : null; - }); - $response = new Response(); - - $this->container->metadataRetriever->expects(static::never())->method('retrieve'); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('bookmarksCountPerTag') - ->willReturn($tags = ['tag1' => 2, 'tag2' => 1]) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::atLeastOnce()) - ->method('executeHooks') - ->withConsecutive(['render_editlink'], ['render_includes']) - ->willReturnCallback(function (string $hook, array $data): array { - if ('render_editlink' === $hook) { - static::assertSame('', $data['link']['title']); - static::assertSame('', $data['link']['description']); - } - - return $data; - }) - ; - - $result = $this->controller->displayCreateForm($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('editlink', (string) $result->getBody()); - - static::assertSame('Shaare - Shaarli', $assignedVariables['pagetitle']); - - static::assertSame($expectedUrl, $assignedVariables['link']['url']); - static::assertSame('', $assignedVariables['link']['title']); - static::assertSame('', $assignedVariables['link']['description']); - static::assertSame('', $assignedVariables['link']['tags']); - static::assertFalse($assignedVariables['link']['private']); - - static::assertTrue($assignedVariables['link_is_new']); - static::assertSame($referer, $assignedVariables['http_referer']); - static::assertSame($tags, $assignedVariables['tags']); - static::assertArrayHasKey('source', $assignedVariables); - static::assertArrayHasKey('default_private_links', $assignedVariables); - static::assertArrayHasKey('async_metadata', $assignedVariables); - static::assertArrayHasKey('retrieve_description', $assignedVariables); - } - - /** - * Test displaying bookmark create form - * Ensure all available query parameters are handled properly. - */ - public function testDisplayCreateFormWithFullParameters(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $parameters = [ - 'post' => 'http://url.tld/other?part=3&utm_ad=pay#hash', - 'title' => 'Provided Title', - 'description' => 'Provided description.', - 'tags' => 'abc@def', - 'private' => '1', - 'source' => 'apps', - ]; - $expectedUrl = str_replace('&utm_ad=pay', '', $parameters['post']); - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }); - $response = new Response(); - - $result = $this->controller->displayCreateForm($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('editlink', (string) $result->getBody()); - - static::assertSame('Shaare - Shaarli', $assignedVariables['pagetitle']); - - static::assertSame($expectedUrl, $assignedVariables['link']['url']); - static::assertSame($parameters['title'], $assignedVariables['link']['title']); - static::assertSame($parameters['description'], $assignedVariables['link']['description']); - static::assertSame($parameters['tags'] . '@', $assignedVariables['link']['tags']); - static::assertTrue($assignedVariables['link']['private']); - static::assertTrue($assignedVariables['link_is_new']); - static::assertSame($parameters['source'], $assignedVariables['source']); - } - - /** - * Test displaying bookmark create form - * Without any parameter. - */ - public function testDisplayCreateFormEmpty(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->httpAccess->expects(static::never())->method('getHttpResponse'); - $this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback'); - - $result = $this->controller->displayCreateForm($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('editlink', (string) $result->getBody()); - static::assertSame('', $assignedVariables['link']['url']); - static::assertSame('Note: ', $assignedVariables['link']['title']); - static::assertSame('', $assignedVariables['link']['description']); - static::assertSame('', $assignedVariables['link']['tags']); - static::assertFalse($assignedVariables['link']['private']); - static::assertTrue($assignedVariables['link_is_new']); - } - - /** - * Test displaying bookmark create form - * URL not using HTTP protocol: do not try to retrieve the title - */ - public function testDisplayCreateFormNotHttp(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $url = 'magnet://kubuntu.torrent'; - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($url): ?string { - return $key === 'post' ? $url : null; - }); - $response = new Response(); - - $this->container->httpAccess->expects(static::never())->method('getHttpResponse'); - $this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback'); - - $result = $this->controller->displayCreateForm($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('editlink', (string) $result->getBody()); - static::assertSame($url, $assignedVariables['link']['url']); - static::assertTrue($assignedVariables['link_is_new']); - } - - /** - * Test displaying bookmark create form - * When markdown formatter is enabled, the no markdown tag should be added to existing tags. - */ - public function testDisplayCreateFormWithMarkdownEnabled(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf - ->expects(static::atLeastOnce()) - ->method('get')->willReturnCallback(function (string $key): ?string { - if ($key === 'formatter') { - return 'markdown'; - } - - return $key; - }) - ; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $result = $this->controller->displayCreateForm($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('editlink', (string) $result->getBody()); - static::assertSame(['nomarkdown' => 1], $assignedVariables['tags']); - } - - /** - * Test displaying bookmark create form - * When an existing URL is submitted, we want to edit the existing link. - */ - public function testDisplayCreateFormWithExistingUrl(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $url = 'http://url.tld/other?part=3&utm_ad=pay#hash'; - $expectedUrl = str_replace('&utm_ad=pay', '', $url); - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($url): ?string { - return $key === 'post' ? $url : null; - }); - $response = new Response(); - - $this->container->httpAccess->expects(static::never())->method('getHttpResponse'); - $this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback'); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByUrl') - ->with($expectedUrl) - ->willReturn( - (new Bookmark()) - ->setId($id = 23) - ->setUrl($expectedUrl) - ->setTitle($title = 'Bookmark Title') - ->setDescription($description = 'Bookmark description.') - ->setTags($tags = ['abc', 'def']) - ->setPrivate(true) - ->setCreated($createdAt = new \DateTime('2020-06-10 18:45:44')) - ) - ; - - $result = $this->controller->displayCreateForm($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('editlink', (string) $result->getBody()); - - static::assertSame('Edit Shaare - Shaarli', $assignedVariables['pagetitle']); - static::assertFalse($assignedVariables['link_is_new']); - - static::assertSame($id, $assignedVariables['link']['id']); - static::assertSame($expectedUrl, $assignedVariables['link']['url']); - static::assertSame($title, $assignedVariables['link']['title']); - static::assertSame($description, $assignedVariables['link']['description']); - static::assertSame(implode('@', $tags) . '@', $assignedVariables['link']['tags']); - static::assertTrue($assignedVariables['link']['private']); - static::assertSame($createdAt, $assignedVariables['link']['created']); - } -} diff --git a/tests/front/controller/admin/ShaarePublishControllerTest/DisplayEditFormTest.php b/tests/front/controller/admin/ShaarePublishControllerTest/DisplayEditFormTest.php deleted file mode 100644 index 738cea12..00000000 --- a/tests/front/controller/admin/ShaarePublishControllerTest/DisplayEditFormTest.php +++ /dev/null @@ -1,155 +0,0 @@ -createContainer(); - - $this->container->httpAccess = $this->createMock(HttpAccess::class); - $this->controller = new ShaarePublishController($this->container); - } - - /** - * Test displaying bookmark edit form - * When an existing ID is provided, ensure that default workflow works properly. - */ - public function testDisplayEditFormDefault(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $id = 11; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->httpAccess->expects(static::never())->method('getHttpResponse'); - $this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback'); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('get') - ->with($id) - ->willReturn( - (new Bookmark()) - ->setId($id) - ->setUrl($url = 'http://domain.tld') - ->setTitle($title = 'Bookmark Title') - ->setDescription($description = 'Bookmark description.') - ->setTags($tags = ['abc', 'def']) - ->setPrivate(true) - ->setCreated($createdAt = new \DateTime('2020-06-10 18:45:44')) - ) - ; - - $result = $this->controller->displayEditForm($request, $response, ['id' => (string) $id]); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('editlink', (string) $result->getBody()); - - static::assertSame('Edit Shaare - Shaarli', $assignedVariables['pagetitle']); - static::assertFalse($assignedVariables['link_is_new']); - - static::assertSame($id, $assignedVariables['link']['id']); - static::assertSame($url, $assignedVariables['link']['url']); - static::assertSame($title, $assignedVariables['link']['title']); - static::assertSame($description, $assignedVariables['link']['description']); - static::assertSame(implode('@', $tags) . '@', $assignedVariables['link']['tags']); - static::assertTrue($assignedVariables['link']['private']); - static::assertSame($createdAt, $assignedVariables['link']['created']); - } - - /** - * Test displaying bookmark edit form - * Invalid ID provided. - */ - public function testDisplayEditFormInvalidId(): void - { - $id = 'invalid'; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier invalid could not be found.']) - ; - - $result = $this->controller->displayEditForm($request, $response, ['id' => $id]); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Test displaying bookmark edit form - * ID not provided. - */ - public function testDisplayEditFormIdNotProvided(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier could not be found.']) - ; - - $result = $this->controller->displayEditForm($request, $response, []); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } - - /** - * Test displaying bookmark edit form - * Bookmark not found. - */ - public function testDisplayEditFormBookmarkNotFound(): void - { - $id = 123; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('get') - ->with($id) - ->willThrowException(new BookmarkNotFoundException()) - ; - - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 123 could not be found.']) - ; - - $result = $this->controller->displayEditForm($request, $response, ['id' => (string) $id]); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); - } -} diff --git a/tests/front/controller/admin/ShaarePublishControllerTest/SaveBookmarkTest.php b/tests/front/controller/admin/ShaarePublishControllerTest/SaveBookmarkTest.php deleted file mode 100644 index 91c377e8..00000000 --- a/tests/front/controller/admin/ShaarePublishControllerTest/SaveBookmarkTest.php +++ /dev/null @@ -1,368 +0,0 @@ -createContainer(); - - $this->container->httpAccess = $this->createMock(HttpAccess::class); - $this->controller = new ShaarePublishController($this->container); - } - - /** - * Test save a new bookmark - */ - public function testSaveBookmark(): void - { - $id = 21; - $parameters = [ - 'lf_url' => 'http://url.tld/other?part=3#hash', - 'lf_title' => 'Provided Title', - 'lf_description' => 'Provided description.', - 'lf_tags' => 'abc def', - 'lf_private' => '1', - 'returnurl' => 'http://shaarli/subfolder/admin/add-shaare' - ]; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $checkBookmark = function (Bookmark $bookmark) use ($parameters) { - static::assertSame($parameters['lf_url'], $bookmark->getUrl()); - static::assertSame($parameters['lf_title'], $bookmark->getTitle()); - static::assertSame($parameters['lf_description'], $bookmark->getDescription()); - static::assertSame($parameters['lf_tags'], $bookmark->getTagsString()); - static::assertTrue($bookmark->isPrivate()); - }; - - $this->container->bookmarkService - ->expects(static::once()) - ->method('addOrSet') - ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark { - static::assertFalse($save); - - $checkBookmark($bookmark); - - $bookmark->setId($id); - - return $bookmark; - }) - ; - $this->container->bookmarkService - ->expects(static::once()) - ->method('set') - ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark { - static::assertTrue($save); - - $checkBookmark($bookmark); - - static::assertSame($id, $bookmark->getId()); - - return $bookmark; - }) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::atLeastOnce()) - ->method('executeHooks') - ->withConsecutive(['save_link']) - ->willReturnCallback(function (string $hook, array $data) use ($parameters, $id): array { - if ('save_link' === $hook) { - static::assertSame($id, $data['id']); - static::assertSame($parameters['lf_url'], $data['url']); - static::assertSame($parameters['lf_title'], $data['title']); - static::assertSame($parameters['lf_description'], $data['description']); - static::assertSame($parameters['lf_tags'], $data['tags']); - static::assertTrue($data['private']); - } - - return $data; - }) - ; - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertRegExp('@/subfolder/#[\w\-]{6}@', $result->getHeader('location')[0]); - } - - - /** - * Test save an existing bookmark - */ - public function testSaveExistingBookmark(): void - { - $id = 21; - $parameters = [ - 'lf_id' => (string) $id, - 'lf_url' => 'http://url.tld/other?part=3#hash', - 'lf_title' => 'Provided Title', - 'lf_description' => 'Provided description.', - 'lf_tags' => 'abc def', - 'lf_private' => '1', - 'returnurl' => 'http://shaarli/subfolder/?page=2' - ]; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $checkBookmark = function (Bookmark $bookmark) use ($parameters, $id) { - static::assertSame($id, $bookmark->getId()); - static::assertSame($parameters['lf_url'], $bookmark->getUrl()); - static::assertSame($parameters['lf_title'], $bookmark->getTitle()); - static::assertSame($parameters['lf_description'], $bookmark->getDescription()); - static::assertSame($parameters['lf_tags'], $bookmark->getTagsString()); - static::assertTrue($bookmark->isPrivate()); - }; - - $this->container->bookmarkService->expects(static::atLeastOnce())->method('exists')->willReturn(true); - $this->container->bookmarkService - ->expects(static::once()) - ->method('get') - ->willReturn((new Bookmark())->setId($id)->setUrl('http://other.url')) - ; - $this->container->bookmarkService - ->expects(static::once()) - ->method('addOrSet') - ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark { - static::assertFalse($save); - - $checkBookmark($bookmark); - - return $bookmark; - }) - ; - $this->container->bookmarkService - ->expects(static::once()) - ->method('set') - ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark { - static::assertTrue($save); - - $checkBookmark($bookmark); - - static::assertSame($id, $bookmark->getId()); - - return $bookmark; - }) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::atLeastOnce()) - ->method('executeHooks') - ->withConsecutive(['save_link']) - ->willReturnCallback(function (string $hook, array $data) use ($parameters, $id): array { - if ('save_link' === $hook) { - static::assertSame($id, $data['id']); - static::assertSame($parameters['lf_url'], $data['url']); - static::assertSame($parameters['lf_title'], $data['title']); - static::assertSame($parameters['lf_description'], $data['description']); - static::assertSame($parameters['lf_tags'], $data['tags']); - static::assertTrue($data['private']); - } - - return $data; - }) - ; - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertRegExp('@/subfolder/\?page=2#[\w\-]{6}@', $result->getHeader('location')[0]); - } - - /** - * Test save a bookmark - try to retrieve the thumbnail - */ - public function testSaveBookmarkWithThumbnailSync(): void - { - $parameters = ['lf_url' => 'http://url.tld/other?part=3#hash']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) { - if ($key === 'thumbnails.mode') { - return Thumbnailer::MODE_ALL; - } elseif ($key === 'general.enable_async_metadata') { - return false; - } - - return $default; - }); - - $this->container->thumbnailer = $this->createMock(Thumbnailer::class); - $this->container->thumbnailer - ->expects(static::once()) - ->method('get') - ->with($parameters['lf_url']) - ->willReturn($thumb = 'http://thumb.url') - ; - - $this->container->bookmarkService - ->expects(static::once()) - ->method('addOrSet') - ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($thumb): Bookmark { - static::assertSame($thumb, $bookmark->getThumbnail()); - - return $bookmark; - }) - ; - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - } - - /** - * Test save a bookmark - with ID #0 - */ - public function testSaveBookmarkWithIdZero(): void - { - $parameters = ['lf_id' => '0']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->bookmarkService->expects(static::once())->method('exists')->with(0)->willReturn(true); - $this->container->bookmarkService->expects(static::once())->method('get')->with(0)->willReturn(new Bookmark()); - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - } - - /** - * Test save a bookmark - do not attempt to retrieve thumbnails if async mode is enabled. - */ - public function testSaveBookmarkWithThumbnailAsync(): void - { - $parameters = ['lf_url' => 'http://url.tld/other?part=3#hash']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) { - if ($key === 'thumbnails.mode') { - return Thumbnailer::MODE_ALL; - } elseif ($key === 'general.enable_async_metadata') { - return true; - } - - return $default; - }); - - $this->container->thumbnailer = $this->createMock(Thumbnailer::class); - $this->container->thumbnailer->expects(static::never())->method('get'); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('addOrSet') - ->willReturnCallback(function (Bookmark $bookmark): Bookmark { - static::assertNull($bookmark->getThumbnail()); - - return $bookmark; - }) - ; - - $result = $this->controller->save($request, $response); - - static::assertSame(302, $result->getStatusCode()); - } - - /** - * Change the password with a wrong existing password - */ - public function testSaveBookmarkFromBookmarklet(): void - { - $parameters = ['source' => 'bookmarklet']; - - $request = $this->createMock(Request::class); - $request - ->method('getParam') - ->willReturnCallback(function (string $key) use ($parameters): ?string { - return $parameters[$key] ?? null; - }) - ; - $response = new Response(); - - $result = $this->controller->save($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('', (string) $result->getBody()); - } - - /** - * Change the password with a wrong existing password - */ - public function testSaveBookmarkWrongToken(): void - { - $this->container->sessionManager = $this->createMock(SessionManager::class); - $this->container->sessionManager->method('checkToken')->willReturn(false); - - $this->container->bookmarkService->expects(static::never())->method('addOrSet'); - $this->container->bookmarkService->expects(static::never())->method('set'); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->expectException(WrongTokenException::class); - - $this->controller->save($request, $response); - } -} diff --git a/tests/front/controller/admin/ShaarliAdminControllerTest.php b/tests/front/controller/admin/ShaarliAdminControllerTest.php deleted file mode 100644 index 95ac76f1..00000000 --- a/tests/front/controller/admin/ShaarliAdminControllerTest.php +++ /dev/null @@ -1,184 +0,0 @@ -createContainer(); - - $this->controller = new class ($this->container) extends ShaarliAdminController - { - public function checkToken(Request $request): bool - { - return parent::checkToken($request); - } - - public function saveSuccessMessage(string $message): void - { - parent::saveSuccessMessage($message); - } - - public function saveWarningMessage(string $message): void - { - parent::saveWarningMessage($message); - } - - public function saveErrorMessage(string $message): void - { - parent::saveErrorMessage($message); - } - }; - } - - /** - * Trigger controller's checkToken with a valid token. - */ - public function testCheckTokenWithValidToken(): void - { - $request = $this->createMock(Request::class); - $request->method('getParam')->with('token')->willReturn($token = '12345'); - - $this->container->sessionManager = $this->createMock(SessionManager::class); - $this->container->sessionManager->method('checkToken')->with($token)->willReturn(true); - - static::assertTrue($this->controller->checkToken($request)); - } - - /** - * Trigger controller's checkToken with na valid token should raise an exception. - */ - public function testCheckTokenWithNotValidToken(): void - { - $request = $this->createMock(Request::class); - $request->method('getParam')->with('token')->willReturn($token = '12345'); - - $this->container->sessionManager = $this->createMock(SessionManager::class); - $this->container->sessionManager->method('checkToken')->with($token)->willReturn(false); - - $this->expectException(WrongTokenException::class); - - $this->controller->checkToken($request); - } - - /** - * Test saveSuccessMessage() with a first message. - */ - public function testSaveSuccessMessage(): void - { - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_SUCCESS_MESSAGES, [$message = 'bravo!']) - ; - - $this->controller->saveSuccessMessage($message); - } - - /** - * Test saveSuccessMessage() with existing messages. - */ - public function testSaveSuccessMessageWithExistingMessages(): void - { - $this->container->sessionManager - ->expects(static::once()) - ->method('getSessionParameter') - ->with(SessionManager::KEY_SUCCESS_MESSAGES) - ->willReturn(['success1', 'success2']) - ; - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_SUCCESS_MESSAGES, ['success1', 'success2', $message = 'bravo!']) - ; - - $this->controller->saveSuccessMessage($message); - } - - /** - * Test saveWarningMessage() with a first message. - */ - public function testSaveWarningMessage(): void - { - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_WARNING_MESSAGES, [$message = 'warning!']) - ; - - $this->controller->saveWarningMessage($message); - } - - /** - * Test saveWarningMessage() with existing messages. - */ - public function testSaveWarningMessageWithExistingMessages(): void - { - $this->container->sessionManager - ->expects(static::once()) - ->method('getSessionParameter') - ->with(SessionManager::KEY_WARNING_MESSAGES) - ->willReturn(['warning1', 'warning2']) - ; - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_WARNING_MESSAGES, ['warning1', 'warning2', $message = 'warning!']) - ; - - $this->controller->saveWarningMessage($message); - } - - /** - * Test saveErrorMessage() with a first message. - */ - public function testSaveErrorMessage(): void - { - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, [$message = 'error!']) - ; - - $this->controller->saveErrorMessage($message); - } - - /** - * Test saveErrorMessage() with existing messages. - */ - public function testSaveErrorMessageWithExistingMessages(): void - { - $this->container->sessionManager - ->expects(static::once()) - ->method('getSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES) - ->willReturn(['error1', 'error2']) - ; - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(SessionManager::KEY_ERROR_MESSAGES, ['error1', 'error2', $message = 'error!']) - ; - - $this->controller->saveErrorMessage($message); - } -} diff --git a/tests/front/controller/admin/ThumbnailsControllerTest.php b/tests/front/controller/admin/ThumbnailsControllerTest.php deleted file mode 100644 index 0c9b63c3..00000000 --- a/tests/front/controller/admin/ThumbnailsControllerTest.php +++ /dev/null @@ -1,157 +0,0 @@ -createContainer(); - - $this->controller = new ThumbnailsController($this->container); - } - - /** - * Test displaying the thumbnails update page - * Note that only non-note and HTTP bookmarks should be returned. - */ - public function testIndex(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('search') - ->willReturn(SearchResult::getSearchResult([ - (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'), - (new Bookmark())->setId(2)->setUrl('?abcdef')->setTitle('Note 1'), - (new Bookmark())->setId(3)->setUrl('http://url2.tld')->setTitle('Title 2'), - (new Bookmark())->setId(4)->setUrl('ftp://domain.tld', ['ftp'])->setTitle('FTP'), - ])) - ; - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('thumbnails', (string) $result->getBody()); - - static::assertSame('Thumbnails update - Shaarli', $assignedVariables['pagetitle']); - static::assertSame([1, 3], $assignedVariables['ids']); - } - - /** - * Test updating a bookmark thumbnail with valid parameters - */ - public function testAjaxUpdateValid(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $bookmark = (new Bookmark()) - ->setId($id = 123) - ->setUrl($url = 'http://url1.tld') - ->setTitle('Title 1') - ->setThumbnail(false) - ; - - $this->container->thumbnailer = $this->createMock(Thumbnailer::class); - $this->container->thumbnailer - ->expects(static::once()) - ->method('get') - ->with($url) - ->willReturn($thumb = 'http://img.tld/pic.png') - ; - - $this->container->bookmarkService - ->expects(static::once()) - ->method('get') - ->with($id) - ->willReturn($bookmark) - ; - $this->container->bookmarkService - ->expects(static::once()) - ->method('set') - ->willReturnCallback(function (Bookmark $bookmark) use ($thumb): Bookmark { - static::assertSame($thumb, $bookmark->getThumbnail()); - - return $bookmark; - }) - ; - - $result = $this->controller->ajaxUpdate($request, $response, ['id' => (string) $id]); - - static::assertSame(200, $result->getStatusCode()); - - $payload = json_decode((string) $result->getBody(), true); - - static::assertSame($id, $payload['id']); - static::assertSame($url, $payload['url']); - static::assertSame($thumb, $payload['thumbnail']); - } - - /** - * Test updating a bookmark thumbnail - Invalid ID - */ - public function testAjaxUpdateInvalidId(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $result = $this->controller->ajaxUpdate($request, $response, ['id' => 'nope']); - - static::assertSame(400, $result->getStatusCode()); - } - - /** - * Test updating a bookmark thumbnail - No ID - */ - public function testAjaxUpdateNoId(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $result = $this->controller->ajaxUpdate($request, $response, []); - - static::assertSame(400, $result->getStatusCode()); - } - - /** - * Test updating a bookmark thumbnail with valid parameters - */ - public function testAjaxUpdateBookmarkNotFound(): void - { - $id = 123; - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('get') - ->with($id) - ->willThrowException(new BookmarkNotFoundException()) - ; - - $result = $this->controller->ajaxUpdate($request, $response, ['id' => (string) $id]); - - static::assertSame(404, $result->getStatusCode()); - } -} diff --git a/tests/front/controller/admin/TokenControllerTest.php b/tests/front/controller/admin/TokenControllerTest.php deleted file mode 100644 index d2f0907f..00000000 --- a/tests/front/controller/admin/TokenControllerTest.php +++ /dev/null @@ -1,41 +0,0 @@ -createContainer(); - - $this->controller = new TokenController($this->container); - } - - public function testGetToken(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->sessionManager - ->expects(static::once()) - ->method('generateToken') - ->willReturn($token = 'token1234') - ; - - $result = $this->controller->getToken($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame($token, (string) $result->getBody()); - } -} diff --git a/tests/front/controller/admin/ToolsControllerTest.php b/tests/front/controller/admin/ToolsControllerTest.php deleted file mode 100644 index e82f8b14..00000000 --- a/tests/front/controller/admin/ToolsControllerTest.php +++ /dev/null @@ -1,69 +0,0 @@ -createContainer(); - - $this->controller = new ToolsController($this->container); - } - - public function testDefaultInvokeWithHttps(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->environment = [ - 'SERVER_NAME' => 'shaarli', - 'SERVER_PORT' => 443, - 'HTTPS' => 'on', - ]; - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('tools', (string) $result->getBody()); - static::assertSame('https://shaarli/', $assignedVariables['pageabsaddr']); - static::assertTrue($assignedVariables['sslenabled']); - } - - public function testDefaultInvokeWithoutHttps(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->environment = [ - 'SERVER_NAME' => 'shaarli', - 'SERVER_PORT' => 80, - ]; - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('tools', (string) $result->getBody()); - static::assertSame('http://shaarli/', $assignedVariables['pageabsaddr']); - static::assertFalse($assignedVariables['sslenabled']); - } -} diff --git a/tests/front/controller/visitor/BookmarkListControllerTest.php b/tests/front/controller/visitor/BookmarkListControllerTest.php deleted file mode 100644 index cd2740cd..00000000 --- a/tests/front/controller/visitor/BookmarkListControllerTest.php +++ /dev/null @@ -1,538 +0,0 @@ -createContainer(); - - $this->controller = new BookmarkListController($this->container); - } - - /** - * Test rendering list of bookmarks with default parameters (first page). - */ - public function testIndexDefaultFirstPage(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('search') - ->with( - ['searchtags' => '', 'searchterm' => ''], - null, - false, - false, - false, - ['offset' => 0, 'limit' => 2] - ) - ->willReturn(SearchResult::getSearchResult([ - (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'), - (new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'), - (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'), - ], 0, 2)); - - $this->container->sessionManager - ->method('getSessionParameter') - ->willReturnCallback(function (string $parameter, $default = null) { - if ('LINKS_PER_PAGE' === $parameter) { - return 2; - } - - return $default; - }) - ; - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('linklist', (string) $result->getBody()); - - static::assertSame('Shaarli', $assignedVariables['pagetitle']); - static::assertSame('?page=2', $assignedVariables['previous_page_url']); - static::assertSame('', $assignedVariables['next_page_url']); - static::assertSame(2, $assignedVariables['page_max']); - static::assertSame('', $assignedVariables['search_tags']); - static::assertSame(3, $assignedVariables['result_count']); - static::assertSame(1, $assignedVariables['page_current']); - static::assertSame('', $assignedVariables['search_term']); - static::assertNull($assignedVariables['visibility']); - static::assertCount(2, $assignedVariables['links']); - - $link = $assignedVariables['links'][0]; - - static::assertSame(1, $link['id']); - static::assertSame('http://url1.tld', $link['url']); - static::assertSame('Title 1', $link['title']); - - $link = $assignedVariables['links'][1]; - - static::assertSame(2, $link['id']); - static::assertSame('http://url2.tld', $link['url']); - static::assertSame('Title 2', $link['title']); - } - - /** - * Test rendering list of bookmarks with default parameters (second page). - */ - public function testIndexDefaultSecondPage(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $request->method('getParam')->willReturnCallback(function (string $key) { - if ('page' === $key) { - return '2'; - } - - return null; - }); - $response = new Response(); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('search') - ->with( - ['searchtags' => '', 'searchterm' => ''], - null, - false, - false, - false, - ['offset' => 2, 'limit' => 2] - ) - ->willReturn(SearchResult::getSearchResult([ - (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'), - (new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'), - (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'), - ], 2, 2)) - ; - - $this->container->sessionManager - ->method('getSessionParameter') - ->willReturnCallback(function (string $parameter, $default = null) { - if ('LINKS_PER_PAGE' === $parameter) { - return 2; - } - - return $default; - }) - ; - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('linklist', (string) $result->getBody()); - - static::assertSame('Shaarli', $assignedVariables['pagetitle']); - static::assertSame('', $assignedVariables['previous_page_url']); - static::assertSame('?page=1', $assignedVariables['next_page_url']); - static::assertSame(2, $assignedVariables['page_max']); - static::assertSame('', $assignedVariables['search_tags']); - static::assertSame(3, $assignedVariables['result_count']); - static::assertSame(2, $assignedVariables['page_current']); - static::assertSame('', $assignedVariables['search_term']); - static::assertNull($assignedVariables['visibility']); - static::assertCount(1, $assignedVariables['links']); - - $link = $assignedVariables['links'][2]; - - static::assertSame(3, $link['id']); - static::assertSame('http://url3.tld', $link['url']); - static::assertSame('Title 3', $link['title']); - } - - /** - * Test rendering list of bookmarks with filters. - */ - public function testIndexDefaultWithFilters(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $request->method('getParam')->willReturnCallback(function (string $key) { - if ('searchtags' === $key) { - return 'abc@def'; - } - if ('searchterm' === $key) { - return 'ghi jkl'; - } - - return null; - }); - $response = new Response(); - - $this->container->sessionManager - ->method('getSessionParameter') - ->willReturnCallback(function (string $key, $default) { - if ('LINKS_PER_PAGE' === $key) { - return 2; - } - if ('visibility' === $key) { - return 'private'; - } - if ('untaggedonly' === $key) { - return true; - } - - return $default; - }) - ; - - $this->container->bookmarkService - ->expects(static::once()) - ->method('search') - ->with( - ['searchtags' => 'abc@def', 'searchterm' => 'ghi jkl'], - 'private', - false, - true, - false, - ['offset' => 0, 'limit' => 2] - ) - ->willReturn(SearchResult::getSearchResult([ - (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'), - (new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'), - (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'), - ], 0, 2)) - ; - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('linklist', (string) $result->getBody()); - - static::assertSame('Search: ghi jkl [abc] [def] - Shaarli', $assignedVariables['pagetitle']); - static::assertSame('?page=2&searchterm=ghi+jkl&searchtags=abc%40def', $assignedVariables['previous_page_url']); - } - - /** - * Test displaying a permalink with valid parameters - */ - public function testPermalinkValid(): void - { - $hash = 'abcdef'; - - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByHash') - ->with($hash) - ->willReturn((new Bookmark())->setId(123)->setTitle('Title 1')->setUrl('http://url1.tld')) - ; - - $result = $this->controller->permalink($request, $response, ['hash' => $hash]); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('linklist', (string) $result->getBody()); - - static::assertSame('Title 1 - Shaarli', $assignedVariables['pagetitle']); - static::assertCount(1, $assignedVariables['links']); - - $link = $assignedVariables['links'][0]; - - static::assertSame(123, $link['id']); - static::assertSame('http://url1.tld', $link['url']); - static::assertSame('Title 1', $link['title']); - } - - /** - * Test displaying a permalink with an unknown small hash : renders a 404 template error - */ - public function testPermalinkNotFound(): void - { - $hash = 'abcdef'; - - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByHash') - ->with($hash) - ->willThrowException(new BookmarkNotFoundException()) - ; - - $result = $this->controller->permalink($request, $response, ['hash' => $hash]); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('404', (string) $result->getBody()); - - static::assertSame( - 'The link you are trying to reach does not exist or has been deleted.', - $assignedVariables['error_message'] - ); - } - - /** - * Test GET /shaare/{hash}?key={key} - Find a link by hash using a private link. - */ - public function testPermalinkWithPrivateKey(): void - { - $hash = 'abcdef'; - $privateKey = 'this is a private key'; - - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $request->method('getParam')->willReturnCallback(function (string $key, $default = null) use ($privateKey) { - return $key === 'key' ? $privateKey : $default; - }); - $response = new Response(); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByHash') - ->with($hash, $privateKey) - ->willReturn((new Bookmark())->setId(123)->setTitle('Title 1')->setUrl('http://url1.tld')) - ; - - $result = $this->controller->permalink($request, $response, ['hash' => $hash]); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('linklist', (string) $result->getBody()); - static::assertCount(1, $assignedVariables['links']); - } - - /** - * Test getting link list with thumbnail updates. - * -> 2 thumbnails update, only 1 datastore write - */ - public function testThumbnailUpdateFromLinkList(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->loginManager = $this->createMock(LoginManager::class); - $this->container->loginManager->method('isLoggedIn')->willReturn(true); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf - ->method('get') - ->willReturnCallback(function (string $key, $default) { - if ($key === 'thumbnails.mode') { - return Thumbnailer::MODE_ALL; - } elseif ($key === 'general.enable_async_metadata') { - return false; - } - - return $default; - }) - ; - - $this->container->thumbnailer = $this->createMock(Thumbnailer::class); - $this->container->thumbnailer - ->expects(static::exactly(2)) - ->method('get') - ->withConsecutive(['https://url2.tld'], ['https://url4.tld']) - ; - - $this->container->bookmarkService - ->expects(static::once()) - ->method('search') - ->willReturn(SearchResult::getSearchResult([ - (new Bookmark())->setId(1)->setUrl('https://url1.tld')->setTitle('Title 1')->setThumbnail(false), - $b1 = (new Bookmark())->setId(2)->setUrl('https://url2.tld')->setTitle('Title 2'), - (new Bookmark())->setId(3)->setUrl('https://url3.tld')->setTitle('Title 3')->setThumbnail(false), - $b2 = (new Bookmark())->setId(2)->setUrl('https://url4.tld')->setTitle('Title 4'), - (new Bookmark())->setId(2)->setUrl('ftp://url5.tld', ['ftp'])->setTitle('Title 5'), - ])) - ; - $this->container->bookmarkService - ->expects(static::exactly(2)) - ->method('set') - ->withConsecutive([$b1, false], [$b2, false]) - ; - $this->container->bookmarkService->expects(static::once())->method('save'); - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('linklist', (string) $result->getBody()); - } - - /** - * Test getting a permalink with thumbnail update. - */ - public function testThumbnailUpdateFromPermalink(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->loginManager = $this->createMock(LoginManager::class); - $this->container->loginManager->method('isLoggedIn')->willReturn(true); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf - ->method('get') - ->willReturnCallback(function (string $key, $default) { - if ($key === 'thumbnails.mode') { - return Thumbnailer::MODE_ALL; - } elseif ($key === 'general.enable_async_metadata') { - return false; - } - - return $default; - }) - ; - - $this->container->thumbnailer = $this->createMock(Thumbnailer::class); - $this->container->thumbnailer->expects(static::once())->method('get')->withConsecutive(['https://url.tld']); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByHash') - ->willReturn($bookmark = (new Bookmark())->setId(2)->setUrl('https://url.tld')->setTitle('Title 1')) - ; - $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, true); - $this->container->bookmarkService->expects(static::never())->method('save'); - - $result = $this->controller->permalink($request, $response, ['hash' => 'abc']); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('linklist', (string) $result->getBody()); - } - - /** - * Test getting a permalink with thumbnail update with async setting: no update should run. - */ - public function testThumbnailUpdateFromPermalinkAsync(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->loginManager = $this->createMock(LoginManager::class); - $this->container->loginManager->method('isLoggedIn')->willReturn(true); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf - ->method('get') - ->willReturnCallback(function (string $key, $default) { - if ($key === 'thumbnails.mode') { - return Thumbnailer::MODE_ALL; - } elseif ($key === 'general.enable_async_metadata') { - return true; - } - - return $default; - }) - ; - - $this->container->thumbnailer = $this->createMock(Thumbnailer::class); - $this->container->thumbnailer->expects(static::never())->method('get'); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByHash') - ->willReturn((new Bookmark())->setId(2)->setUrl('https://url.tld')->setTitle('Title 1')) - ; - $this->container->bookmarkService->expects(static::never())->method('set'); - $this->container->bookmarkService->expects(static::never())->method('save'); - - $result = $this->controller->permalink($request, $response, ['hash' => 'abc']); - - static::assertSame(200, $result->getStatusCode()); - } - - /** - * Trigger legacy controller in link list controller: permalink - */ - public function testLegacyControllerPermalink(): void - { - $hash = 'abcdef'; - $this->container->environment['QUERY_STRING'] = $hash; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $result = $this->controller->index($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame('/subfolder/shaare/' . $hash, $result->getHeader('location')[0]); - } - - /** - * Trigger legacy controller in link list controller: ?do= query parameter - */ - public function testLegacyControllerDoPage(): void - { - $request = $this->createMock(Request::class); - $request->method('getQueryParam')->with('do')->willReturn('picwall'); - $response = new Response(); - - $result = $this->controller->index($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame('/subfolder/picture-wall', $result->getHeader('location')[0]); - } - - /** - * Trigger legacy controller in link list controller: ?do= query parameter with unknown legacy route - */ - public function testLegacyControllerUnknownDoPage(): void - { - $request = $this->createMock(Request::class); - $request->method('getQueryParam')->with('do')->willReturn('nope'); - $response = new Response(); - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('linklist', (string) $result->getBody()); - } - - /** - * Trigger legacy controller in link list controller: other GET route (e.g. ?post) - */ - public function testLegacyControllerGetParameter(): void - { - $request = $this->createMock(Request::class); - $request->method('getQueryParams')->willReturn(['post' => $url = 'http://url.tld']); - $response = new Response(); - - $this->container->loginManager = $this->createMock(LoginManager::class); - $this->container->loginManager->method('isLoggedIn')->willReturn(true); - - $result = $this->controller->index($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame( - '/subfolder/admin/shaare?post=' . urlencode($url), - $result->getHeader('location')[0] - ); - } -} diff --git a/tests/front/controller/visitor/DailyControllerTest.php b/tests/front/controller/visitor/DailyControllerTest.php deleted file mode 100644 index a0dd54e1..00000000 --- a/tests/front/controller/visitor/DailyControllerTest.php +++ /dev/null @@ -1,726 +0,0 @@ -createContainer(); - - $this->controller = new DailyController($this->container); - DailyController::$DAILY_RSS_NB_DAYS = 2; - } - - public function testValidIndexControllerInvokeDefault(): void - { - $currentDay = new \DateTimeImmutable('2020-05-13'); - $previousDate = new \DateTime('2 days ago 00:00:00'); - $nextDate = new \DateTime('today 00:00:00'); - - $request = $this->createMock(Request::class); - $request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string { - return $key === 'day' ? $currentDay->format('Ymd') : null; - }); - $response = new Response(); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByDate') - ->willReturnCallback( - function ($from, $to, &$previous, &$next) use ($currentDay, $previousDate, $nextDate): array { - $previous = $previousDate; - $next = $nextDate; - - return [ - (new Bookmark()) - ->setId(1) - ->setUrl('http://url.tld') - ->setTitle(static::generateString(50)) - ->setDescription(static::generateString(500)) - , - (new Bookmark()) - ->setId(2) - ->setUrl('http://url2.tld') - ->setTitle(static::generateString(50)) - ->setDescription(static::generateString(500)) - , - (new Bookmark()) - ->setId(3) - ->setUrl('http://url3.tld') - ->setTitle(static::generateString(50)) - ->setDescription(static::generateString(500)) - , - ]; - } - ) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::atLeastOnce()) - ->method('executeHooks') - ->withConsecutive(['render_daily']) - ->willReturnCallback( - function (string $hook, array $data, array $param) use ($currentDay, $previousDate, $nextDate): array { - if ('render_daily' === $hook) { - static::assertArrayHasKey('linksToDisplay', $data); - static::assertCount(3, $data['linksToDisplay']); - static::assertSame(1, $data['linksToDisplay'][0]['id']); - static::assertSame($currentDay->getTimestamp(), $data['day']); - static::assertSame($previousDate->format('Ymd'), $data['previousday']); - static::assertSame($nextDate->format('Ymd'), $data['nextday']); - - static::assertArrayHasKey('loggedin', $param); - } - - return $data; - } - ) - ; - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('daily', (string) $result->getBody()); - static::assertSame( - 'Daily - ' . format_date($currentDay, false, true) . ' - Shaarli', - $assignedVariables['pagetitle'] - ); - static::assertEquals($currentDay, $assignedVariables['dayDate']); - static::assertEquals($currentDay->getTimestamp(), $assignedVariables['day']); - static::assertSame($previousDate->format('Ymd'), $assignedVariables['previousday']); - static::assertSame($nextDate->format('Ymd'), $assignedVariables['nextday']); - static::assertSame('day', $assignedVariables['type']); - static::assertSame('May 13, 2020', $assignedVariables['dayDesc']); - static::assertSame('Daily', $assignedVariables['localizedType']); - static::assertCount(3, $assignedVariables['linksToDisplay']); - - $link = $assignedVariables['linksToDisplay'][0]; - - static::assertSame(1, $link['id']); - static::assertSame('http://url.tld', $link['url']); - static::assertNotEmpty($link['title']); - static::assertNotEmpty($link['description']); - static::assertNotEmpty($link['formatedDescription']); - - $link = $assignedVariables['linksToDisplay'][1]; - - static::assertSame(2, $link['id']); - static::assertSame('http://url2.tld', $link['url']); - static::assertNotEmpty($link['title']); - static::assertNotEmpty($link['description']); - static::assertNotEmpty($link['formatedDescription']); - - $link = $assignedVariables['linksToDisplay'][2]; - - static::assertSame(3, $link['id']); - static::assertSame('http://url3.tld', $link['url']); - static::assertNotEmpty($link['title']); - static::assertNotEmpty($link['description']); - static::assertNotEmpty($link['formatedDescription']); - - static::assertCount(3, $assignedVariables['cols']); - static::assertCount(1, $assignedVariables['cols'][0]); - static::assertCount(1, $assignedVariables['cols'][1]); - static::assertCount(1, $assignedVariables['cols'][2]); - - $link = $assignedVariables['cols'][0][0]; - - static::assertSame(1, $link['id']); - static::assertSame('http://url.tld', $link['url']); - static::assertNotEmpty($link['title']); - static::assertNotEmpty($link['description']); - static::assertNotEmpty($link['formatedDescription']); - - $link = $assignedVariables['cols'][1][0]; - - static::assertSame(2, $link['id']); - static::assertSame('http://url2.tld', $link['url']); - static::assertNotEmpty($link['title']); - static::assertNotEmpty($link['description']); - static::assertNotEmpty($link['formatedDescription']); - - $link = $assignedVariables['cols'][2][0]; - - static::assertSame(3, $link['id']); - static::assertSame('http://url3.tld', $link['url']); - static::assertNotEmpty($link['title']); - static::assertNotEmpty($link['description']); - static::assertNotEmpty($link['formatedDescription']); - } - - /** - * Daily page - test that everything goes fine with no future or past bookmarks - */ - public function testValidIndexControllerInvokeNoFutureOrPast(): void - { - $currentDay = new \DateTimeImmutable('2020-05-13'); - - $request = $this->createMock(Request::class); - $request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string { - return $key === 'day' ? $currentDay->format('Ymd') : null; - }); - $response = new Response(); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByDate') - ->willReturnCallback(function () use ($currentDay): array { - return [ - (new Bookmark()) - ->setId(1) - ->setUrl('http://url.tld') - ->setTitle(static::generateString(50)) - ->setDescription(static::generateString(500)) - , - ]; - }) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::atLeastOnce()) - ->method('executeHooks') - ->withConsecutive(['render_daily']) - ->willReturnCallback(function (string $hook, array $data, array $param) use ($currentDay): array { - if ('render_daily' === $hook) { - static::assertArrayHasKey('linksToDisplay', $data); - static::assertCount(1, $data['linksToDisplay']); - static::assertSame(1, $data['linksToDisplay'][0]['id']); - static::assertSame($currentDay->getTimestamp(), $data['day']); - static::assertEmpty($data['previousday']); - static::assertEmpty($data['nextday']); - - static::assertArrayHasKey('loggedin', $param); - } - - return $data; - }); - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('daily', (string) $result->getBody()); - static::assertSame( - 'Daily - ' . format_date($currentDay, false, true) . ' - Shaarli', - $assignedVariables['pagetitle'] - ); - static::assertCount(1, $assignedVariables['linksToDisplay']); - - $link = $assignedVariables['linksToDisplay'][0]; - static::assertSame(1, $link['id']); - } - - /** - * Daily page - test that height adjustment in columns is working - */ - public function testValidIndexControllerInvokeHeightAdjustment(): void - { - $currentDay = new \DateTimeImmutable('2020-05-13'); - - $request = $this->createMock(Request::class); - $response = new Response(); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByDate') - ->willReturnCallback(function () use ($currentDay): array { - return [ - (new Bookmark())->setId(1)->setUrl('http://url.tld')->setTitle('title'), - (new Bookmark()) - ->setId(2) - ->setUrl('http://url.tld') - ->setTitle(static::generateString(50)) - ->setDescription(static::generateString(5000)) - , - (new Bookmark())->setId(3)->setUrl('http://url.tld')->setTitle('title'), - (new Bookmark())->setId(4)->setUrl('http://url.tld')->setTitle('title'), - (new Bookmark())->setId(5)->setUrl('http://url.tld')->setTitle('title'), - (new Bookmark())->setId(6)->setUrl('http://url.tld')->setTitle('title'), - (new Bookmark())->setId(7)->setUrl('http://url.tld')->setTitle('title'), - ]; - }) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::atLeastOnce()) - ->method('executeHooks') - ->willReturnCallback(function (string $hook, array $data, array $param): array { - return $data; - }) - ; - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('daily', (string) $result->getBody()); - static::assertCount(7, $assignedVariables['linksToDisplay']); - - $columnIds = function (array $column): array { - return array_map(function (array $item): int { - return $item['id']; - }, $column); - }; - - static::assertSame([1, 4, 6], $columnIds($assignedVariables['cols'][0])); - static::assertSame([2], $columnIds($assignedVariables['cols'][1])); - static::assertSame([3, 5, 7], $columnIds($assignedVariables['cols'][2])); - } - - /** - * Daily page - no bookmark - */ - public function testValidIndexControllerInvokeNoBookmark(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - // Links dataset: 2 links with thumbnails - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByDate') - ->willReturnCallback(function (): array { - return []; - }) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::atLeastOnce()) - ->method('executeHooks') - ->willReturnCallback(function (string $hook, array $data, array $param): array { - return $data; - }) - ; - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('daily', (string) $result->getBody()); - static::assertCount(0, $assignedVariables['linksToDisplay']); - static::assertSame('Today - ' . (new \DateTime())->format('F j, Y'), $assignedVariables['dayDesc']); - static::assertEquals((new \DateTime())->setTime(0, 0)->getTimestamp(), $assignedVariables['day']); - static::assertEquals((new \DateTime())->setTime(0, 0), $assignedVariables['dayDate']); - } - - /** - * Daily RSS - default behaviour - */ - public function testValidRssControllerInvokeDefault(): void - { - $dates = [ - new \DateTimeImmutable('2020-05-17'), - new \DateTimeImmutable('2020-05-15'), - new \DateTimeImmutable('2020-05-13'), - new \DateTimeImmutable('+1 month'), - ]; - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->bookmarkService->expects(static::once())->method('search')->willReturn( - SearchResult::getSearchResult([ - (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'), - (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'), - (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'), - (new Bookmark())->setId(4)->setCreated($dates[2])->setUrl('http://domain.tld/4'), - (new Bookmark())->setId(5)->setCreated($dates[3])->setUrl('http://domain.tld/5'), - ]) - ); - - $this->container->pageCacheManager - ->expects(static::once()) - ->method('getCachePage') - ->willReturnCallback(function (): CachedPage { - $cachedPage = $this->createMock(CachedPage::class); - $cachedPage->expects(static::once())->method('cache')->with('dailyrss'); - - return $cachedPage; - }); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $result = $this->controller->rss($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]); - static::assertSame('dailyrss', (string) $result->getBody()); - static::assertSame('Shaarli', $assignedVariables['title']); - static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']); - static::assertSame('http://shaarli/subfolder/daily-rss', $assignedVariables['page_url']); - static::assertFalse($assignedVariables['hide_timestamps']); - static::assertCount(3, $assignedVariables['days']); - - $day = $assignedVariables['days'][$dates[0]->format('Ymd')]; - $date = $dates[0]->setTime(23, 59, 59); - - static::assertEquals($date, $day['date']); - static::assertSame($date->format(\DateTime::RSS), $day['date_rss']); - static::assertSame(format_date($date, false), $day['date_human']); - static::assertSame('http://shaarli/subfolder/daily?day=' . $dates[0]->format('Ymd'), $day['absolute_url']); - static::assertCount(1, $day['links']); - static::assertSame(1, $day['links'][0]['id']); - static::assertSame('http://domain.tld/1', $day['links'][0]['url']); - static::assertEquals($dates[0], $day['links'][0]['created']); - - $day = $assignedVariables['days'][$dates[1]->format('Ymd')]; - $date = $dates[1]->setTime(23, 59, 59); - - static::assertEquals($date, $day['date']); - static::assertSame($date->format(\DateTime::RSS), $day['date_rss']); - static::assertSame(format_date($date, false), $day['date_human']); - static::assertSame('http://shaarli/subfolder/daily?day=' . $dates[1]->format('Ymd'), $day['absolute_url']); - static::assertCount(2, $day['links']); - - static::assertSame(2, $day['links'][0]['id']); - static::assertSame('http://domain.tld/2', $day['links'][0]['url']); - static::assertEquals($dates[1], $day['links'][0]['created']); - static::assertSame(3, $day['links'][1]['id']); - static::assertSame('http://domain.tld/3', $day['links'][1]['url']); - static::assertEquals($dates[1], $day['links'][1]['created']); - - $day = $assignedVariables['days'][$dates[2]->format('Ymd')]; - $date = $dates[2]->setTime(23, 59, 59); - - static::assertEquals($date, $day['date']); - static::assertSame($date->format(\DateTime::RSS), $day['date_rss']); - static::assertSame(format_date($date, false), $day['date_human']); - static::assertSame('http://shaarli/subfolder/daily?day=' . $dates[2]->format('Ymd'), $day['absolute_url']); - static::assertCount(1, $day['links']); - static::assertSame(4, $day['links'][0]['id']); - static::assertSame('http://domain.tld/4', $day['links'][0]['url']); - static::assertEquals($dates[2], $day['links'][0]['created']); - } - - /** - * Daily RSS - trigger cache rendering - */ - public function testValidRssControllerInvokeTriggerCache(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->pageCacheManager->method('getCachePage')->willReturnCallback(function (): CachedPage { - $cachedPage = $this->createMock(CachedPage::class); - $cachedPage->method('cachedVersion')->willReturn('this is cache!'); - - return $cachedPage; - }); - - $this->container->bookmarkService->expects(static::never())->method('search'); - - $result = $this->controller->rss($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]); - static::assertSame('this is cache!', (string) $result->getBody()); - } - - /** - * Daily RSS - No bookmark - */ - public function testValidRssControllerInvokeNoBookmark(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->bookmarkService - ->expects(static::once())->method('search') - ->willReturn(SearchResult::getSearchResult([])); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $result = $this->controller->rss($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]); - static::assertSame('dailyrss', (string) $result->getBody()); - static::assertSame('Shaarli', $assignedVariables['title']); - static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']); - static::assertSame('http://shaarli/subfolder/daily-rss', $assignedVariables['page_url']); - static::assertFalse($assignedVariables['hide_timestamps']); - static::assertCount(0, $assignedVariables['days']); - } - - /** - * Test simple display index with week parameter - */ - public function testSimpleIndexWeekly(): void - { - $currentDay = new \DateTimeImmutable('2020-05-13'); - $expectedDay = new \DateTimeImmutable('2020-05-11'); - - $request = $this->createMock(Request::class); - $request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string { - return $key === 'week' ? $currentDay->format('YW') : null; - }); - $response = new Response(); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByDate') - ->willReturnCallback( - function (): array { - return [ - (new Bookmark()) - ->setId(1) - ->setUrl('http://url.tld') - ->setTitle(static::generateString(50)) - ->setDescription(static::generateString(500)) - , - (new Bookmark()) - ->setId(2) - ->setUrl('http://url2.tld') - ->setTitle(static::generateString(50)) - ->setDescription(static::generateString(500)) - , - ]; - } - ) - ; - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('daily', (string) $result->getBody()); - static::assertSame( - 'Weekly - Week 20 (May 11, 2020) - Shaarli', - $assignedVariables['pagetitle'] - ); - - static::assertCount(2, $assignedVariables['linksToDisplay']); - static::assertEquals($expectedDay->setTime(0, 0), $assignedVariables['dayDate']); - static::assertSame($expectedDay->setTime(0, 0)->getTimestamp(), $assignedVariables['day']); - static::assertSame('', $assignedVariables['previousday']); - static::assertSame('', $assignedVariables['nextday']); - static::assertSame('Week 20 (May 11, 2020)', $assignedVariables['dayDesc']); - static::assertSame('week', $assignedVariables['type']); - static::assertSame('Weekly', $assignedVariables['localizedType']); - } - - /** - * Test simple display index with month parameter - */ - public function testSimpleIndexMonthly(): void - { - $currentDay = new \DateTimeImmutable('2020-05-13'); - $expectedDay = new \DateTimeImmutable('2020-05-01'); - - $request = $this->createMock(Request::class); - $request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string { - return $key === 'month' ? $currentDay->format('Ym') : null; - }); - $response = new Response(); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $this->container->bookmarkService - ->expects(static::once()) - ->method('findByDate') - ->willReturnCallback( - function (): array { - return [ - (new Bookmark()) - ->setId(1) - ->setUrl('http://url.tld') - ->setTitle(static::generateString(50)) - ->setDescription(static::generateString(500)) - , - (new Bookmark()) - ->setId(2) - ->setUrl('http://url2.tld') - ->setTitle(static::generateString(50)) - ->setDescription(static::generateString(500)) - , - ]; - } - ) - ; - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('daily', (string) $result->getBody()); - static::assertSame( - 'Monthly - May, 2020 - Shaarli', - $assignedVariables['pagetitle'] - ); - - static::assertCount(2, $assignedVariables['linksToDisplay']); - static::assertEquals($expectedDay->setTime(0, 0), $assignedVariables['dayDate']); - static::assertSame($expectedDay->setTime(0, 0)->getTimestamp(), $assignedVariables['day']); - static::assertSame('', $assignedVariables['previousday']); - static::assertSame('', $assignedVariables['nextday']); - static::assertSame('May, 2020', $assignedVariables['dayDesc']); - static::assertSame('month', $assignedVariables['type']); - static::assertSame('Monthly', $assignedVariables['localizedType']); - } - - /** - * Test simple display RSS with week parameter - */ - public function testSimpleRssWeekly(): void - { - $dates = [ - new \DateTimeImmutable('2020-05-19'), - new \DateTimeImmutable('2020-05-13'), - ]; - $expectedDates = [ - new \DateTimeImmutable('2020-05-24 23:59:59'), - new \DateTimeImmutable('2020-05-17 23:59:59'), - ]; - - $this->container->environment['QUERY_STRING'] = 'week'; - $request = $this->createMock(Request::class); - $request->method('getQueryParam')->willReturnCallback(function (string $key): ?string { - return $key === 'week' ? '' : null; - }); - $response = new Response(); - - $this->container->bookmarkService->expects(static::once())->method('search')->willReturn( - SearchResult::getSearchResult([ - (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'), - (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'), - (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'), - ]) - ); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $result = $this->controller->rss($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]); - static::assertSame('dailyrss', (string) $result->getBody()); - static::assertSame('Shaarli', $assignedVariables['title']); - static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']); - static::assertSame('http://shaarli/subfolder/daily-rss?week', $assignedVariables['page_url']); - static::assertFalse($assignedVariables['hide_timestamps']); - static::assertCount(2, $assignedVariables['days']); - - $day = $assignedVariables['days'][$dates[0]->format('YW')]; - $date = $expectedDates[0]; - - static::assertEquals($date, $day['date']); - static::assertSame($date->format(\DateTime::RSS), $day['date_rss']); - static::assertSame('Week 21 (May 18, 2020)', $day['date_human']); - static::assertSame('http://shaarli/subfolder/daily?week=' . $dates[0]->format('YW'), $day['absolute_url']); - static::assertCount(1, $day['links']); - - $day = $assignedVariables['days'][$dates[1]->format('YW')]; - $date = $expectedDates[1]; - - static::assertEquals($date, $day['date']); - static::assertSame($date->format(\DateTime::RSS), $day['date_rss']); - static::assertSame('Week 20 (May 11, 2020)', $day['date_human']); - static::assertSame('http://shaarli/subfolder/daily?week=' . $dates[1]->format('YW'), $day['absolute_url']); - static::assertCount(2, $day['links']); - } - - /** - * Test simple display RSS with month parameter - */ - public function testSimpleRssMonthly(): void - { - $dates = [ - new \DateTimeImmutable('2022-02-19'), - new \DateTimeImmutable('2020-04-13'), - ]; - $expectedDates = [ - new \DateTimeImmutable('2022-02-28 23:59:59'), - new \DateTimeImmutable('2020-04-30 23:59:59'), - ]; - - $this->container->environment['QUERY_STRING'] = 'month'; - $request = $this->createMock(Request::class); - $request->method('getQueryParam')->willReturnCallback(function (string $key): ?string { - return $key === 'month' ? '' : null; - }); - $response = new Response(); - - $this->container->bookmarkService->expects(static::once())->method('search')->willReturn( - SearchResult::getSearchResult([ - (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'), - (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'), - (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'), - ]) - ); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $result = $this->controller->rss($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]); - static::assertSame('dailyrss', (string) $result->getBody()); - static::assertSame('Shaarli', $assignedVariables['title']); - static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']); - static::assertSame('http://shaarli/subfolder/daily-rss?month', $assignedVariables['page_url']); - static::assertFalse($assignedVariables['hide_timestamps']); - static::assertCount(2, $assignedVariables['days']); - - $day = $assignedVariables['days'][$dates[0]->format('Ym')]; - $date = $expectedDates[0]; - - static::assertEquals($date, $day['date']); - static::assertSame($date->format(\DateTime::RSS), $day['date_rss']); - static::assertSame('February, 2022', $day['date_human']); - static::assertSame('http://shaarli/subfolder/daily?month=' . $dates[0]->format('Ym'), $day['absolute_url']); - static::assertCount(1, $day['links']); - - $day = $assignedVariables['days'][$dates[1]->format('Ym')]; - $date = $expectedDates[1]; - - static::assertEquals($date, $day['date']); - static::assertSame($date->format(\DateTime::RSS), $day['date_rss']); - static::assertSame('April, 2020', $day['date_human']); - static::assertSame('http://shaarli/subfolder/daily?month=' . $dates[1]->format('Ym'), $day['absolute_url']); - static::assertCount(2, $day['links']); - } -} diff --git a/tests/front/controller/visitor/ErrorControllerTest.php b/tests/front/controller/visitor/ErrorControllerTest.php deleted file mode 100644 index e0dc8db8..00000000 --- a/tests/front/controller/visitor/ErrorControllerTest.php +++ /dev/null @@ -1,98 +0,0 @@ -createContainer(); - - $this->controller = new ErrorController($this->container); - } - - /** - * Test displaying error with a ShaarliFrontException: display exception message and use its code for HTTTP code - */ - public function testDisplayFrontExceptionError(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $message = 'error message'; - $errorCode = 418; - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $result = ($this->controller)( - $request, - $response, - new class ($message, $errorCode) extends ShaarliFrontException { - } - ); - - static::assertSame($errorCode, $result->getStatusCode()); - static::assertSame($message, $assignedVariables['message']); - static::assertArrayNotHasKey('stacktrace', $assignedVariables); - } - - /** - * Test displaying error with any exception (no debug) while logged in: - * display full error details - */ - public function testDisplayAnyExceptionErrorNoDebugLoggedIn(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $this->container->loginManager->method('isLoggedIn')->willReturn(true); - - $result = ($this->controller)($request, $response, new \Exception('abc')); - - static::assertSame(500, $result->getStatusCode()); - static::assertSame('Error: abc', $assignedVariables['message']); - static::assertContainsPolyfill('Please report it on Github', $assignedVariables['text']); - static::assertArrayHasKey('stacktrace', $assignedVariables); - } - - /** - * Test displaying error with any exception (no debug) while logged out: - * display standard error without detail - */ - public function testDisplayAnyExceptionErrorNoDebug(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $this->container->loginManager->method('isLoggedIn')->willReturn(false); - - $result = ($this->controller)($request, $response, new \Exception('abc')); - - static::assertSame(500, $result->getStatusCode()); - static::assertSame('An unexpected error occurred.', $assignedVariables['message']); - static::assertArrayNotHasKey('text', $assignedVariables); - static::assertArrayNotHasKey('stacktrace', $assignedVariables); - } -} diff --git a/tests/front/controller/visitor/ErrorNotFoundControllerTest.php b/tests/front/controller/visitor/ErrorNotFoundControllerTest.php deleted file mode 100644 index a1cbbecf..00000000 --- a/tests/front/controller/visitor/ErrorNotFoundControllerTest.php +++ /dev/null @@ -1,81 +0,0 @@ -createContainer(); - - $this->controller = new ErrorNotFoundController($this->container); - } - - /** - * Test displaying 404 error - */ - public function testDisplayNotFoundError(): void - { - $request = $this->createMock(Request::class); - $request->expects(static::once())->method('getRequestTarget')->willReturn('/'); - $request->method('getUri')->willReturnCallback(function (): Uri { - $uri = $this->createMock(Uri::class); - $uri->method('getBasePath')->willReturn('/subfolder'); - - return $uri; - }); - - $response = new Response(); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $result = ($this->controller)( - $request, - $response - ); - - static::assertSame(404, $result->getStatusCode()); - static::assertSame('404', (string) $result->getBody()); - static::assertSame('Requested page could not be found.', $assignedVariables['error_message']); - } - - /** - * Test displaying 404 error from REST API - */ - public function testDisplayNotFoundErrorFromAPI(): void - { - $request = $this->createMock(Request::class); - $request->expects(static::once())->method('getRequestTarget')->willReturn('/sufolder/api/v1/links'); - $request->method('getUri')->willReturnCallback(function (): Uri { - $uri = $this->createMock(Uri::class); - $uri->method('getBasePath')->willReturn('/subfolder'); - - return $uri; - }); - - $response = new Response(); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $result = ($this->controller)($request, $response); - - static::assertSame(404, $result->getStatusCode()); - static::assertSame([], $assignedVariables); - } -} diff --git a/tests/front/controller/visitor/FeedControllerTest.php b/tests/front/controller/visitor/FeedControllerTest.php deleted file mode 100644 index 4ae7c925..00000000 --- a/tests/front/controller/visitor/FeedControllerTest.php +++ /dev/null @@ -1,151 +0,0 @@ -createContainer(); - - $this->container->feedBuilder = $this->createMock(FeedBuilder::class); - - $this->controller = new FeedController($this->container); - } - - /** - * Feed Controller - RSS default behaviour - */ - public function testDefaultRssController(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->feedBuilder->expects(static::once())->method('setLocale'); - $this->container->feedBuilder->expects(static::once())->method('setHideDates')->with(false); - $this->container->feedBuilder->expects(static::once())->method('setUsePermalinks')->with(true); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $this->container->feedBuilder->method('buildData')->willReturn(['content' => 'data']); - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::atLeastOnce()) - ->method('executeHooks') - ->withConsecutive(['render_feed']) - ->willReturnCallback(function (string $hook, array $data, array $param): void { - if ('render_feed' === $hook) { - static::assertSame('data', $data['content']); - - static::assertArrayHasKey('loggedin', $param); - static::assertSame('feed.rss', $param['target']); - } - }) - ; - - $result = $this->controller->rss($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]); - static::assertSame('feed.rss', (string) $result->getBody()); - static::assertSame('data', $assignedVariables['content']); - } - - /** - * Feed Controller - ATOM default behaviour - */ - public function testDefaultAtomController(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->feedBuilder->expects(static::once())->method('setLocale'); - $this->container->feedBuilder->expects(static::once())->method('setHideDates')->with(false); - $this->container->feedBuilder->expects(static::once())->method('setUsePermalinks')->with(true); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $this->container->feedBuilder->method('buildData')->willReturn(['content' => 'data']); - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::atLeastOnce()) - ->method('executeHooks') - ->withConsecutive(['render_feed']) - ->willReturnCallback(function (string $hook, array $data, array $param): void { - if ('render_feed' === $hook) { - static::assertSame('data', $data['content']); - - static::assertArrayHasKey('loggedin', $param); - static::assertSame('feed.atom', $param['target']); - } - }) - ; - - $result = $this->controller->atom($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertStringContainsString('application/atom', $result->getHeader('Content-Type')[0]); - static::assertSame('feed.atom', (string) $result->getBody()); - static::assertSame('data', $assignedVariables['content']); - } - - /** - * Feed Controller - ATOM with parameters - */ - public function testAtomControllerWithParameters(): void - { - $request = $this->createMock(Request::class); - $request->method('getParams')->willReturn(['parameter' => 'value']); - $response = new Response(); - - // Save RainTPL assigned variables - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $this->container->feedBuilder - ->method('buildData') - ->with('atom', ['parameter' => 'value']) - ->willReturn(['content' => 'data']) - ; - - // Make sure that PluginManager hook is triggered - $this->container->pluginManager - ->expects(static::atLeastOnce()) - ->method('executeHooks') - ->withConsecutive(['render_feed']) - ->willReturnCallback(function (string $hook, array $data, array $param): void { - if ('render_feed' === $hook) { - static::assertSame('data', $data['content']); - - static::assertArrayHasKey('loggedin', $param); - static::assertSame('feed.atom', $param['target']); - } - }) - ; - - $result = $this->controller->atom($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertStringContainsString('application/atom', $result->getHeader('Content-Type')[0]); - static::assertSame('feed.atom', (string) $result->getBody()); - static::assertSame('data', $assignedVariables['content']); - } -} diff --git a/tests/front/controller/visitor/FrontControllerMockHelper.php b/tests/front/controller/visitor/FrontControllerMockHelper.php deleted file mode 100644 index b0b00e95..00000000 --- a/tests/front/controller/visitor/FrontControllerMockHelper.php +++ /dev/null @@ -1,122 +0,0 @@ -container = $this->createMock(ShaarliTestContainer::class); - - $this->container->loginManager = $this->createMock(LoginManager::class); - - // Config - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) { - if ($parameter === 'general.tags_separator') { - return '@'; - } - - return $default === null ? $parameter : $default; - }); - - // PageBuilder - $this->container->pageBuilder = $this->createMock(PageBuilder::class); - $this->container->pageBuilder - ->method('render') - ->willReturnCallback(function (string $template): string { - return $template; - }) - ; - - // Plugin Manager - $this->container->pluginManager = $this->createMock(PluginManager::class); - - // BookmarkService - $this->container->bookmarkService = $this->createMock(BookmarkServiceInterface::class); - - // Formatter - $this->container->formatterFactory = $this->createMock(FormatterFactory::class); - $this->container->formatterFactory - ->method('getFormatter') - ->willReturnCallback(function (): BookmarkFormatter { - return new BookmarkRawFormatter($this->container->conf, true); - }) - ; - - // CacheManager - $this->container->pageCacheManager = $this->createMock(PageCacheManager::class); - - // SessionManager - $this->container->sessionManager = $this->createMock(SessionManager::class); - - // $_SERVER - $this->container->environment = [ - 'SERVER_NAME' => 'shaarli', - 'SERVER_PORT' => '80', - 'REQUEST_URI' => '/subfolder/daily-rss', - 'REMOTE_ADDR' => '1.2.3.4', - 'SCRIPT_NAME' => '/subfolder/index.php', - ]; - - $this->container->basePath = '/subfolder'; - } - - /** - * Pass a reference of an array which will be populated by `pageBuilder->assign` calls during execution. - * - * @param mixed $variables Array reference to populate. - */ - protected function assignTemplateVars(array &$variables): void - { - $this->container->pageBuilder - ->method('assign') - ->willReturnCallback(function ($key, $value) use (&$variables) { - $variables[$key] = $value; - - return $this; - }) - ; - } - - protected static function generateString(int $length): string - { - // bin2hex(random_bytes) generates string twice as long as given parameter - $length = (int) ceil($length / 2); - - return bin2hex(random_bytes($length)); - } - - /** - * Force to be used in PHPUnit context. - */ - abstract protected function isInTestsContext(): bool; -} diff --git a/tests/front/controller/visitor/InstallControllerTest.php b/tests/front/controller/visitor/InstallControllerTest.php deleted file mode 100644 index 9cf0e1c9..00000000 --- a/tests/front/controller/visitor/InstallControllerTest.php +++ /dev/null @@ -1,304 +0,0 @@ -createContainer(); - - $this->container->conf = $this->createMock(ConfigManager::class); - $this->container->conf->method('getConfigFileExt')->willReturn(static::MOCK_FILE); - $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) { - if ($key === 'resource.raintpl_tpl') { - return '.'; - } - - return $default ?? $key; - }); - - $this->controller = new InstallController($this->container); - } - - protected function tearDown(): void - { - if (file_exists(static::MOCK_FILE)) { - unlink(static::MOCK_FILE); - } - } - - /** - * Test displaying install page with valid session. - */ - public function testInstallIndexWithValidSession(): void - { - $assignedVariables = []; - $this->assignTemplateVars($assignedVariables); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->sessionManager = $this->createMock(SessionManager::class); - $this->container->sessionManager - ->method('getSessionParameter') - ->willReturnCallback(function (string $key, $default) { - return $key === 'session_tested' ? 'Working' : $default; - }) - ; - - $result = $this->controller->index($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('install', (string) $result->getBody()); - - static::assertIsArray($assignedVariables['continents']); - static::assertSame('Africa', $assignedVariables['continents'][0]); - static::assertSame('UTC', $assignedVariables['continents']['selected']); - - static::assertIsArray($assignedVariables['cities']); - static::assertSame(['continent' => 'Africa', 'city' => 'Abidjan'], $assignedVariables['cities'][0]); - static::assertSame('UTC', $assignedVariables['continents']['selected']); - - static::assertIsArray($assignedVariables['languages']); - static::assertSame('Automatic', $assignedVariables['languages']['auto']); - static::assertSame('French', $assignedVariables['languages']['fr']); - - static::assertSame(PHP_VERSION, $assignedVariables['php_version']); - static::assertArrayHasKey('php_has_reached_eol', $assignedVariables); - static::assertArrayHasKey('php_eol', $assignedVariables); - static::assertArrayHasKey('php_extensions', $assignedVariables); - static::assertArrayHasKey('permissions', $assignedVariables); - static::assertEmpty($assignedVariables['permissions']); - - static::assertSame('Install Shaarli', $assignedVariables['pagetitle']); - } - - /** - * Instantiate the install controller with an existing config file: exception. - */ - public function testInstallWithExistingConfigFile(): void - { - $this->expectException(AlreadyInstalledException::class); - - touch(static::MOCK_FILE); - - $this->controller = new InstallController($this->container); - } - - /** - * Call controller without session yet defined, redirect to test session install page. - */ - public function testInstallRedirectToSessionTest(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->sessionManager = $this->createMock(SessionManager::class); - $this->container->sessionManager - ->expects(static::once()) - ->method('setSessionParameter') - ->with(InstallController::SESSION_TEST_KEY, InstallController::SESSION_TEST_VALUE) - ; - - $result = $this->controller->index($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame('/subfolder/install/session-test', $result->getHeader('location')[0]); - } - - /** - * Call controller in session test mode: valid session then redirect to install page. - */ - public function testInstallSessionTestValid(): void - { - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->sessionManager = $this->createMock(SessionManager::class); - $this->container->sessionManager - ->method('getSessionParameter') - ->with(InstallController::SESSION_TEST_KEY) - ->willReturn(InstallController::SESSION_TEST_VALUE) - ; - - $result = $this->controller->sessionTest($request, $response); - - static::assertSame(302, $result->getStatusCode()); - static::assertSame('/subfolder/install', $result->getHeader('location')[0]); - } - - /** - * Call controller in session test mode: invalid session then redirect to error page. - */ - public function testInstallSessionTestError(): void - { - $assignedVars = []; - $this->assignTemplateVars($assignedVars); - - $request = $this->createMock(Request::class); - $response = new Response(); - - $this->container->sessionManager = $this->createMock(SessionManager::class); - $this->container->sessionManager - ->method('getSessionParameter') - ->with(InstallController::SESSION_TEST_KEY) - ->willReturn('KO') - ; - - $result = $this->controller->sessionTest($request, $response); - - static::assertSame(200, $result->getStatusCode()); - static::assertSame('error', (string) $result->getBody()); - static::assertStringStartsWith( - '
Sessions do not seem to work correctly on your server',
-            $assignedVars['message']
-        );
-    }
-
-    /**
-     * Test saving valid data from install form. Also initialize datastore.
-     */
-    public function testSaveInstallValid(): void
-    {
-        $providedParameters = [
-            'continent' => 'Europe',
-            'city' => 'Berlin',
-            'setlogin' => 'bob',
-            'setpassword' => 'password',
-            'title' => 'Shaarli',
-            'language' => 'fr',
-            'updateCheck' => true,
-            'enableApi' => true,
-        ];
-
-        $expectedSettings = [
-            'general.timezone' => 'Europe/Berlin',
-            'credentials.login' => 'bob',
-            'credentials.salt' => '_NOT_EMPTY',
-            'credentials.hash' => '_NOT_EMPTY',
-            'general.title' => 'Shaarli',
-            'translation.language' => 'en',
-            'updates.check_updates' => true,
-            'api.enabled' => true,
-            'api.secret' => '_NOT_EMPTY',
-            'general.header_link' => '/subfolder',
-        ];
-
-        $request = $this->createMock(Request::class);
-        $request->method('getParam')->willReturnCallback(function (string $key) use ($providedParameters) {
-            return $providedParameters[$key] ?? null;
-        });
-        $response = new Response();
-
-        $this->container->conf = $this->createMock(ConfigManager::class);
-        $this->container->conf
-            ->method('get')
-            ->willReturnCallback(function (string $key, $value) {
-                if ($key === 'credentials.login') {
-                    return 'bob';
-                } elseif ($key === 'credentials.salt') {
-                    return 'salt';
-                }
-
-                return $value;
-            })
-        ;
-        $this->container->conf
-            ->expects(static::exactly(count($expectedSettings)))
-            ->method('set')
-            ->willReturnCallback(function (string $key, $value) use ($expectedSettings) {
-                if ($expectedSettings[$key] ?? null === '_NOT_EMPTY') {
-                    static::assertNotEmpty($value);
-                } else {
-                    static::assertSame($expectedSettings[$key], $value);
-                }
-            })
-        ;
-        $this->container->conf->expects(static::once())->method('write');
-
-        $this->container->sessionManager
-            ->expects(static::once())
-            ->method('setSessionParameter')
-            ->with(SessionManager::KEY_SUCCESS_MESSAGES)
-        ;
-
-        $result = $this->controller->save($request, $response);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame('/subfolder/login', $result->getHeader('location')[0]);
-    }
-
-    /**
-     * Test default settings (timezone and title).
-     * Also check that bookmarks are not initialized if
-     */
-    public function testSaveInstallDefaultValues(): void
-    {
-        $confSettings = [];
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $this->container->conf->method('set')->willReturnCallback(function (string $key, $value) use (&$confSettings) {
-            $confSettings[$key] = $value;
-        });
-
-        $result = $this->controller->save($request, $response);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame('/subfolder/login', $result->getHeader('location')[0]);
-
-        static::assertSame('UTC', $confSettings['general.timezone']);
-        static::assertSame('Shared Bookmarks', $confSettings['general.title']);
-    }
-
-    /**
-     * Same test  as testSaveInstallDefaultValues() but for an instance install in root directory.
-     */
-    public function testSaveInstallDefaultValuesWithoutSubfolder(): void
-    {
-        $confSettings = [];
-
-        $this->container->environment = [
-            'SERVER_NAME' => 'shaarli',
-            'SERVER_PORT' => '80',
-            'REQUEST_URI' => '/install',
-            'REMOTE_ADDR' => '1.2.3.4',
-            'SCRIPT_NAME' => '/index.php',
-        ];
-
-        $this->container->basePath = '';
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $this->container->conf->method('set')->willReturnCallback(function (string $key, $value) use (&$confSettings) {
-            $confSettings[$key] = $value;
-        });
-
-        $result = $this->controller->save($request, $response);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame('/login', $result->getHeader('location')[0]);
-
-        static::assertSame('UTC', $confSettings['general.timezone']);
-        static::assertSame('Shared Bookmarks', $confSettings['general.title']);
-    }
-}
diff --git a/tests/front/controller/visitor/LoginControllerTest.php b/tests/front/controller/visitor/LoginControllerTest.php
deleted file mode 100644
index 00d9eab3..00000000
--- a/tests/front/controller/visitor/LoginControllerTest.php
+++ /dev/null
@@ -1,404 +0,0 @@
-createContainer();
-
-        $this->container->cookieManager = $this->createMock(CookieManager::class);
-        $this->container->sessionManager->method('checkToken')->willReturn(true);
-
-        $this->controller = new LoginController($this->container);
-    }
-
-    /**
-     * Test displaying login form with valid parameters.
-     */
-    public function testValidControllerInvoke(): void
-    {
-        $request = $this->createMock(Request::class);
-        $request
-            ->expects(static::atLeastOnce())
-            ->method('getParam')
-            ->willReturnCallback(function (string $key) {
-                return 'returnurl' === $key ? '> referer' : null;
-            })
-        ;
-        $response = new Response();
-
-        $assignedVariables = [];
-        $this->container->pageBuilder
-            ->method('assign')
-            ->willReturnCallback(function ($key, $value) use (&$assignedVariables) {
-                $assignedVariables[$key] = $value;
-
-                return $this;
-            })
-        ;
-
-        $this->container->loginManager->method('canLogin')->willReturn(true);
-
-        $result = $this->controller->index($request, $response);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(200, $result->getStatusCode());
-        static::assertSame(TemplatePage::LOGIN, (string) $result->getBody());
-
-        static::assertSame('> referer', $assignedVariables['returnurl']);
-        static::assertSame(true, $assignedVariables['remember_user_default']);
-        static::assertSame('Login - Shaarli', $assignedVariables['pagetitle']);
-    }
-
-    /**
-     * Test displaying login form with username defined in the request.
-     */
-    public function testValidControllerInvokeWithUserName(): void
-    {
-        $this->container->environment = ['HTTP_REFERER' => '> referer'];
-
-        $request = $this->createMock(Request::class);
-        $request
-            ->expects(static::atLeastOnce())
-            ->method('getParam')
-            ->willReturnCallback(function (string $key, $default) {
-                if ('login' === $key) {
-                    return 'myUser>';
-                }
-
-                return $default;
-            })
-        ;
-        $response = new Response();
-
-        $assignedVariables = [];
-        $this->container->pageBuilder
-            ->method('assign')
-            ->willReturnCallback(function ($key, $value) use (&$assignedVariables) {
-                $assignedVariables[$key] = $value;
-
-                return $this;
-            })
-        ;
-
-        $this->container->loginManager->expects(static::once())->method('canLogin')->willReturn(true);
-
-        $result = $this->controller->index($request, $response);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(200, $result->getStatusCode());
-        static::assertSame('loginform', (string) $result->getBody());
-
-        static::assertSame('myUser>', $assignedVariables['username']);
-        static::assertSame('> referer', $assignedVariables['returnurl']);
-        static::assertSame(true, $assignedVariables['remember_user_default']);
-        static::assertSame('Login - Shaarli', $assignedVariables['pagetitle']);
-    }
-
-    /**
-     * Test displaying login page while being logged in.
-     */
-    public function testLoginControllerWhileLoggedIn(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $this->container->loginManager->expects(static::once())->method('isLoggedIn')->willReturn(true);
-
-        $result = $this->controller->index($request, $response);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/'], $result->getHeader('Location'));
-    }
-
-    /**
-     * Test displaying login page with open shaarli configured: redirect to homepage.
-     */
-    public function testLoginControllerOpenShaarli(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $conf = $this->createMock(ConfigManager::class);
-        $conf->method('get')->willReturnCallback(function (string $parameter, $default) {
-            if ($parameter === 'security.open_shaarli') {
-                return true;
-            }
-            return $default;
-        });
-        $this->container->conf = $conf;
-
-        $result = $this->controller->index($request, $response);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/'], $result->getHeader('Location'));
-    }
-
-    /**
-     * Test displaying login page while being banned.
-     */
-    public function testLoginControllerWhileBanned(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $this->container->loginManager->method('isLoggedIn')->willReturn(false);
-        $this->container->loginManager->method('canLogin')->willReturn(false);
-
-        $this->expectException(LoginBannedException::class);
-
-        $this->controller->index($request, $response);
-    }
-
-    /**
-     * Test processing login with valid parameters.
-     */
-    public function testProcessLoginWithValidParameters(): void
-    {
-        $parameters = [
-            'login' => 'bob',
-            'password' => 'pass',
-        ];
-        $request = $this->createMock(Request::class);
-        $request
-            ->expects(static::atLeastOnce())
-            ->method('getParam')
-            ->willReturnCallback(function (string $key) use ($parameters) {
-                return $parameters[$key] ?? null;
-            })
-        ;
-        $response = new Response();
-
-        $this->container->loginManager->method('canLogin')->willReturn(true);
-        $this->container->loginManager->expects(static::once())->method('handleSuccessfulLogin');
-        $this->container->loginManager
-            ->expects(static::once())
-            ->method('checkCredentials')
-            ->with('1.2.3.4', 'bob', 'pass')
-            ->willReturn(true)
-        ;
-        $this->container->loginManager->method('getStaySignedInToken')->willReturn(bin2hex(random_bytes(8)));
-
-        $this->container->sessionManager->expects(static::never())->method('extendSession');
-        $this->container->sessionManager->expects(static::once())->method('destroy');
-        $this->container->sessionManager
-            ->expects(static::once())
-            ->method('cookieParameters')
-            ->with(0, '/subfolder/', 'shaarli')
-        ;
-        $this->container->sessionManager->expects(static::once())->method('start');
-        $this->container->sessionManager->expects(static::once())->method('regenerateId')->with(true);
-
-        $result = $this->controller->login($request, $response);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame('/subfolder/', $result->getHeader('location')[0]);
-    }
-
-    /**
-     * Test processing login with return URL.
-     */
-    public function testProcessLoginWithReturnUrl(): void
-    {
-        $parameters = [
-            'returnurl' => 'http://shaarli/subfolder/admin/shaare',
-        ];
-        $request = $this->createMock(Request::class);
-        $request
-            ->expects(static::atLeastOnce())
-            ->method('getParam')
-            ->willReturnCallback(function (string $key) use ($parameters) {
-                return $parameters[$key] ?? null;
-            })
-        ;
-        $response = new Response();
-
-        $this->container->loginManager->method('canLogin')->willReturn(true);
-        $this->container->loginManager->expects(static::once())->method('handleSuccessfulLogin');
-        $this->container->loginManager->expects(static::once())->method('checkCredentials')->willReturn(true);
-        $this->container->loginManager->method('getStaySignedInToken')->willReturn(bin2hex(random_bytes(8)));
-
-        $result = $this->controller->login($request, $response);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame('/subfolder/admin/shaare', $result->getHeader('location')[0]);
-    }
-
-    /**
-     * Test processing login with remember me session enabled.
-     */
-    public function testProcessLoginLongLastingSession(): void
-    {
-        $parameters = [
-            'longlastingsession' => true,
-        ];
-        $request = $this->createMock(Request::class);
-        $request
-            ->expects(static::atLeastOnce())
-            ->method('getParam')
-            ->willReturnCallback(function (string $key) use ($parameters) {
-                return $parameters[$key] ?? null;
-            })
-        ;
-        $response = new Response();
-
-        $this->container->loginManager->method('canLogin')->willReturn(true);
-        $this->container->loginManager->expects(static::once())->method('handleSuccessfulLogin');
-        $this->container->loginManager->expects(static::once())->method('checkCredentials')->willReturn(true);
-        $this->container->loginManager->method('getStaySignedInToken')->willReturn(bin2hex(random_bytes(8)));
-
-        $this->container->sessionManager->expects(static::once())->method('destroy');
-        $this->container->sessionManager
-            ->expects(static::once())
-            ->method('cookieParameters')
-            ->with(42, '/subfolder/', 'shaarli')
-        ;
-        $this->container->sessionManager->expects(static::once())->method('start');
-        $this->container->sessionManager->expects(static::once())->method('regenerateId')->with(true);
-        $this->container->sessionManager->expects(static::once())->method('extendSession')->willReturn(42);
-
-        $this->container->cookieManager = $this->createMock(CookieManager::class);
-        $this->container->cookieManager
-            ->expects(static::once())
-            ->method('setCookieParameter')
-            ->willReturnCallback(function (string $name): CookieManager {
-                static::assertSame(CookieManager::STAY_SIGNED_IN, $name);
-
-                return $this->container->cookieManager;
-            })
-        ;
-
-        $result = $this->controller->login($request, $response);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame('/subfolder/', $result->getHeader('location')[0]);
-    }
-
-    /**
-     * Test processing login with invalid credentials
-     */
-    public function testProcessLoginWrongCredentials(): void
-    {
-        $parameters = [
-            'returnurl' => 'http://shaarli/subfolder/admin/shaare',
-        ];
-        $request = $this->createMock(Request::class);
-        $request
-            ->expects(static::atLeastOnce())
-            ->method('getParam')
-            ->willReturnCallback(function (string $key) use ($parameters) {
-                return $parameters[$key] ?? null;
-            })
-        ;
-        $response = new Response();
-
-        $this->container->loginManager->method('canLogin')->willReturn(true);
-        $this->container->loginManager->expects(static::once())->method('handleFailedLogin');
-        $this->container->loginManager->expects(static::once())->method('checkCredentials')->willReturn(false);
-
-        $this->container->sessionManager
-            ->expects(static::once())
-            ->method('setSessionParameter')
-            ->with(SessionManager::KEY_ERROR_MESSAGES, ['Wrong login/password.'])
-        ;
-
-        $result = $this->controller->login($request, $response);
-
-        static::assertSame(200, $result->getStatusCode());
-        static::assertSame(TemplatePage::LOGIN, (string) $result->getBody());
-    }
-
-    /**
-     * Test processing login with wrong token
-     */
-    public function testProcessLoginWrongToken(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $this->container->sessionManager = $this->createMock(SessionManager::class);
-        $this->container->sessionManager->method('checkToken')->willReturn(false);
-
-        $this->expectException(WrongTokenException::class);
-
-        $this->controller->login($request, $response);
-    }
-
-    /**
-     * Test processing login with wrong token
-     */
-    public function testProcessLoginAlreadyLoggedIn(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $this->container->loginManager->method('isLoggedIn')->willReturn(true);
-        $this->container->loginManager->expects(static::never())->method('handleSuccessfulLogin');
-        $this->container->loginManager->expects(static::never())->method('handleFailedLogin');
-
-        $result = $this->controller->login($request, $response);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame('/subfolder/', $result->getHeader('location')[0]);
-    }
-
-    /**
-     * Test processing login with wrong token
-     */
-    public function testProcessLoginInOpenShaarli(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $this->container->conf = $this->createMock(ConfigManager::class);
-        $this->container->conf->method('get')->willReturnCallback(function (string $key, $value) {
-            return 'security.open_shaarli' === $key ? true : $value;
-        });
-
-        $this->container->loginManager->expects(static::never())->method('handleSuccessfulLogin');
-        $this->container->loginManager->expects(static::never())->method('handleFailedLogin');
-
-        $result = $this->controller->login($request, $response);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame('/subfolder/', $result->getHeader('location')[0]);
-    }
-
-    /**
-     * Test processing login while being banned
-     */
-    public function testProcessLoginWhileBanned(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $this->container->loginManager->method('canLogin')->willReturn(false);
-        $this->container->loginManager->expects(static::never())->method('handleSuccessfulLogin');
-        $this->container->loginManager->expects(static::never())->method('handleFailedLogin');
-
-        $this->expectException(LoginBannedException::class);
-
-        $this->controller->login($request, $response);
-    }
-}
diff --git a/tests/front/controller/visitor/OpenSearchControllerTest.php b/tests/front/controller/visitor/OpenSearchControllerTest.php
deleted file mode 100644
index 42d876c3..00000000
--- a/tests/front/controller/visitor/OpenSearchControllerTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
-createContainer();
-
-        $this->controller = new OpenSearchController($this->container);
-    }
-
-    public function testOpenSearchController(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        // Save RainTPL assigned variables
-        $assignedVariables = [];
-        $this->assignTemplateVars($assignedVariables);
-
-        $result = $this->controller->index($request, $response);
-
-        static::assertSame(200, $result->getStatusCode());
-        static::assertStringContainsString(
-            'application/opensearchdescription+xml',
-            $result->getHeader('Content-Type')[0]
-        );
-        static::assertSame('opensearch', (string) $result->getBody());
-        static::assertSame('http://shaarli/subfolder/', $assignedVariables['serverurl']);
-    }
-}
diff --git a/tests/front/controller/visitor/PictureWallControllerTest.php b/tests/front/controller/visitor/PictureWallControllerTest.php
deleted file mode 100644
index 429e99a2..00000000
--- a/tests/front/controller/visitor/PictureWallControllerTest.php
+++ /dev/null
@@ -1,124 +0,0 @@
-createContainer();
-
-        $this->controller = new PictureWallController($this->container);
-    }
-
-    public function testValidControllerInvokeDefault(): void
-    {
-        $request = $this->createMock(Request::class);
-        $request->expects(static::once())->method('getQueryParams')->willReturn([]);
-        $response = new Response();
-
-        // ConfigManager: thumbnails are enabled
-        $this->container->conf = $this->createMock(ConfigManager::class);
-        $this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) {
-            if ($parameter === 'thumbnails.mode') {
-                return Thumbnailer::MODE_COMMON;
-            }
-
-            return $default;
-        });
-
-        // Save RainTPL assigned variables
-        $assignedVariables = [];
-        $this->assignTemplateVars($assignedVariables);
-
-        // Links dataset: 2 links with thumbnails
-        $this->container->bookmarkService
-            ->expects(static::once())
-            ->method('search')
-            ->willReturnCallback(function (array $parameters, ?string $visibility): SearchResult {
-                // Visibility is set through the container, not the call
-                static::assertNull($visibility);
-
-                // No query parameters
-                if (count($parameters) === 0) {
-                    return SearchResult::getSearchResult([
-                        (new Bookmark())->setId(1)->setUrl('http://url.tld')->setThumbnail('thumb1'),
-                        (new Bookmark())->setId(2)->setUrl('http://url2.tld'),
-                        (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setThumbnail('thumb2'),
-                    ]);
-                }
-            })
-        ;
-
-        // Make sure that PluginManager hook is triggered
-        $this->container->pluginManager
-            ->expects(static::atLeastOnce())
-            ->method('executeHooks')
-            ->withConsecutive(['render_picwall'])
-            ->willReturnCallback(function (string $hook, array $data, array $param): array {
-                if ('render_picwall' === $hook) {
-                    static::assertArrayHasKey('linksToDisplay', $data);
-                    static::assertCount(2, $data['linksToDisplay']);
-                    static::assertSame(1, $data['linksToDisplay'][0]['id']);
-                    static::assertSame(3, $data['linksToDisplay'][1]['id']);
-                    static::assertArrayHasKey('loggedin', $param);
-                }
-
-                return $data;
-            });
-
-        $result = $this->controller->index($request, $response);
-
-        static::assertSame(200, $result->getStatusCode());
-        static::assertSame('picwall', (string) $result->getBody());
-        static::assertSame('Picture wall - Shaarli', $assignedVariables['pagetitle']);
-        static::assertCount(2, $assignedVariables['linksToDisplay']);
-
-        $link = $assignedVariables['linksToDisplay'][0];
-
-        static::assertSame(1, $link['id']);
-        static::assertSame('http://url.tld', $link['url']);
-        static::assertSame('thumb1', $link['thumbnail']);
-
-        $link = $assignedVariables['linksToDisplay'][1];
-
-        static::assertSame(3, $link['id']);
-        static::assertSame('http://url3.tld', $link['url']);
-        static::assertSame('thumb2', $link['thumbnail']);
-    }
-
-    public function testControllerWithThumbnailsDisabled(): void
-    {
-        $this->expectException(ThumbnailsDisabledException::class);
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        // ConfigManager: thumbnails are disabled
-        $this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) {
-            if ($parameter === 'thumbnails.mode') {
-                return Thumbnailer::MODE_NONE;
-            }
-
-            return $default;
-        });
-
-        $this->controller->index($request, $response);
-    }
-}
diff --git a/tests/front/controller/visitor/PublicSessionFilterControllerTest.php b/tests/front/controller/visitor/PublicSessionFilterControllerTest.php
deleted file mode 100644
index 7e3b00af..00000000
--- a/tests/front/controller/visitor/PublicSessionFilterControllerTest.php
+++ /dev/null
@@ -1,122 +0,0 @@
-createContainer();
-
-        $this->controller = new PublicSessionFilterController($this->container);
-    }
-
-    /**
-     * Link per page - Default call with valid parameter and a referer.
-     */
-    public function testLinksPerPage(): void
-    {
-        $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
-
-        $request = $this->createMock(Request::class);
-        $request->method('getParam')->with('nb')->willReturn('8');
-        $response = new Response();
-
-        $this->container->sessionManager
-            ->expects(static::once())
-            ->method('setSessionParameter')
-            ->with(SessionManager::KEY_LINKS_PER_PAGE, 8)
-        ;
-
-        $result = $this->controller->linksPerPage($request, $response);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
-    }
-
-    /**
-     * Link per page - Invalid value, should use default value (20)
-     */
-    public function testLinksPerPageNotValid(): void
-    {
-        $request = $this->createMock(Request::class);
-        $request->method('getParam')->with('nb')->willReturn('test');
-        $response = new Response();
-
-        $this->container->sessionManager
-            ->expects(static::once())
-            ->method('setSessionParameter')
-            ->with(SessionManager::KEY_LINKS_PER_PAGE, 20)
-        ;
-
-        $result = $this->controller->linksPerPage($request, $response);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/'], $result->getHeader('location'));
-    }
-
-    /**
-     * Untagged only - valid call
-     */
-    public function testUntaggedOnly(): void
-    {
-        $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $this->container->sessionManager
-            ->expects(static::once())
-            ->method('setSessionParameter')
-            ->with(SessionManager::KEY_UNTAGGED_ONLY, true)
-        ;
-
-        $result = $this->controller->untaggedOnly($request, $response);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
-    }
-
-    /**
-     * Untagged only - toggle off
-     */
-    public function testUntaggedOnlyToggleOff(): void
-    {
-        $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $this->container->sessionManager
-            ->method('getSessionParameter')
-            ->with(SessionManager::KEY_UNTAGGED_ONLY)
-            ->willReturn(true)
-        ;
-        $this->container->sessionManager
-            ->expects(static::once())
-            ->method('setSessionParameter')
-            ->with(SessionManager::KEY_UNTAGGED_ONLY, false)
-        ;
-
-        $result = $this->controller->untaggedOnly($request, $response);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
-    }
-}
diff --git a/tests/front/controller/visitor/ShaarliVisitorControllerTest.php b/tests/front/controller/visitor/ShaarliVisitorControllerTest.php
deleted file mode 100644
index 61886f7d..00000000
--- a/tests/front/controller/visitor/ShaarliVisitorControllerTest.php
+++ /dev/null
@@ -1,249 +0,0 @@
-createContainer();
-
-        $this->controller = new class ($this->container) extends ShaarliVisitorController
-        {
-            public function assignView(string $key, $value): ShaarliVisitorController
-            {
-                return parent::assignView($key, $value);
-            }
-
-            public function render(string $template): string
-            {
-                return parent::render($template);
-            }
-
-            public function redirectFromReferer(
-                Request $request,
-                Response $response,
-                array $loopTerms = [],
-                array $clearParams = [],
-                string $anchor = null
-            ): Response {
-                return parent::redirectFromReferer($request, $response, $loopTerms, $clearParams, $anchor);
-            }
-        };
-        $this->assignedValues = [];
-
-        $this->request = $this->createMock(Request::class);
-    }
-
-    public function testAssignView(): void
-    {
-        $this->assignTemplateVars($this->assignedValues);
-
-        $self = $this->controller->assignView('variableName', 'variableValue');
-
-        static::assertInstanceOf(ShaarliVisitorController::class, $self);
-        static::assertSame('variableValue', $this->assignedValues['variableName']);
-    }
-
-    public function testRender(): void
-    {
-        $this->assignTemplateVars($this->assignedValues);
-
-        $this->container->bookmarkService
-            ->method('count')
-            ->willReturnCallback(function (string $visibility): int {
-                return $visibility === BookmarkFilter::$PRIVATE ? 5 : 10;
-            })
-        ;
-
-        $this->container->pluginManager
-            ->method('executeHooks')
-            ->willReturnCallback(function (string $hook, array &$data, array $params): array {
-                return $data[$hook] = $params;
-            });
-        $this->container->pluginManager->method('getErrors')->willReturn(['error']);
-
-        $this->container->loginManager->method('isLoggedIn')->willReturn(true);
-
-        $render = $this->controller->render('templateName');
-
-        static::assertSame('templateName', $render);
-
-        static::assertSame('templateName', $this->assignedValues['_PAGE_']);
-        static::assertSame('templateName', $this->assignedValues['template']);
-
-        static::assertSame(10, $this->assignedValues['linkcount']);
-        static::assertSame(5, $this->assignedValues['privateLinkcount']);
-        static::assertSame(['error'], $this->assignedValues['plugin_errors']);
-
-        static::assertSame('templateName', $this->assignedValues['plugins_includes']['render_includes']['target']);
-        static::assertTrue($this->assignedValues['plugins_includes']['render_includes']['loggedin']);
-        static::assertSame('templateName', $this->assignedValues['plugins_header']['render_header']['target']);
-        static::assertTrue($this->assignedValues['plugins_header']['render_header']['loggedin']);
-        static::assertSame('templateName', $this->assignedValues['plugins_footer']['render_footer']['target']);
-        static::assertTrue($this->assignedValues['plugins_footer']['render_footer']['loggedin']);
-    }
-
-    /**
-     * Test redirectFromReferer() - Default behaviour
-     */
-    public function testRedirectFromRefererDefault(): void
-    {
-        $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
-
-        $response = new Response();
-
-        $result = $this->controller->redirectFromReferer($this->request, $response);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/controller?query=param&other=2'], $result->getHeader('location'));
-    }
-
-    /**
-     * Test redirectFromReferer() - With a loop term not matched in the referer
-     */
-    public function testRedirectFromRefererWithUnmatchedLoopTerm(): void
-    {
-        $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
-
-        $response = new Response();
-
-        $result = $this->controller->redirectFromReferer($this->request, $response, ['nope']);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/controller?query=param&other=2'], $result->getHeader('location'));
-    }
-
-    /**
-     * Test redirectFromReferer() - With a loop term matching the referer in its path -> redirect to default
-     */
-    public function testRedirectFromRefererWithMatchingLoopTermInPath(): void
-    {
-        $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
-
-        $response = new Response();
-
-        $result = $this->controller->redirectFromReferer($this->request, $response, ['nope', 'controller']);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/'], $result->getHeader('location'));
-    }
-
-    /**
-     * Test redirectFromReferer() - With a loop term matching the referer in its query parameters -> redirect to default
-     */
-    public function testRedirectFromRefererWithMatchingLoopTermInQueryParam(): void
-    {
-        $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
-
-        $response = new Response();
-
-        $result = $this->controller->redirectFromReferer($this->request, $response, ['nope', 'other']);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/'], $result->getHeader('location'));
-    }
-
-    /**
-     * Test redirectFromReferer() - With a loop term matching the referer in its query value
-     *                              -> we do not block redirection for query parameter values.
-     */
-    public function testRedirectFromRefererWithMatchingLoopTermInQueryValue(): void
-    {
-        $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
-
-        $response = new Response();
-
-        $result = $this->controller->redirectFromReferer($this->request, $response, ['nope', 'param']);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/controller?query=param&other=2'], $result->getHeader('location'));
-    }
-
-    /**
-     * Test redirectFromReferer() - With a loop term matching the referer in its domain name
-     *                              -> we do not block redirection for shaarli's hosts
-     */
-    public function testRedirectFromRefererWithLoopTermInDomain(): void
-    {
-        $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
-
-        $response = new Response();
-
-        $result = $this->controller->redirectFromReferer($this->request, $response, ['shaarli']);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/controller?query=param&other=2'], $result->getHeader('location'));
-    }
-
-    /**
-     * Test redirectFromReferer() - With a loop term matching a query parameter AND clear this query param
-     *                              -> the param should be cleared before checking if it matches the redir loop terms
-     */
-    public function testRedirectFromRefererWithMatchingClearedParam(): void
-    {
-        $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
-
-        $response = new Response();
-
-        $result = $this->controller->redirectFromReferer($this->request, $response, ['query'], ['query']);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/controller?other=2'], $result->getHeader('location'));
-    }
-
-    /**
-     * Test redirectFromReferer() - From another domain -> we ignore the given referrer.
-     */
-    public function testRedirectExternalReferer(): void
-    {
-        $this->container->environment['HTTP_REFERER'] = 'http://other.domain.tld/controller?query=param&other=2';
-
-        $response = new Response();
-
-        $result = $this->controller->redirectFromReferer($this->request, $response, ['query'], ['query']);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/'], $result->getHeader('location'));
-    }
-
-    /**
-     * Test redirectFromReferer() - From another domain -> we ignore the given referrer.
-     */
-    public function testRedirectExternalRefererExplicitDomainName(): void
-    {
-        $this->container->environment['SERVER_NAME'] = 'my.shaarli.tld';
-        $this->container->environment['HTTP_REFERER'] = 'http://your.shaarli.tld/controller?query=param&other=2';
-
-        $response = new Response();
-
-        $result = $this->controller->redirectFromReferer($this->request, $response, ['query'], ['query']);
-
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/'], $result->getHeader('location'));
-    }
-}
diff --git a/tests/front/controller/visitor/TagCloudControllerTest.php b/tests/front/controller/visitor/TagCloudControllerTest.php
deleted file mode 100644
index f2509461..00000000
--- a/tests/front/controller/visitor/TagCloudControllerTest.php
+++ /dev/null
@@ -1,381 +0,0 @@
-createContainer();
-
-        $this->controller = new TagCloudController($this->container);
-    }
-
-    /**
-     * Tag Cloud - default parameters
-     */
-    public function testValidCloudControllerInvokeDefault(): void
-    {
-        $allTags = [
-            'ghi' => 1,
-            'abc' => 3,
-            'def' => 12,
-        ];
-        $expectedOrder = ['abc', 'def', 'ghi'];
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        // Save RainTPL assigned variables
-        $assignedVariables = [];
-        $this->assignTemplateVars($assignedVariables);
-
-        $this->container->bookmarkService
-            ->expects(static::once())
-            ->method('bookmarksCountPerTag')
-            ->with([], null)
-            ->willReturnCallback(function () use ($allTags): array {
-                return $allTags;
-            })
-        ;
-
-        // Make sure that PluginManager hook is triggered
-        $this->container->pluginManager
-            ->expects(static::atLeastOnce())
-            ->method('executeHooks')
-            ->withConsecutive(['render_tagcloud'])
-            ->willReturnCallback(function (string $hook, array $data, array $param): array {
-                if ('render_tagcloud' === $hook) {
-                    static::assertSame('', $data['search_tags']);
-                    static::assertCount(3, $data['tags']);
-
-                    static::assertArrayHasKey('loggedin', $param);
-                }
-
-                return $data;
-            })
-        ;
-
-        $result = $this->controller->cloud($request, $response);
-
-        static::assertSame(200, $result->getStatusCode());
-        static::assertSame('tag.cloud', (string) $result->getBody());
-        static::assertSame('Tag cloud - Shaarli', $assignedVariables['pagetitle']);
-
-        static::assertSame('', $assignedVariables['search_tags']);
-        static::assertCount(3, $assignedVariables['tags']);
-        static::assertSame($expectedOrder, array_keys($assignedVariables['tags']));
-
-        foreach ($allTags as $tag => $count) {
-            static::assertArrayHasKey($tag, $assignedVariables['tags']);
-            static::assertSame($count, $assignedVariables['tags'][$tag]['count']);
-            static::assertGreaterThan(0, $assignedVariables['tags'][$tag]['size']);
-            static::assertLessThan(5, $assignedVariables['tags'][$tag]['size']);
-        }
-    }
-
-    /**
-     * Tag Cloud - Additional parameters:
-     *   - logged in
-     *   - visibility private
-     *   - search tags: `ghi` and `def` (note that filtered tags are not displayed anymore)
-     */
-    public function testValidCloudControllerInvokeWithParameters(): void
-    {
-        $request = $this->createMock(Request::class);
-        $request
-            ->method('getQueryParam')
-            ->with()
-            ->willReturnCallback(function (string $key): ?string {
-                if ('searchtags' === $key) {
-                    return 'ghi@def';
-                }
-
-                return null;
-            })
-        ;
-        $response = new Response();
-
-        // Save RainTPL assigned variables
-        $assignedVariables = [];
-        $this->assignTemplateVars($assignedVariables);
-
-        $this->container->loginManager->method('isLoggedin')->willReturn(true);
-        $this->container->sessionManager->expects(static::once())->method('getSessionParameter')->willReturn('private');
-
-        $this->container->bookmarkService
-            ->expects(static::once())
-            ->method('bookmarksCountPerTag')
-            ->with(['ghi', 'def'], BookmarkFilter::$PRIVATE)
-            ->willReturnCallback(function (): array {
-                return ['abc' => 3];
-            })
-        ;
-
-        // Make sure that PluginManager hook is triggered
-        $this->container->pluginManager
-            ->expects(static::atLeastOnce())
-            ->method('executeHooks')
-            ->withConsecutive(['render_tagcloud'])
-            ->willReturnCallback(function (string $hook, array $data, array $param): array {
-                if ('render_tagcloud' === $hook) {
-                    static::assertSame('ghi@def@', $data['search_tags']);
-                    static::assertCount(1, $data['tags']);
-
-                    static::assertArrayHasKey('loggedin', $param);
-                }
-
-                return $data;
-            })
-        ;
-
-        $result = $this->controller->cloud($request, $response);
-
-        static::assertSame(200, $result->getStatusCode());
-        static::assertSame('tag.cloud', (string) $result->getBody());
-        static::assertSame('ghi def - Tag cloud - Shaarli', $assignedVariables['pagetitle']);
-
-        static::assertSame('ghi@def@', $assignedVariables['search_tags']);
-        static::assertCount(1, $assignedVariables['tags']);
-
-        static::assertArrayHasKey('abc', $assignedVariables['tags']);
-        static::assertSame(3, $assignedVariables['tags']['abc']['count']);
-        static::assertGreaterThan(0, $assignedVariables['tags']['abc']['size']);
-        static::assertLessThan(5, $assignedVariables['tags']['abc']['size']);
-    }
-
-    /**
-     * Tag Cloud - empty
-     */
-    public function testEmptyCloud(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        // Save RainTPL assigned variables
-        $assignedVariables = [];
-        $this->assignTemplateVars($assignedVariables);
-
-        $this->container->bookmarkService
-            ->expects(static::once())
-            ->method('bookmarksCountPerTag')
-            ->with([], null)
-            ->willReturnCallback(function (array $parameters, ?string $visibility): array {
-                return [];
-            })
-        ;
-
-        // Make sure that PluginManager hook is triggered
-        $this->container->pluginManager
-            ->expects(static::atLeastOnce())
-            ->method('executeHooks')
-            ->withConsecutive(['render_tagcloud'])
-            ->willReturnCallback(function (string $hook, array $data, array $param): array {
-                if ('render_tagcloud' === $hook) {
-                    static::assertSame('', $data['search_tags']);
-                    static::assertCount(0, $data['tags']);
-
-                    static::assertArrayHasKey('loggedin', $param);
-                }
-
-                return $data;
-            })
-        ;
-
-        $result = $this->controller->cloud($request, $response);
-
-        static::assertSame(200, $result->getStatusCode());
-        static::assertSame('tag.cloud', (string) $result->getBody());
-        static::assertSame('Tag cloud - Shaarli', $assignedVariables['pagetitle']);
-
-        static::assertSame('', $assignedVariables['search_tags']);
-        static::assertCount(0, $assignedVariables['tags']);
-    }
-
-    /**
-     * Tag List - Default sort is by usage DESC
-     */
-    public function testValidListControllerInvokeDefault(): void
-    {
-        $allTags = [
-            'def' => 12,
-            'abc' => 3,
-            'ghi' => 1,
-        ];
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        // Save RainTPL assigned variables
-        $assignedVariables = [];
-        $this->assignTemplateVars($assignedVariables);
-
-        $this->container->bookmarkService
-            ->expects(static::once())
-            ->method('bookmarksCountPerTag')
-            ->with([], null)
-            ->willReturnCallback(function () use ($allTags): array {
-                return $allTags;
-            })
-        ;
-
-        // Make sure that PluginManager hook is triggered
-        $this->container->pluginManager
-            ->expects(static::atLeastOnce())
-            ->method('executeHooks')
-            ->withConsecutive(['render_taglist'])
-            ->willReturnCallback(function (string $hook, array $data, array $param): array {
-                if ('render_taglist' === $hook) {
-                    static::assertSame('', $data['search_tags']);
-                    static::assertCount(3, $data['tags']);
-
-                    static::assertArrayHasKey('loggedin', $param);
-                }
-
-                return $data;
-            })
-        ;
-
-        $result = $this->controller->list($request, $response);
-
-        static::assertSame(200, $result->getStatusCode());
-        static::assertSame('tag.list', (string) $result->getBody());
-        static::assertSame('Tag list - Shaarli', $assignedVariables['pagetitle']);
-
-        static::assertSame('', $assignedVariables['search_tags']);
-        static::assertCount(3, $assignedVariables['tags']);
-
-        foreach ($allTags as $tag => $count) {
-            static::assertSame($count, $assignedVariables['tags'][$tag]);
-        }
-    }
-
-    /**
-     * Tag List - Additional parameters:
-     *   - logged in
-     *   - visibility private
-     *   - search tags: `ghi` and `def` (note that filtered tags are not displayed anymore)
-     *   - sort alphabetically
-     */
-    public function testValidListControllerInvokeWithParameters(): void
-    {
-        $request = $this->createMock(Request::class);
-        $request
-            ->method('getQueryParam')
-            ->with()
-            ->willReturnCallback(function (string $key): ?string {
-                if ('searchtags' === $key) {
-                    return 'ghi@def';
-                } elseif ('sort' === $key) {
-                    return 'alpha';
-                }
-
-                return null;
-            })
-        ;
-        $response = new Response();
-
-        // Save RainTPL assigned variables
-        $assignedVariables = [];
-        $this->assignTemplateVars($assignedVariables);
-
-        $this->container->loginManager->method('isLoggedin')->willReturn(true);
-        $this->container->sessionManager->expects(static::once())->method('getSessionParameter')->willReturn('private');
-
-        $this->container->bookmarkService
-            ->expects(static::once())
-            ->method('bookmarksCountPerTag')
-            ->with(['ghi', 'def'], BookmarkFilter::$PRIVATE)
-            ->willReturnCallback(function (): array {
-                return ['abc' => 3];
-            })
-        ;
-
-        // Make sure that PluginManager hook is triggered
-        $this->container->pluginManager
-            ->expects(static::atLeastOnce())
-            ->method('executeHooks')
-            ->withConsecutive(['render_taglist'])
-            ->willReturnCallback(function (string $hook, array $data, array $param): array {
-                if ('render_taglist' === $hook) {
-                    static::assertSame('ghi@def@', $data['search_tags']);
-                    static::assertCount(1, $data['tags']);
-
-                    static::assertArrayHasKey('loggedin', $param);
-                }
-
-                return $data;
-            })
-        ;
-
-        $result = $this->controller->list($request, $response);
-
-        static::assertSame(200, $result->getStatusCode());
-        static::assertSame('tag.list', (string) $result->getBody());
-        static::assertSame('ghi def - Tag list - Shaarli', $assignedVariables['pagetitle']);
-
-        static::assertSame('ghi@def@', $assignedVariables['search_tags']);
-        static::assertCount(1, $assignedVariables['tags']);
-        static::assertSame(3, $assignedVariables['tags']['abc']);
-    }
-
-    /**
-     * Tag List - empty
-     */
-    public function testEmptyList(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        // Save RainTPL assigned variables
-        $assignedVariables = [];
-        $this->assignTemplateVars($assignedVariables);
-
-        $this->container->bookmarkService
-            ->expects(static::once())
-            ->method('bookmarksCountPerTag')
-            ->with([], null)
-            ->willReturnCallback(function (array $parameters, ?string $visibility): array {
-                return [];
-            })
-        ;
-
-        // Make sure that PluginManager hook is triggered
-        $this->container->pluginManager
-            ->expects(static::atLeastOnce())
-            ->method('executeHooks')
-            ->withConsecutive(['render_taglist'])
-            ->willReturnCallback(function (string $hook, array $data, array $param): array {
-                if ('render_taglist' === $hook) {
-                    static::assertSame('', $data['search_tags']);
-                    static::assertCount(0, $data['tags']);
-
-                    static::assertArrayHasKey('loggedin', $param);
-                }
-
-                return $data;
-            })
-        ;
-
-        $result = $this->controller->list($request, $response);
-
-        static::assertSame(200, $result->getStatusCode());
-        static::assertSame('tag.list', (string) $result->getBody());
-        static::assertSame('Tag list - Shaarli', $assignedVariables['pagetitle']);
-
-        static::assertSame('', $assignedVariables['search_tags']);
-        static::assertCount(0, $assignedVariables['tags']);
-    }
-}
diff --git a/tests/front/controller/visitor/TagControllerTest.php b/tests/front/controller/visitor/TagControllerTest.php
deleted file mode 100644
index 5a556c6d..00000000
--- a/tests/front/controller/visitor/TagControllerTest.php
+++ /dev/null
@@ -1,215 +0,0 @@
-createContainer();
-
-        $this->controller = new TagController($this->container);
-    }
-
-    public function testAddTagWithReferer(): void
-    {
-        $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/'];
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $tags = ['newTag' => 'abc'];
-
-        $result = $this->controller->addTag($request, $response, $tags);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/controller/?searchtags=abc'], $result->getHeader('location'));
-    }
-
-    public function testAddTagWithRefererAndExistingSearch(): void
-    {
-        $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $tags = ['newTag' => 'abc'];
-
-        $result = $this->controller->addTag($request, $response, $tags);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/controller/?searchtags=def%40abc'], $result->getHeader('location'));
-    }
-
-    public function testAddTagWithoutRefererAndExistingSearch(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $tags = ['newTag' => 'abc'];
-
-        $result = $this->controller->addTag($request, $response, $tags);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/?searchtags=abc'], $result->getHeader('location'));
-    }
-
-    public function testAddTagRemoveLegacyQueryParam(): void
-    {
-        $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&addtag=abc'];
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $tags = ['newTag' => 'abc'];
-
-        $result = $this->controller->addTag($request, $response, $tags);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/controller/?searchtags=def%40abc'], $result->getHeader('location'));
-    }
-
-    public function testAddTagResetPagination(): void
-    {
-        $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&page=12'];
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $tags = ['newTag' => 'abc'];
-
-        $result = $this->controller->addTag($request, $response, $tags);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/controller/?searchtags=def%40abc'], $result->getHeader('location'));
-    }
-
-    public function testAddTagWithRefererAndEmptySearch(): void
-    {
-        $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags='];
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $tags = ['newTag' => 'abc'];
-
-        $result = $this->controller->addTag($request, $response, $tags);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/controller/?searchtags=abc'], $result->getHeader('location'));
-    }
-
-    public function testAddTagWithoutNewTagWithReferer(): void
-    {
-        $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $result = $this->controller->addTag($request, $response, []);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/controller/?searchtags=def'], $result->getHeader('location'));
-    }
-
-    public function testAddTagWithoutNewTagWithoutReferer(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $result = $this->controller->addTag($request, $response, []);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/'], $result->getHeader('location'));
-    }
-
-    public function testRemoveTagWithoutMatchingTag(): void
-    {
-        $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $tags = ['tag' => 'abc'];
-
-        $result = $this->controller->removeTag($request, $response, $tags);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/controller/?searchtags=def'], $result->getHeader('location'));
-    }
-
-    public function testRemoveTagWithoutTagsearch(): void
-    {
-        $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/'];
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $tags = ['tag' => 'abc'];
-
-        $result = $this->controller->removeTag($request, $response, $tags);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/controller/'], $result->getHeader('location'));
-    }
-
-    public function testRemoveTagWithoutReferer(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $tags = ['tag' => 'abc'];
-
-        $result = $this->controller->removeTag($request, $response, $tags);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/'], $result->getHeader('location'));
-    }
-
-    public function testRemoveTagWithoutTag(): void
-    {
-        $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtag=abc'];
-
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $result = $this->controller->removeTag($request, $response, []);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/controller/?searchtag=abc'], $result->getHeader('location'));
-    }
-
-    public function testRemoveTagWithoutTagWithoutReferer(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $result = $this->controller->removeTag($request, $response, []);
-
-        static::assertInstanceOf(Response::class, $result);
-        static::assertSame(302, $result->getStatusCode());
-        static::assertSame(['/subfolder/'], $result->getHeader('location'));
-    }
-}
diff --git a/tests/helper/ApplicationUtilsTest.php b/tests/helper/ApplicationUtilsTest.php
deleted file mode 100644
index 1b4f8204..00000000
--- a/tests/helper/ApplicationUtilsTest.php
+++ /dev/null
@@ -1,414 +0,0 @@
-assertEquals(
-            '0.5.4',
-            ApplicationUtils::getVersion(
-                'https://raw.githubusercontent.com/shaarli/Shaarli/'
-                . 'v0.5.4/shaarli_version.php',
-                $testTimeout
-            )
-        );
-        $this->assertRegExp(
-            self::$versionPattern,
-            ApplicationUtils::getVersion(
-                'https://raw.githubusercontent.com/shaarli/Shaarli/'
-                . 'latest/shaarli_version.php',
-                $testTimeout
-            )
-        );
-    }
-
-    /**
-     * Attempt to retrieve the latest version from an invalid File
-     */
-    public function testGetVersionCodeFromFile()
-    {
-        file_put_contents('sandbox/version.php', '' . PHP_EOL);
-        $this->assertEquals(
-            '1.2.3',
-            ApplicationUtils::getVersion('sandbox/version.php', 1)
-        );
-    }
-
-    /**
-     * Attempt to retrieve the latest version from an invalid File
-     */
-    public function testGetVersionCodeInvalidFile()
-    {
-        $oldlog = ini_get('error_log');
-        ini_set('error_log', '/dev/null');
-        $this->assertFalse(
-            ApplicationUtils::getVersion('idontexist', 1)
-        );
-        ini_set('error_log', $oldlog);
-    }
-
-    /**
-     * Test update checks - the user is logged off
-     */
-    public function testCheckUpdateLoggedOff()
-    {
-        $this->assertFalse(
-            ApplicationUtils::checkUpdate(self::$testVersion, 'null', 0, false, false)
-        );
-    }
-
-    /**
-     * Test update checks - the user has disabled updates
-     */
-    public function testCheckUpdateUserDisabled()
-    {
-        $this->assertFalse(
-            ApplicationUtils::checkUpdate(self::$testVersion, 'null', 0, false, true)
-        );
-    }
-
-    /**
-     * A newer version is available
-     */
-    public function testCheckUpdateNewVersionAvailable()
-    {
-        $newVersion = '1.8.3';
-        FakeApplicationUtils::$VERSION_CODE = $newVersion;
-
-        $version = FakeApplicationUtils::checkUpdate(
-            self::$testVersion,
-            self::$testUpdateFile,
-            100,
-            true,
-            true
-        );
-
-        $this->assertEquals($newVersion, $version);
-    }
-
-    /**
-     * No available information about versions
-     */
-    public function testCheckUpdateNewVersionUnavailable()
-    {
-        $version = FakeApplicationUtils::checkUpdate(
-            self::$testVersion,
-            self::$testUpdateFile,
-            100,
-            true,
-            true
-        );
-
-        $this->assertFalse($version);
-    }
-
-    /**
-     * Test update checks - invalid Git branch
-     */
-    public function testCheckUpdateInvalidGitBranch()
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessageRegExp('/Invalid branch selected for updates/');
-
-        ApplicationUtils::checkUpdate('', 'null', 0, true, true, 'unstable');
-    }
-
-    /**
-     * Shaarli is up-to-date
-     */
-    public function testCheckUpdateNewVersionUpToDate()
-    {
-        FakeApplicationUtils::$VERSION_CODE = self::$testVersion;
-
-        $version = FakeApplicationUtils::checkUpdate(
-            self::$testVersion,
-            self::$testUpdateFile,
-            100,
-            true,
-            true
-        );
-
-        $this->assertFalse($version);
-    }
-
-    /**
-     * Time-traveller's Shaarli
-     */
-    public function testCheckUpdateNewVersionMaartiMcFly()
-    {
-        FakeApplicationUtils::$VERSION_CODE = '0.4.1';
-
-        $version = FakeApplicationUtils::checkUpdate(
-            self::$testVersion,
-            self::$testUpdateFile,
-            100,
-            true,
-            true
-        );
-
-        $this->assertFalse($version);
-    }
-
-    /**
-     * The version has been checked recently and Shaarli is up-to-date
-     */
-    public function testCheckUpdateNewVersionTwiceUpToDate()
-    {
-        FakeApplicationUtils::$VERSION_CODE = self::$testVersion;
-
-        // Create the update file
-        $version = FakeApplicationUtils::checkUpdate(
-            self::$testVersion,
-            self::$testUpdateFile,
-            100,
-            true,
-            true
-        );
-
-        $this->assertFalse($version);
-
-        // Reuse the update file
-        $version = FakeApplicationUtils::checkUpdate(
-            self::$testVersion,
-            self::$testUpdateFile,
-            100,
-            true,
-            true
-        );
-
-        $this->assertFalse($version);
-    }
-
-    /**
-     * The version has been checked recently and Shaarli is outdated
-     */
-    public function testCheckUpdateNewVersionTwiceOutdated()
-    {
-        $newVersion = '1.8.3';
-        FakeApplicationUtils::$VERSION_CODE = $newVersion;
-
-        // Create the update file
-        $version = FakeApplicationUtils::checkUpdate(
-            self::$testVersion,
-            self::$testUpdateFile,
-            100,
-            true,
-            true
-        );
-        $this->assertEquals($newVersion, $version);
-
-        // Reuse the update file
-        $version = FakeApplicationUtils::checkUpdate(
-            self::$testVersion,
-            self::$testUpdateFile,
-            100,
-            true,
-            true
-        );
-        $this->assertEquals($newVersion, $version);
-    }
-
-    /**
-     * Check supported PHP versions
-     */
-    public function testCheckSupportedPHPVersion()
-    {
-        $minVersion = '5.3';
-        $this->assertTrue(ApplicationUtils::checkPHPVersion($minVersion, '5.4.32'));
-        $this->assertTrue(ApplicationUtils::checkPHPVersion($minVersion, '5.5'));
-        $this->assertTrue(ApplicationUtils::checkPHPVersion($minVersion, '5.6.10'));
-    }
-
-    /**
-     * Check a unsupported PHP version
-     */
-    public function testCheckSupportedPHPVersion51()
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessageRegExp('/Your PHP version is obsolete/');
-
-        $this->assertTrue(ApplicationUtils::checkPHPVersion('5.3', '5.1.0'));
-    }
-
-    /**
-     * Check another unsupported PHP version
-     */
-    public function testCheckSupportedPHPVersion52()
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessageRegExp('/Your PHP version is obsolete/');
-
-        $this->assertTrue(ApplicationUtils::checkPHPVersion('5.3', '5.2'));
-    }
-
-    /**
-     * Checks resource permissions for the current Shaarli installation
-     */
-    public function testCheckCurrentResourcePermissions()
-    {
-        $conf = new ConfigManager('');
-        $conf->set('resource.thumbnails_cache', 'cache');
-        $conf->set('resource.config', 'data/config.php');
-        $conf->set('resource.data_dir', 'data');
-        $conf->set('resource.datastore', 'data/datastore.php');
-        $conf->set('resource.ban_file', 'data/ipbans.php');
-        $conf->set('resource.log', 'data/log.txt');
-        $conf->set('resource.page_cache', 'pagecache');
-        $conf->set('resource.raintpl_tmp', 'tmp');
-        $conf->set('resource.raintpl_tpl', 'tpl');
-        $conf->set('resource.theme', 'default');
-        $conf->set('resource.update_check', 'data/lastupdatecheck.txt');
-
-        $this->assertEquals(
-            [],
-            ApplicationUtils::checkResourcePermissions($conf)
-        );
-    }
-
-    /**
-     * Checks resource permissions for a non-existent Shaarli installation
-     */
-    public function testCheckCurrentResourcePermissionsErrors()
-    {
-        $conf = new ConfigManager('');
-        $conf->set('resource.thumbnails_cache', 'null/cache');
-        $conf->set('resource.config', 'null/data/config.php');
-        $conf->set('resource.data_dir', 'null/data');
-        $conf->set('resource.datastore', 'null/data/store.php');
-        $conf->set('resource.ban_file', 'null/data/ipbans.php');
-        $conf->set('resource.log', 'null/data/log.txt');
-        $conf->set('resource.page_cache', 'null/pagecache');
-        $conf->set('resource.raintpl_tmp', 'null/tmp');
-        $conf->set('resource.raintpl_tpl', 'null/tpl');
-        $conf->set('resource.raintpl_theme', 'null/tpl/default');
-        $conf->set('resource.update_check', 'null/data/lastupdatecheck.txt');
-        $this->assertEquals(
-            [
-                '"null/tpl" directory is not readable',
-                '"null/tpl/default" directory is not readable',
-                '"null/cache" directory is not readable',
-                '"null/cache" directory is not writable',
-                '"null/data" directory is not readable',
-                '"null/data" directory is not writable',
-                '"null/pagecache" directory is not readable',
-                '"null/pagecache" directory is not writable',
-                '"null/tmp" directory is not readable',
-                '"null/tmp" directory is not writable'
-            ],
-            ApplicationUtils::checkResourcePermissions($conf)
-        );
-    }
-
-    /**
-     * Checks resource permissions in minimal mode.
-     */
-    public function testCheckCurrentResourcePermissionsErrorsMinimalMode(): void
-    {
-        $conf = new ConfigManager('');
-        $conf->set('resource.thumbnails_cache', 'null/cache');
-        $conf->set('resource.config', 'null/data/config.php');
-        $conf->set('resource.data_dir', 'null/data');
-        $conf->set('resource.datastore', 'null/data/store.php');
-        $conf->set('resource.ban_file', 'null/data/ipbans.php');
-        $conf->set('resource.log', 'null/data/log.txt');
-        $conf->set('resource.page_cache', 'null/pagecache');
-        $conf->set('resource.raintpl_tmp', 'null/tmp');
-        $conf->set('resource.raintpl_tpl', 'null/tpl');
-        $conf->set('resource.raintpl_theme', 'null/tpl/default');
-        $conf->set('resource.update_check', 'null/data/lastupdatecheck.txt');
-
-        static::assertSame(
-            [
-                '"null/tpl" directory is not readable',
-                '"null/tpl/default" directory is not readable',
-                '"null/tmp" directory is not readable',
-                '"null/tmp" directory is not writable'
-            ],
-            ApplicationUtils::checkResourcePermissions($conf, true)
-        );
-    }
-
-    /**
-     * Check update with 'dev' as curent version (master branch).
-     * It should always return false.
-     */
-    public function testCheckUpdateDev()
-    {
-        $this->assertFalse(
-            ApplicationUtils::checkUpdate('dev', self::$testUpdateFile, 100, true, true)
-        );
-    }
-
-    /**
-     * Basic test of getPhpExtensionsRequirement()
-     */
-    public function testGetPhpExtensionsRequirementSimple(): void
-    {
-        static::assertCount(8, ApplicationUtils::getPhpExtensionsRequirement());
-        static::assertSame([
-            'name' => 'json',
-            'required' => true,
-            'desc' => 'Configuration parsing',
-            'loaded' => true,
-        ], ApplicationUtils::getPhpExtensionsRequirement()[0]);
-    }
-
-    /**
-     * Test getPhpEol with a known version: 7.4 -> 2022
-     */
-    public function testGetKnownPhpEol(): void
-    {
-        static::assertSame('2022-11-28', ApplicationUtils::getPhpEol('7.4.7'));
-    }
-
-    /**
-     * Test getPhpEol with an unknown version: 7.4 -> 2022
-     */
-    public function testGetUnknownPhpEol(): void
-    {
-        static::assertSame(
-            (((int) (new \DateTime())->format('Y')) + 2) . (new \DateTime())->format('-m-d'),
-            ApplicationUtils::getPhpEol('7.51.34')
-        );
-    }
-}
diff --git a/tests/helper/DailyPageHelperTest.php b/tests/helper/DailyPageHelperTest.php
deleted file mode 100644
index 8dab908d..00000000
--- a/tests/helper/DailyPageHelperTest.php
+++ /dev/null
@@ -1,344 +0,0 @@
-createMock(Request::class);
-        $request->method('getQueryParam')->willReturnCallback(function ($key) use ($queryParams): ?string {
-            return $queryParams[$key] ?? null;
-        });
-
-        $type = DailyPageHelper::extractRequestedType($request);
-
-        static::assertSame($type, $expectedType);
-    }
-
-    /**
-     * @dataProvider getRequestedDateTimes
-     */
-    public function testExtractRequestedDateTime(
-        string $type,
-        string $input,
-        ?Bookmark $bookmark,
-        DateTimeInterface $expectedDateTime,
-        string $compareFormat = 'Ymd'
-    ): void {
-        $dateTime = DailyPageHelper::extractRequestedDateTime($type, $input, $bookmark);
-
-        static::assertSame($dateTime->format($compareFormat), $expectedDateTime->format($compareFormat));
-    }
-
-    public function testExtractRequestedDateTimeExceptionUnknownType(): void
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessage('Unsupported daily format type');
-
-        DailyPageHelper::extractRequestedDateTime('nope', null, null);
-    }
-
-    /**
-     * @dataProvider getFormatsByType
-     */
-    public function testGetFormatByType(string $type, string $expectedFormat): void
-    {
-        $format = DailyPageHelper::getFormatByType($type);
-
-        static::assertSame($expectedFormat, $format);
-    }
-
-    public function testGetFormatByTypeExceptionUnknownType(): void
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessage('Unsupported daily format type');
-
-        DailyPageHelper::getFormatByType('nope');
-    }
-
-    /**
-     * @dataProvider getStartDatesByType
-     */
-    public function testGetStartDatesByType(
-        string $type,
-        DateTimeImmutable $dateTime,
-        DateTimeInterface $expectedDateTime
-    ): void {
-        $startDateTime = DailyPageHelper::getStartDateTimeByType($type, $dateTime);
-
-        static::assertEquals($expectedDateTime, $startDateTime);
-    }
-
-    public function testGetStartDatesByTypeExceptionUnknownType(): void
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessage('Unsupported daily format type');
-
-        DailyPageHelper::getStartDateTimeByType('nope', new DateTimeImmutable());
-    }
-
-    /**
-     * @dataProvider getEndDatesByType
-     */
-    public function testGetEndDatesByType(
-        string $type,
-        DateTimeImmutable $dateTime,
-        DateTimeInterface $expectedDateTime
-    ): void {
-        $endDateTime = DailyPageHelper::getEndDateTimeByType($type, $dateTime);
-
-        static::assertEquals($expectedDateTime, $endDateTime);
-    }
-
-    public function testGetEndDatesByTypeExceptionUnknownType(): void
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessage('Unsupported daily format type');
-
-        DailyPageHelper::getEndDateTimeByType('nope', new DateTimeImmutable());
-    }
-
-    /**
-     * @dataProvider getDescriptionsByType
-     */
-    public function testGeDescriptionsByType(
-        string $type,
-        DateTimeImmutable $dateTime,
-        string $expectedDescription
-    ): void {
-        $description = DailyPageHelper::getDescriptionByType($type, $dateTime);
-
-        static::assertEquals($expectedDescription, $description);
-    }
-
-    /**
-     * @dataProvider getDescriptionsByTypeNotIncludeRelative
-     */
-    public function testGeDescriptionsByTypeNotIncludeRelative(
-        string $type,
-        \DateTimeImmutable $dateTime,
-        string $expectedDescription
-    ): void {
-        $description = DailyPageHelper::getDescriptionByType($type, $dateTime, false);
-
-        static::assertEquals($expectedDescription, $description);
-    }
-
-    public function getDescriptionByTypeExceptionUnknownType(): void
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessage('Unsupported daily format type');
-
-        DailyPageHelper::getDescriptionByType('nope', new DateTimeImmutable());
-    }
-
-    /**
-     * @dataProvider getRssLengthsByType
-     */
-    public function testGeRssLengthsByType(string $type): void
-    {
-        $length = DailyPageHelper::getRssLengthByType($type);
-
-        static::assertIsInt($length);
-    }
-
-    public function testGeRssLengthsByTypeExceptionUnknownType(): void
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessage('Unsupported daily format type');
-
-        DailyPageHelper::getRssLengthByType('nope');
-    }
-
-    /**
-     * @dataProvider getCacheDatePeriodByType
-     */
-    public function testGetCacheDatePeriodByType(
-        string $type,
-        DateTimeImmutable $requested,
-        DateTimeInterface $start,
-        DateTimeInterface $end
-    ): void {
-        $period = DailyPageHelper::getCacheDatePeriodByType($type, $requested);
-
-        static::assertEquals($start, $period->getStartDate());
-        static::assertEquals($end, $period->getEndDate());
-    }
-
-    public function testGetCacheDatePeriodByTypeExceptionUnknownType(): void
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessage('Unsupported daily format type');
-
-        DailyPageHelper::getCacheDatePeriodByType('nope');
-    }
-
-    /**
-     * Data provider for testExtractRequestedType() test method.
-     */
-    public function getRequestedTypes(): array
-    {
-        return [
-            [['month' => null], DailyPageHelper::DAY],
-            [['month' => ''], DailyPageHelper::MONTH],
-            [['month' => 'content'], DailyPageHelper::MONTH],
-            [['week' => null], DailyPageHelper::DAY],
-            [['week' => ''], DailyPageHelper::WEEK],
-            [['week' => 'content'], DailyPageHelper::WEEK],
-            [['day' => null], DailyPageHelper::DAY],
-            [['day' => ''], DailyPageHelper::DAY],
-            [['day' => 'content'], DailyPageHelper::DAY],
-        ];
-    }
-
-    /**
-     * Data provider for testExtractRequestedDateTime() test method.
-     */
-    public function getRequestedDateTimes(): array
-    {
-        return [
-            [DailyPageHelper::DAY, '20201013', null, new DateTime('2020-10-13')],
-            [
-                DailyPageHelper::DAY,
-                '',
-                (new Bookmark())->setCreated($date = new DateTime('2020-10-13 12:05:31')),
-                $date,
-            ],
-            [DailyPageHelper::DAY, '', null, new DateTime()],
-            [DailyPageHelper::WEEK, '202030', null, new DateTime('2020-07-20')],
-            [
-                DailyPageHelper::WEEK,
-                '',
-                (new Bookmark())->setCreated($date = new DateTime('2020-10-13 12:05:31')),
-                new DateTime('2020-10-13'),
-            ],
-            [DailyPageHelper::WEEK, '', null, new DateTime(), 'Ym'],
-            [DailyPageHelper::MONTH, '202008', null, new DateTime('2020-08-01'), 'Ym'],
-            [
-                DailyPageHelper::MONTH,
-                '',
-                (new Bookmark())->setCreated($date = new DateTime('2020-10-13 12:05:31')),
-                new DateTime('2020-10-13'),
-                'Ym'
-            ],
-            [DailyPageHelper::MONTH, '', null, new DateTime(), 'Ym'],
-        ];
-    }
-
-    /**
-     * Data provider for testGetFormatByType() test method.
-     */
-    public function getFormatsByType(): array
-    {
-        return [
-            [DailyPageHelper::DAY, 'Ymd'],
-            [DailyPageHelper::WEEK, 'YW'],
-            [DailyPageHelper::MONTH, 'Ym'],
-        ];
-    }
-
-    /**
-     * Data provider for testGetStartDatesByType() test method.
-     */
-    public function getStartDatesByType(): array
-    {
-        return [
-            [DailyPageHelper::DAY, new DateTimeImmutable('2020-10-09 04:05:06'), new DateTime('2020-10-09 00:00:00')],
-            [DailyPageHelper::WEEK, new DateTimeImmutable('2020-10-09 04:05:06'), new DateTime('2020-10-05 00:00:00')],
-            [DailyPageHelper::MONTH, new DateTimeImmutable('2022-03-30 04:05:06'), new DateTime('2022-03-01 00:00:00')],
-        ];
-    }
-
-    /**
-     * Data provider for testGetEndDatesByType() test method.
-     */
-    public function getEndDatesByType(): array
-    {
-        return [
-            [DailyPageHelper::DAY, new DateTimeImmutable('2020-10-09 04:05:06'), new DateTime('2020-10-09 23:59:59')],
-            [DailyPageHelper::WEEK, new DateTimeImmutable('2020-10-09 04:05:06'), new DateTime('2020-10-11 23:59:59')],
-            [DailyPageHelper::MONTH, new DateTimeImmutable('2022-02-28 04:05:06'), new DateTime('2022-02-28 23:59:59')],
-            [DailyPageHelper::MONTH, new DateTimeImmutable('2022-03-30 04:05:06'), new DateTime('2022-03-31 23:59:59')],
-        ];
-    }
-
-    /**
-     * Data provider for testGetDescriptionsByType() test method.
-     */
-    public function getDescriptionsByType(): array
-    {
-        return [
-            [DailyPageHelper::DAY, $date = new DateTimeImmutable(), 'Today - ' . $date->format('F j, Y')],
-            [DailyPageHelper::DAY, $date = new DateTimeImmutable('-1 day'), 'Yesterday - ' . $date->format('F j, Y')],
-            [DailyPageHelper::DAY, new DateTimeImmutable('2020-10-09 04:05:06'), 'October 9, 2020'],
-            [DailyPageHelper::WEEK, new DateTimeImmutable('2020-10-09 04:05:06'), 'Week 41 (October 5, 2020)'],
-            [DailyPageHelper::MONTH, new DateTimeImmutable('2020-10-09 04:05:06'), 'October, 2020'],
-        ];
-    }
-
-    /**
-     * Data provider for testGeDescriptionsByTypeNotIncludeRelative() test method.
-     */
-    public function getDescriptionsByTypeNotIncludeRelative(): array
-    {
-        return [
-            [DailyPageHelper::DAY, $date = new \DateTimeImmutable(), $date->format('F j, Y')],
-            [DailyPageHelper::DAY, $date = new \DateTimeImmutable('-1 day'), $date->format('F j, Y')],
-            [DailyPageHelper::DAY, new \DateTimeImmutable('2020-10-09 04:05:06'), 'October 9, 2020'],
-            [DailyPageHelper::WEEK, new \DateTimeImmutable('2020-10-09 04:05:06'), 'Week 41 (October 5, 2020)'],
-            [DailyPageHelper::MONTH, new \DateTimeImmutable('2020-10-09 04:05:06'), 'October, 2020'],
-        ];
-    }
-
-    /**
-     * Data provider for testGetRssLengthsByType() test method.
-     */
-    public function getRssLengthsByType(): array
-    {
-        return [
-            [DailyPageHelper::DAY],
-            [DailyPageHelper::WEEK],
-            [DailyPageHelper::MONTH],
-        ];
-    }
-
-    /**
-     * Data provider for testGetCacheDatePeriodByType() test method.
-     */
-    public function getCacheDatePeriodByType(): array
-    {
-        return [
-            [
-                DailyPageHelper::DAY,
-                new DateTimeImmutable('2020-10-09 04:05:06'),
-                new DateTime('2020-10-09 00:00:00'),
-                new DateTime('2020-10-09 23:59:59'),
-            ],
-            [
-                DailyPageHelper::WEEK,
-                new DateTimeImmutable('2020-10-09 04:05:06'),
-                new DateTime('2020-10-05 00:00:00'),
-                new DateTime('2020-10-11 23:59:59'),
-            ],
-            [
-                DailyPageHelper::MONTH,
-                new DateTimeImmutable('2020-10-09 04:05:06'),
-                new DateTime('2020-10-01 00:00:00'),
-                new DateTime('2020-10-31 23:59:59'),
-            ],
-        ];
-    }
-}
diff --git a/tests/helper/FileUtilsTest.php b/tests/helper/FileUtilsTest.php
deleted file mode 100644
index 8035f79c..00000000
--- a/tests/helper/FileUtilsTest.php
+++ /dev/null
@@ -1,197 +0,0 @@
-assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
-        $this->assertTrue(startsWith(file_get_contents(self::$file), 'assertEquals($data, FileUtils::readFlatDB(self::$file));
-
-        $data = 0;
-        $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
-        $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
-
-        $data = null;
-        $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
-        $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
-
-        $data = false;
-        $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
-        $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
-    }
-
-    /**
-     * File not writable: raise an exception.
-     */
-    public function testWriteWithoutPermission()
-    {
-        $this->expectException(\Shaarli\Exceptions\IOException::class);
-        $this->expectExceptionMessage('Error accessing "sandbox/flat.db"');
-
-        touch(self::$file);
-        chmod(self::$file, 0440);
-        FileUtils::writeFlatDB(self::$file, null);
-    }
-
-    /**
-     * Folder non existent: raise an exception.
-     */
-    public function testWriteFolderDoesNotExist()
-    {
-        $this->expectException(\Shaarli\Exceptions\IOException::class);
-        $this->expectExceptionMessage('Error accessing "nopefolder"');
-
-        FileUtils::writeFlatDB('nopefolder/file', null);
-    }
-
-    /**
-     * Folder non writable: raise an exception.
-     */
-    public function testWriteFolderPermission()
-    {
-        $this->expectException(\Shaarli\Exceptions\IOException::class);
-        $this->expectExceptionMessage('Error accessing "sandbox"');
-
-        chmod(dirname(self::$file), 0555);
-        try {
-            FileUtils::writeFlatDB(self::$file, null);
-        } catch (Exception $e) {
-            chmod(dirname(self::$file), 0755);
-            throw $e;
-        }
-    }
-
-    /**
-     * Read non existent file, use default parameter.
-     */
-    public function testReadNotExistentFile()
-    {
-        $this->assertEquals(null, FileUtils::readFlatDB(self::$file));
-        $this->assertEquals(['test'], FileUtils::readFlatDB(self::$file, ['test']));
-    }
-
-    /**
-     * Read non readable file, use default parameter.
-     */
-    public function testReadNotReadable()
-    {
-        touch(self::$file);
-        chmod(self::$file, 0220);
-        $this->assertEquals(null, FileUtils::readFlatDB(self::$file));
-        $this->assertEquals(['test'], FileUtils::readFlatDB(self::$file, ['test']));
-    }
-
-    /**
-     * Test clearFolder with self delete and excluded files
-     */
-    public function testClearFolderSelfDeleteWithExclusion(): void
-    {
-        FileUtils::clearFolder('sandbox', true, ['file2']);
-
-        static::assertFileExists('sandbox/folder1/file2');
-        static::assertFileExists('sandbox/folder1');
-        static::assertFileExists('sandbox/file2');
-        static::assertFileExists('sandbox');
-
-        static::assertFileNotExists('sandbox/folder1/file1');
-        static::assertFileNotExists('sandbox/file1');
-        static::assertFileNotExists('sandbox/folder3');
-    }
-
-    /**
-     * Test clearFolder with self delete and excluded files
-     */
-    public function testClearFolderSelfDeleteWithoutExclusion(): void
-    {
-        FileUtils::clearFolder('sandbox', true);
-
-        static::assertFileNotExists('sandbox');
-    }
-
-    /**
-     * Test clearFolder with self delete and excluded files
-     */
-    public function testClearFolderNoSelfDeleteWithoutExclusion(): void
-    {
-        FileUtils::clearFolder('sandbox', false);
-
-        static::assertFileExists('sandbox');
-
-        // 2 because '.' and '..'
-        static::assertCount(2, new \DirectoryIterator('sandbox'));
-    }
-
-    /**
-     * Test clearFolder on a file instead of a folder
-     */
-    public function testClearFolderOnANonDirectory(): void
-    {
-        $this->expectException(IOException::class);
-        $this->expectExceptionMessage('Provided path is not a directory.');
-
-        FileUtils::clearFolder('sandbox/file1', false);
-    }
-
-    /**
-     * Test clearFolder on a file instead of a folder
-     */
-    public function testClearFolderOutsideOfShaarliDirectory(): void
-    {
-        $this->expectException(IOException::class);
-        $this->expectExceptionMessage('Trying to delete a folder outside of Shaarli path.');
-
-
-        FileUtils::clearFolder('/tmp/shaarli-to-delete', true);
-    }
-}
diff --git a/tests/http/HttpUtils/ClientIpIdTest.php b/tests/http/HttpUtils/ClientIpIdTest.php
deleted file mode 100644
index acb2e9d4..00000000
--- a/tests/http/HttpUtils/ClientIpIdTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-assertEquals(
-            '10.1.167.42',
-            client_ip_id(['REMOTE_ADDR' => '10.1.167.42'])
-        );
-    }
-
-    /**
-     * Get a remote client ID based on its IP and proxy information (1)
-     */
-    public function testClientIpIdRemoteForwarded()
-    {
-        $this->assertEquals(
-            '10.1.167.42_127.0.1.47',
-            client_ip_id([
-                'REMOTE_ADDR' => '10.1.167.42',
-                'HTTP_X_FORWARDED_FOR' => '127.0.1.47'
-            ])
-        );
-    }
-
-    /**
-     * Get a remote client ID based on its IP and proxy information (2)
-     */
-    public function testClientIpIdRemoteForwardedClient()
-    {
-        $this->assertEquals(
-            '10.1.167.42_10.1.167.56_127.0.1.47',
-            client_ip_id([
-                'REMOTE_ADDR' => '10.1.167.42',
-                'HTTP_X_FORWARDED_FOR' => '10.1.167.56',
-                'HTTP_CLIENT_IP' => '127.0.1.47'
-            ])
-        );
-    }
-}
diff --git a/tests/http/HttpUtils/GetHttpUrlTest.php b/tests/http/HttpUtils/GetHttpUrlTest.php
deleted file mode 100644
index 3e0b0322..00000000
--- a/tests/http/HttpUtils/GetHttpUrlTest.php
+++ /dev/null
@@ -1,66 +0,0 @@
-assertEquals('Invalid HTTP UrlUtils', $headers[0]);
-        $this->assertFalse($content);
-
-        // Non HTTP
-        list($headers, $content) = get_http_response('ftp://save.tld/mysave', 1);
-        $this->assertEquals('Invalid HTTP UrlUtils', $headers[0]);
-        $this->assertFalse($content);
-    }
-
-    /**
-     * Get an invalid remote URL
-     */
-    public function testGetInvalidRemoteUrl()
-    {
-        list($headers, $content) = @get_http_response('http://non.existent', 1);
-        $this->assertFalse($headers);
-        $this->assertFalse($content);
-    }
-
-    /**
-     * Test getAbsoluteUrl with relative target URL.
-     */
-    public function testGetAbsoluteUrlWithRelative()
-    {
-        $origin = 'http://non.existent/blabla/?test';
-        $target = '/stuff.php';
-
-        $expected = 'http://non.existent/stuff.php';
-        $this->assertEquals($expected, getAbsoluteUrl($origin, $target));
-
-        $target = 'stuff.php';
-        $expected = 'http://non.existent/blabla/stuff.php';
-        $this->assertEquals($expected, getAbsoluteUrl($origin, $target));
-    }
-
-    /**
-     * Test getAbsoluteUrl with absolute target URL.
-     */
-    public function testGetAbsoluteUrlWithAbsolute()
-    {
-        $origin = 'http://non.existent/blabla/?test';
-        $target = 'http://other.url/stuff.php';
-
-        $this->assertEquals($target, getAbsoluteUrl($origin, $target));
-    }
-}
diff --git a/tests/http/HttpUtils/GetIpAdressFromProxyTest.php b/tests/http/HttpUtils/GetIpAdressFromProxyTest.php
deleted file mode 100644
index a96a6802..00000000
--- a/tests/http/HttpUtils/GetIpAdressFromProxyTest.php
+++ /dev/null
@@ -1,58 +0,0 @@
-assertFalse(getIpAddressFromProxy([], []));
-    }
-
-    /**
-     * Test with a single IP in proxy header.
-     */
-    public function testWithOneForwardedIp()
-    {
-        $ip = '1.1.1.1';
-        $server = ['HTTP_X_FORWARDED_FOR' => $ip];
-        $this->assertEquals($ip, getIpAddressFromProxy($server, []));
-    }
-
-    /**
-     * Test with a multiple IPs in proxy header.
-     */
-    public function testWithMultipleForwardedIp()
-    {
-        $ip = '1.1.1.1';
-        $ip2 = '2.2.2.2';
-
-        $server = ['HTTP_X_FORWARDED_FOR' => $ip . ',' . $ip2];
-        $this->assertEquals($ip2, getIpAddressFromProxy($server, []));
-
-        $server = ['HTTP_X_FORWARDED_FOR' => $ip . ' ,   ' . $ip2];
-        $this->assertEquals($ip2, getIpAddressFromProxy($server, []));
-    }
-
-    /**
-     * Test with a trusted IP address.
-     */
-    public function testWithTrustedIp()
-    {
-        $ip = '1.1.1.1';
-        $ip2 = '2.2.2.2';
-
-        $server = ['HTTP_X_FORWARDED_FOR' => $ip];
-        $this->assertFalse(getIpAddressFromProxy($server, [$ip]));
-
-        $server = ['HTTP_X_FORWARDED_FOR' => $ip . ',' . $ip2];
-        $this->assertEquals($ip2, getIpAddressFromProxy($server, [$ip]));
-        $this->assertFalse(getIpAddressFromProxy($server, [$ip, $ip2]));
-    }
-}
diff --git a/tests/http/HttpUtils/IndexUrlTest.php b/tests/http/HttpUtils/IndexUrlTest.php
deleted file mode 100644
index 94b8ff8a..00000000
--- a/tests/http/HttpUtils/IndexUrlTest.php
+++ /dev/null
@@ -1,139 +0,0 @@
-assertEquals(
-            'http://host.tld/',
-            index_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/index.php'
-                ]
-            )
-        );
-
-        $this->assertEquals(
-            'http://host.tld/admin/',
-            index_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/admin/index.php'
-                ]
-            )
-        );
-    }
-
-    /**
-     * The resource is != "index.php"
-     */
-    public function testOtherResource()
-    {
-        $this->assertEquals(
-            'http://host.tld/page.php',
-            page_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/page.php'
-                ]
-            )
-        );
-
-        $this->assertEquals(
-            'http://host.tld/admin/page.php',
-            page_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/admin/page.php'
-                ]
-            )
-        );
-    }
-
-    /**
-     * The route is stored in REQUEST_URI
-     */
-    public function testPageUrlWithRoute()
-    {
-        $this->assertEquals(
-            'http://host.tld/picture-wall',
-            page_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/index.php',
-                    'REQUEST_URI' => '/picture-wall',
-                ]
-            )
-        );
-
-        $this->assertEquals(
-            'http://host.tld/admin/picture-wall',
-            page_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/admin/index.php',
-                    'REQUEST_URI' => '/admin/picture-wall',
-                ]
-            )
-        );
-    }
-
-    /**
-     * The route is stored in REQUEST_URI and subfolder
-     */
-    public function testPageUrlWithRouteUnderSubfolder()
-    {
-        $this->assertEquals(
-            'http://host.tld/subfolder/picture-wall',
-            page_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/subfolder/index.php',
-                    'REQUEST_URI' => '/subfolder/picture-wall',
-                ]
-            )
-        );
-
-        $this->assertEquals(
-            'http://host.tld/subfolder/admin/picture-wall',
-            page_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/subfolder/admin/index.php',
-                    'REQUEST_URI' => '/subfolder/admin/picture-wall',
-                ]
-            )
-        );
-    }
-}
diff --git a/tests/http/HttpUtils/IndexUrlTestWithConstant.php b/tests/http/HttpUtils/IndexUrlTestWithConstant.php
deleted file mode 100644
index 67c4668d..00000000
--- a/tests/http/HttpUtils/IndexUrlTestWithConstant.php
+++ /dev/null
@@ -1,51 +0,0 @@
-assertEquals(
-            'http://other-host.tld/subfolder/',
-            index_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/index.php',
-                    'REQUEST_URI' => '/picture-wall',
-                ]
-            )
-        );
-
-        $this->assertEquals(
-            'http://other-host.tld/subfolder/',
-            index_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/admin/index.php',
-                    'REQUEST_URI' => '/admin/picture-wall',
-                ]
-            )
-        );
-    }
-}
diff --git a/tests/http/HttpUtils/IsHttpsTest.php b/tests/http/HttpUtils/IsHttpsTest.php
deleted file mode 100644
index 397ec29d..00000000
--- a/tests/http/HttpUtils/IsHttpsTest.php
+++ /dev/null
@@ -1,36 +0,0 @@
-assertTrue(is_https(['HTTPS' => true]));
-        $this->assertTrue(is_https(['HTTPS' => '1']));
-        $this->assertTrue(is_https(['HTTPS' => false, 'HTTP_X_FORWARDED_PORT' => 443]));
-        $this->assertTrue(is_https(['HTTPS' => false, 'HTTP_X_FORWARDED_PORT' => '443']));
-        $this->assertTrue(is_https(['HTTPS' => false, 'HTTP_X_FORWARDED_PORT' => '443,123,456,']));
-    }
-
-    /**
-     * Test is_https with HTTP values.
-     */
-    public function testIsHttpsFalse()
-    {
-        $this->assertFalse(is_https([]));
-        $this->assertFalse(is_https(['HTTPS' => false]));
-        $this->assertFalse(is_https(['HTTPS' => '0']));
-        $this->assertFalse(is_https(['HTTPS' => false, 'HTTP_X_FORWARDED_PORT' => 123]));
-        $this->assertFalse(is_https(['HTTPS' => false, 'HTTP_X_FORWARDED_PORT' => '123']));
-        $this->assertFalse(is_https(['HTTPS' => false, 'HTTP_X_FORWARDED_PORT' => ',123,456,']));
-    }
-}
diff --git a/tests/http/HttpUtils/PageUrlTest.php b/tests/http/HttpUtils/PageUrlTest.php
deleted file mode 100644
index bf882e5b..00000000
--- a/tests/http/HttpUtils/PageUrlTest.php
+++ /dev/null
@@ -1,77 +0,0 @@
-assertEquals(
-            'http://host.tld/?p1=v1&p2=v2',
-            page_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/index.php',
-                    'QUERY_STRING' => 'p1=v1&p2=v2'
-                ]
-            )
-        );
-
-        $this->assertEquals(
-            'http://host.tld/admin/?action=edit_tag',
-            page_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/admin/index.php',
-                    'QUERY_STRING' => 'action=edit_tag'
-                ]
-            )
-        );
-    }
-
-    /**
-     * The resource is != "index.php"
-     */
-    public function testOtherResource()
-    {
-        $this->assertEquals(
-            'http://host.tld/page.php?p1=v1&p2=v2',
-            page_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/page.php',
-                    'QUERY_STRING' => 'p1=v1&p2=v2'
-                ]
-            )
-        );
-
-        $this->assertEquals(
-            'http://host.tld/admin/page.php?action=edit_tag',
-            page_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'SCRIPT_NAME' => '/admin/page.php',
-                    'QUERY_STRING' => 'action=edit_tag'
-                ]
-            )
-        );
-    }
-}
diff --git a/tests/http/HttpUtils/ServerUrlTest.php b/tests/http/HttpUtils/ServerUrlTest.php
deleted file mode 100644
index 5a4f9252..00000000
--- a/tests/http/HttpUtils/ServerUrlTest.php
+++ /dev/null
@@ -1,222 +0,0 @@
-assertEquals(
-            'https://host.tld',
-            server_url(
-                [
-                    'HTTPS' => 'ON',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '443'
-                ]
-            )
-        );
-
-        $this->assertEquals(
-            'https://host.tld:8080',
-            server_url(
-                [
-                    'HTTPS' => 'ON',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '8080'
-                ]
-            )
-        );
-    }
-
-    /**
-     * Detect a Proxy that sets Forwarded-Host
-     */
-    public function testHttpsProxyForwardedHost()
-    {
-        $this->assertEquals(
-            'https://host.tld:8080',
-            server_url(
-                [
-                    'HTTP_X_FORWARDED_PROTO' => 'https',
-                    'HTTP_X_FORWARDED_PORT' => '8080',
-                    'HTTP_X_FORWARDED_HOST' => 'host.tld'
-                ]
-            )
-        );
-
-        $this->assertEquals(
-            'https://host.tld:4974',
-            server_url(
-                [
-                    'HTTP_X_FORWARDED_PROTO' => 'https, https',
-                    'HTTP_X_FORWARDED_PORT' => '4974, 80',
-                    'HTTP_X_FORWARDED_HOST' => 'host.tld, example.com'
-                ]
-            )
-        );
-    }
-
-    /**
-     * Detect a Proxy with SSL enabled
-     */
-    public function testHttpsProxyForward()
-    {
-        $this->assertEquals(
-            'https://host.tld:8080',
-            server_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'HTTP_X_FORWARDED_PROTO' => 'https',
-                    'HTTP_X_FORWARDED_PORT' => '8080'
-                ]
-            )
-        );
-
-        $this->assertEquals(
-            'https://host.tld',
-            server_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'HTTP_X_FORWARDED_PROTO' => 'https'
-                ]
-            )
-        );
-
-        $this->assertEquals(
-            'https://host.tld',
-            server_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'HTTP_X_FORWARDED_PROTO' => 'https',
-                    'HTTP_X_FORWARDED_PORT' => '443'
-                ]
-            )
-        );
-
-        $this->assertEquals(
-            'https://host.tld:4974',
-            server_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'HTTP_X_FORWARDED_PROTO' => 'https, https',
-                    'HTTP_X_FORWARDED_PORT' => '4974, 80'
-                ]
-            )
-        );
-    }
-
-    /**
-     * Detect if the server uses a specific port (!= 80)
-     */
-    public function testPort()
-    {
-        // HTTP
-        $this->assertEquals(
-            'http://host.tld:8080',
-            server_url(
-                [
-                    'HTTPS' => 'OFF',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '8080'
-                ]
-            )
-        );
-
-        // HTTPS
-        $this->assertEquals(
-            'https://host.tld:8080',
-            server_url(
-                [
-                    'HTTPS' => 'ON',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '8080'
-                ]
-            )
-        );
-    }
-
-    /**
-     * HTTP server on port 80
-     */
-    public function testStandardHttpPort()
-    {
-        $this->assertEquals(
-            'http://host.tld',
-            server_url(
-                [
-                    'HTTPS' => 'OFF',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80'
-                ]
-            )
-        );
-    }
-
-    /**
-     * HTTPS server on port 443
-     */
-    public function testStandardHttpsPort()
-    {
-        $this->assertEquals(
-            'https://host.tld',
-            server_url(
-                [
-                    'HTTPS' => 'ON',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '443'
-                ]
-            )
-        );
-    }
-
-    /**
-     * Misconfigured server (see #1022): Proxy HTTP but 443
-     */
-    public function testHttpWithPort433()
-    {
-        $this->assertEquals(
-            'https://host.tld',
-            server_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'HTTP_X_FORWARDED_PROTO' => 'http',
-                    'HTTP_X_FORWARDED_PORT' => '443'
-                ]
-            )
-        );
-
-        $this->assertEquals(
-            'https://host.tld',
-            server_url(
-                [
-                    'HTTPS' => 'Off',
-                    'SERVER_NAME' => 'host.tld',
-                    'SERVER_PORT' => '80',
-                    'HTTP_X_FORWARDED_PROTO' => 'https, http',
-                    'HTTP_X_FORWARDED_PORT' => '443, 80'
-                ]
-            )
-        );
-    }
-}
diff --git a/tests/http/MetadataRetrieverTest.php b/tests/http/MetadataRetrieverTest.php
deleted file mode 100644
index bf63256a..00000000
--- a/tests/http/MetadataRetrieverTest.php
+++ /dev/null
@@ -1,161 +0,0 @@
-conf = $this->createMock(ConfigManager::class);
-        $this->httpAccess = $this->createMock(HttpAccess::class);
-        $this->retriever = new MetadataRetriever($this->conf, $this->httpAccess);
-
-        $this->conf->method('get')->willReturnCallback(function (string $param, $default) {
-            return $default === null ? $param : $default;
-        });
-    }
-
-    /**
-     * Test metadata retrieve() with values returned
-     */
-    public function testFullRetrieval(): void
-    {
-        $url = 'https://domain.tld/link';
-        $remoteTitle = 'Remote Title ';
-        $remoteDesc = 'Sometimes the meta description is relevant.';
-        $remoteTags = 'abc def';
-        $remoteCharset = 'utf-8';
-
-        $expectedResult = [
-            'title' => trim($remoteTitle),
-            'description' => $remoteDesc,
-            'tags' => $remoteTags,
-        ];
-
-        $this->httpAccess
-            ->expects(static::once())
-            ->method('getCurlHeaderCallback')
-            ->willReturnCallback(
-                function (&$charset) use (
-                    $remoteCharset
-                ): callable {
-                    return function () use (
-                        &$charset,
-                        $remoteCharset
-                    ): void {
-                        $charset = $remoteCharset;
-                    };
-                }
-            )
-        ;
-        $this->httpAccess
-            ->expects(static::once())
-            ->method('getCurlDownloadCallback')
-            ->willReturnCallback(
-                function (
-                    &$charset,
-                    &$title,
-                    &$description,
-                    &$tags
-                ) use (
-                    $remoteCharset,
-                    $remoteTitle,
-                    $remoteDesc,
-                    $remoteTags
-                ): callable {
-                    return function () use (
-                        &$charset,
-                        &$title,
-                        &$description,
-                        &$tags,
-                        $remoteCharset,
-                        $remoteTitle,
-                        $remoteDesc,
-                        $remoteTags
-                    ): void {
-                        static::assertSame($remoteCharset, $charset);
-
-                        $title = $remoteTitle;
-                        $description = $remoteDesc;
-                        $tags = $remoteTags;
-                    };
-                }
-            )
-        ;
-        $this->httpAccess
-            ->expects(static::once())
-            ->method('getHttpResponse')
-            ->with($url, 30, 4194304)
-            ->willReturnCallback(function ($url, $timeout, $maxBytes, $headerCallback, $dlCallback): void {
-                $headerCallback();
-                $dlCallback();
-            })
-        ;
-
-        $result = $this->retriever->retrieve($url);
-
-        static::assertSame($expectedResult, $result);
-    }
-
-    /**
-     * Test metadata retrieve() without any value
-     */
-    public function testEmptyRetrieval(): void
-    {
-        $url = 'https://domain.tld/link';
-
-        $expectedResult = [
-            'title' => null,
-            'description' => null,
-            'tags' => null,
-        ];
-
-        $this->httpAccess
-            ->expects(static::once())
-            ->method('getCurlDownloadCallback')
-            ->willReturnCallback(
-                function (): callable {
-                    return function (): void {
-                    };
-                }
-            )
-        ;
-        $this->httpAccess
-            ->expects(static::once())
-            ->method('getCurlHeaderCallback')
-            ->willReturnCallback(
-                function (): callable {
-                    return function (): void {
-                    };
-                }
-            )
-        ;
-        $this->httpAccess
-            ->expects(static::once())
-            ->method('getHttpResponse')
-            ->with($url, 30, 4194304)
-            ->willReturnCallback(function ($url, $timeout, $maxBytes, $headerCallback, $dlCallback): void {
-                $headerCallback();
-                $dlCallback();
-            })
-        ;
-
-        $result = $this->retriever->retrieve($url);
-
-        static::assertSame($expectedResult, $result);
-    }
-}
diff --git a/tests/http/UrlTest.php b/tests/http/UrlTest.php
deleted file mode 100644
index 3bead6d7..00000000
--- a/tests/http/UrlTest.php
+++ /dev/null
@@ -1,201 +0,0 @@
-cleanup();
-        $this->assertEquals(self::$baseUrl, $url->toString());
-    }
-
-    /**
-     * Instantiate an empty URL
-     */
-    public function testEmptyConstruct()
-    {
-        $url = new Url('');
-        $this->assertEquals('', $url->toString());
-    }
-
-    /**
-     * Instantiate a URL
-     */
-    public function testConstruct()
-    {
-        $ref = 'http://username:password@hostname:9090/path'
-            . '?arg1=value1&arg2=value2#anchor';
-        $url = new Url($ref);
-        $this->assertEquals($ref, $url->toString());
-    }
-
-    /**
-     * URL cleanup - nothing to do
-     */
-    public function testNoCleanup()
-    {
-        // URL with no query nor fragment
-        $this->assertUrlIsCleaned();
-
-        // URL with no annoying elements
-        $ref = self::$baseUrl . '?p1=val1&p2=1234#edit';
-        $url = new Url($ref);
-        $this->assertEquals($ref, $url->cleanup());
-    }
-
-    /**
-     * URL cleanup - annoying fragment
-     */
-    public function testCleanupFragment()
-    {
-        $this->assertUrlIsCleaned('', '#tk.rss_all');
-        $this->assertUrlIsCleaned('', '#xtor=RSS-');
-        $this->assertUrlIsCleaned('', '#xtor=RSS-U3ht0tkc4b');
-    }
-
-    /**
-     * URL cleanup - single annoying query parameter
-     */
-    public function testCleanupSingleQueryParam()
-    {
-        $this->assertUrlIsCleaned('?action_object_map=junk');
-        $this->assertUrlIsCleaned('?action_ref_map=Cr4p!');
-        $this->assertUrlIsCleaned('?action_type_map=g4R84g3');
-
-        $this->assertUrlIsCleaned('?fb_stuff=v41u3');
-        $this->assertUrlIsCleaned('?fb=71m3w4573');
-
-        $this->assertUrlIsCleaned('?utm_campaign=zomg');
-        $this->assertUrlIsCleaned('?utm_medium=numnum');
-        $this->assertUrlIsCleaned('?utm_source=c0d3');
-        $this->assertUrlIsCleaned('?utm_term=1n4l');
-
-        $this->assertUrlIsCleaned('?xtor=some-url');
-        $this->assertUrlIsCleaned('?PHPSESSID=012345678910111213');
-    }
-
-    /**
-     * URL cleanup - multiple annoying query parameters
-     */
-    public function testCleanupMultipleQueryParams()
-    {
-        $this->assertUrlIsCleaned('?xtor=some-url&fb=som3th1ng');
-        $this->assertUrlIsCleaned(
-            '?fb=stuff&utm_campaign=zomg&utm_medium=numnum&utm_source=c0d3'
-        );
-    }
-
-    /**
-     * URL cleanup - multiple annoying query parameters, annoying fragment
-     */
-    public function testCleanupMultipleQueryParamsAndFragment()
-    {
-        $this->assertUrlIsCleaned('?xtor=some-url&fb=som3th1ng', '#tk.rss_all');
-    }
-
-    /**
-     * Nominal case - the URL contains both useful and annoying parameters
-     */
-    public function testCleanupMixedContent()
-    {
-        // ditch annoying query params and fragment, keep useful params
-        $url = new Url(
-            self::$baseUrl
-            . '?fb=zomg&my=stuff&utm_medium=numnum&is=kept#tk.rss_all'
-        );
-        $this->assertEquals(self::$baseUrl . '?my=stuff&is=kept', $url->cleanup());
-
-
-        // ditch annoying query params, keep useful params and fragment
-        $url = new Url(
-            self::$baseUrl
-            . '?fb=zomg&my=stuff&utm_medium=numnum&is=kept#again'
-        );
-        $this->assertEquals(
-            self::$baseUrl . '?my=stuff&is=kept#again',
-            $url->cleanup()
-        );
-
-        // test firefox reader url
-        $url = new Url(
-            'about://reader?url=' . urlencode(self::$baseUrl . '?my=stuff&is=kept')
-        );
-        $this->assertEquals(self::$baseUrl . '?my=stuff&is=kept', $url->cleanup());
-    }
-
-    /**
-     * Test default http scheme.
-     */
-    public function testDefaultScheme()
-    {
-        $url = new Url(self::$baseUrl);
-        $this->assertEquals('http', $url->getScheme());
-        $url = new Url('domain.tld');
-        $this->assertEquals('http', $url->getScheme());
-        $url = new Url('ssh://domain.tld');
-        $this->assertEquals('ssh', $url->getScheme());
-        $url = new Url('ftp://domain.tld');
-        $this->assertEquals('ftp', $url->getScheme());
-        $url = new Url('git://domain.tld/push?pull=clone#checkout');
-        $this->assertEquals('git', $url->getScheme());
-    }
-
-    /**
-     * Test add trailing slash.
-     */
-    public function testAddTrailingSlash()
-    {
-        $strOn = 'http://randomstr.com/test/';
-        $strOff = 'http://randomstr.com/test';
-        $this->assertEquals($strOn, add_trailing_slash($strOn));
-        $this->assertEquals($strOn, add_trailing_slash($strOff));
-    }
-
-    /**
-     * Test valid HTTP url.
-     */
-    public function testUrlIsHttp()
-    {
-        $url = new Url(self::$baseUrl);
-        $this->assertTrue($url->isHttp());
-    }
-
-    /**
-     * Test non HTTP url.
-     */
-    public function testUrlIsNotHttp()
-    {
-        $url = new Url('ftp://save.tld/mysave');
-        $this->assertFalse($url->isHttp());
-    }
-
-    /**
-     * Test International Domain Name to ASCII conversion
-     */
-    public function testIdnToAscii()
-    {
-        $ind = 'http://www.académie-française.fr/';
-        $expected = 'http://www.xn--acadmie-franaise-npb1a.fr/';
-        $url = new Url($ind);
-        $this->assertEquals($expected, $url->idnToAscii());
-
-        $notInd = 'http://www.academie-francaise.fr/';
-        $url = new Url($notInd);
-        $this->assertEquals($notInd, $url->idnToAscii());
-    }
-}
diff --git a/tests/http/UrlUtils/CleanupUrlTest.php b/tests/http/UrlUtils/CleanupUrlTest.php
deleted file mode 100644
index a1002ccc..00000000
--- a/tests/http/UrlUtils/CleanupUrlTest.php
+++ /dev/null
@@ -1,110 +0,0 @@
-assertEquals('', cleanup_url(''));
-    }
-
-    /**
-     * Clean an already cleaned URL
-     */
-    public function testCleanupUrlAlreadyClean()
-    {
-        $this->assertEquals($this->ref, cleanup_url($this->ref));
-        $this->ref2 = $this->ref . '/path/to/dir/';
-        $this->assertEquals($this->ref2, cleanup_url($this->ref2));
-    }
-
-    /**
-     * Clean URL fragments
-     */
-    public function testCleanupUrlFragment()
-    {
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '#tk.rss_all'));
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '#xtor=RSS-'));
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '#xtor=RSS-U3ht0tkc4b'));
-    }
-
-    /**
-     * Clean URL query - single annoying parameter
-     */
-    public function testCleanupUrlQuerySingle()
-    {
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?action_object_map=junk'));
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?action_ref_map=Cr4p!'));
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?action_type_map=g4R84g3'));
-
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?fb_stuff=v41u3'));
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?fb=71m3w4573'));
-
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?utm_campaign=zomg'));
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?utm_medium=numnum'));
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?utm_source=c0d3'));
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?utm_term=1n4l'));
-
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?xtor=some-url'));
-
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?campaign_name=junk'));
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?campaign_start=junk'));
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?campaign_item_index=junk'));
-    }
-
-    /**
-     * Clean URL query - multiple annoying parameters
-     */
-    public function testCleanupUrlQueryMultiple()
-    {
-        $this->assertEquals($this->ref, cleanup_url($this->ref . '?xtor=some-url&fb=som3th1ng'));
-
-        $this->assertEquals($this->ref, cleanup_url(
-            $this->ref . '?fb=stuff&utm_campaign=zomg&utm_medium=numnum&utm_source=c0d3'
-        ));
-
-        $this->assertEquals($this->ref, cleanup_url(
-            $this->ref . '?campaign_start=zomg&campaign_name=numnum'
-        ));
-    }
-
-    /**
-     * Clean URL query - multiple annoying parameters and fragment
-     */
-    public function testCleanupUrlQueryFragment()
-    {
-        $this->assertEquals($this->ref, cleanup_url(
-            $this->ref . '?xtor=some-url&fb=som3th1ng#tk.rss_all'
-        ));
-
-        // ditch annoying query params and fragment, keep useful params
-        $this->assertEquals(
-            $this->ref . '?my=stuff&is=kept',
-            cleanup_url(
-                $this->ref . '?fb=zomg&my=stuff&utm_medium=numnum&is=kept#tk.rss_all'
-            )
-        );
-
-        // ditch annoying query params, keep useful params and fragment
-        $this->assertEquals(
-            $this->ref . '?my=stuff&is=kept#again',
-            cleanup_url(
-                $this->ref . '?fb=zomg&my=stuff&utm_medium=numnum&is=kept#again'
-            )
-        );
-    }
-}
diff --git a/tests/http/UrlUtils/GetUrlSchemeTest.php b/tests/http/UrlUtils/GetUrlSchemeTest.php
deleted file mode 100644
index a6eeff00..00000000
--- a/tests/http/UrlUtils/GetUrlSchemeTest.php
+++ /dev/null
@@ -1,33 +0,0 @@
-assertEquals('', get_url_scheme(''));
-    }
-
-    /**
-     * Get normal scheme of UrlUtils
-     */
-    public function testGetUrlScheme()
-    {
-        $this->assertEquals('http', get_url_scheme('http://domain.tld:3000'));
-        $this->assertEquals('https', get_url_scheme('https://domain.tld:3000'));
-        $this->assertEquals('http', get_url_scheme('domain.tld'));
-        $this->assertEquals('ssh', get_url_scheme('ssh://domain.tld'));
-        $this->assertEquals('ftp', get_url_scheme('ftp://domain.tld'));
-        $this->assertEquals('git', get_url_scheme('git://domain.tld/push?pull=clone#checkout'));
-    }
-}
diff --git a/tests/http/UrlUtils/UnparseUrlTest.php b/tests/http/UrlUtils/UnparseUrlTest.php
deleted file mode 100644
index f01765a0..00000000
--- a/tests/http/UrlUtils/UnparseUrlTest.php
+++ /dev/null
@@ -1,33 +0,0 @@
-assertEquals('', unparse_url([]));
-    }
-
-    /**
-     * Rebuild a full-featured URL
-     */
-    public function testUnparseFull()
-    {
-        $ref = 'http://username:password@hostname:9090/path'
-              . '?arg1=value1&arg2=value2#anchor';
-        $this->assertEquals($ref, unparse_url(parse_url($ref)));
-    }
-}
diff --git a/tests/http/UrlUtils/WhitelistProtocolsTest.php b/tests/http/UrlUtils/WhitelistProtocolsTest.php
deleted file mode 100644
index 673af32b..00000000
--- a/tests/http/UrlUtils/WhitelistProtocolsTest.php
+++ /dev/null
@@ -1,63 +0,0 @@
-assertEquals($url, whitelist_protocols($url, $whitelist));
-        $url = '/path.jpg';
-        $this->assertEquals($url, whitelist_protocols($url, $whitelist));
-    }
-
-    /**
-     * Test whitelist_protocols() on a note (relative URL).
-     */
-    public function testWhitelistProtocolMissing()
-    {
-        $whitelist = ['ftp', 'magnet'];
-        $url = 'test.tld/path/?query=value#hash';
-        $this->assertEquals('http://' . $url, whitelist_protocols($url, $whitelist));
-    }
-
-    /**
-     * Test whitelist_protocols() with allowed protocols.
-     */
-    public function testWhitelistAllowedProtocol()
-    {
-        $whitelist = ['ftp', 'magnet'];
-        $url = 'http://test.tld/path/?query=value#hash';
-        $this->assertEquals($url, whitelist_protocols($url, $whitelist));
-        $url = 'https://test.tld/path/?query=value#hash';
-        $this->assertEquals($url, whitelist_protocols($url, $whitelist));
-        $url = 'ftp://test.tld/path/?query=value#hash';
-        $this->assertEquals($url, whitelist_protocols($url, $whitelist));
-        $url = 'magnet:test.tld/path/?query=value#hash';
-        $this->assertEquals($url, whitelist_protocols($url, $whitelist));
-    }
-
-    /**
-     * Test whitelist_protocols() with allowed protocols.
-     */
-    public function testWhitelistDisallowedProtocol()
-    {
-        $whitelist = ['ftp', 'magnet'];
-        $url = 'javascript:alert("xss");';
-        $this->assertEquals('http://alert("xss");', whitelist_protocols($url, $whitelist));
-        $url = 'other://test.tld/path/?query=value#hash';
-        $this->assertEquals('http://test.tld/path/?query=value#hash', whitelist_protocols($url, $whitelist));
-    }
-}
diff --git a/tests/languages/bootstrap.php b/tests/languages/bootstrap.php
deleted file mode 100644
index 69ade20e..00000000
--- a/tests/languages/bootstrap.php
+++ /dev/null
@@ -1,7 +0,0 @@
-assertTrue(class_exists('IntlDateFormatter'));
-    }
-
-    /**
-     * Test date_format().
-     */
-    public function testDateFormat()
-    {
-        $current = setlocale(LC_ALL, 0);
-        autoLocale('de-de');
-        $date = DateTime::createFromFormat('Ymd_His', '20170102_201112');
-        $this->assertRegExp('/2\. Januar 2017 (um )?20:11:12 GMT\+0?3(:00)?/', format_date($date, true, true));
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test date_format() without time.
-     */
-    public function testDateFormatNoTime()
-    {
-        $current = setlocale(LC_ALL, 0);
-        autoLocale('de-de');
-        $date = DateTime::createFromFormat('Ymd_His', '20170102_201112');
-        $this->assertRegExp('/2\. Januar 2017/', format_date($date, false, true));
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test date_format() using DateTime
-     */
-    public function testDateFormatDefault()
-    {
-        $date = DateTime::createFromFormat('Ymd_His', '20170102_101112');
-        $this->assertEquals('January 2, 2017 10:11:12 AM GMT+03:00', format_date($date, true, false));
-    }
-
-    /**
-     * Test date_format() using DateTime
-     */
-    public function testDateFormatDefaultNoTime()
-    {
-        $date = DateTime::createFromFormat('Ymd_His', '20170201_101112');
-        $this->assertEquals('February 1, 2017', format_date($date, false, false));
-    }
-
-    /**
-     * Test autoLocale with a simple value
-     */
-    public function testAutoLocaleValid()
-    {
-        $current = setlocale(LC_ALL, 0);
-        $header = 'en-us';
-        autoLocale($header);
-        $this->assertEquals('en_US.utf8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale with an alternative locale value
-     */
-    public function testAutoLocaleValidAlternative()
-    {
-        $current = setlocale(LC_ALL, 0);
-        $header = 'en_US.UTF-8';
-        autoLocale($header);
-        $this->assertEquals('en_US.utf8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale with multiples value, the first one is valid
-     */
-    public function testAutoLocaleMultipleFirstValid()
-    {
-        $current = setlocale(LC_ALL, 0);
-        $header = 'en-us,de-de';
-        autoLocale($header);
-        $this->assertEquals('en_US.utf8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale with multiples value, the second one is available
-     */
-    public function testAutoLocaleMultipleSecondAvailable()
-    {
-        $current = setlocale(LC_ALL, 0);
-        $header = 'mgg_IN,fr-fr';
-        autoLocale($header);
-        $this->assertEquals('fr_FR.utf8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale without value: defaults to en_US.
-     */
-    public function testAutoLocaleBlank()
-    {
-        $current = setlocale(LC_ALL, 0);
-        autoLocale('');
-        $this->assertEquals('en_US.UTF-8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale with an unavailable value: defaults to en_US.
-     */
-    public function testAutoLocaleUnavailable()
-    {
-        $current = setlocale(LC_ALL, 0);
-        autoLocale('mgg_IN');
-        $this->assertEquals('en_US.UTF-8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-}
diff --git a/tests/languages/en/UtilsEnTest.php b/tests/languages/en/UtilsEnTest.php
deleted file mode 100644
index f1e21b9a..00000000
--- a/tests/languages/en/UtilsEnTest.php
+++ /dev/null
@@ -1,135 +0,0 @@
-assertTrue(class_exists('IntlDateFormatter'));
-    }
-
-    /**
-     * Test date_format().
-     */
-    public function testDateFormat()
-    {
-        $current = setlocale(LC_ALL, 0);
-        autoLocale('en_US.UTF-8');
-        $date = DateTime::createFromFormat('Ymd_His', '20170102_201112');
-        $this->assertRegExp('/January 2, 2017 (at )?8:11:12 PM GMT\+0?3(:00)?/', format_date($date, true, true));
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test date_format() without time.
-     */
-    public function testDateFormatNoTime()
-    {
-        $current = setlocale(LC_ALL, 0);
-        autoLocale('en_US.UTF-8');
-        $date = DateTime::createFromFormat('Ymd_His', '20170102_201112');
-        $this->assertRegExp('/January 2, 2017/', format_date($date, false, true));
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test date_format() using DateTime
-     */
-    public function testDateFormatDefault()
-    {
-        $date = DateTime::createFromFormat('Ymd_His', '20170102_101112');
-        $this->assertEquals('January 2, 2017 10:11:12 AM GMT+03:00', format_date($date, true, false));
-    }
-
-    /**
-     * Test date_format() using DateTime
-     */
-    public function testDateFormatDefaultNoTime()
-    {
-        $date = DateTime::createFromFormat('Ymd_His', '20170201_101112');
-        $this->assertEquals('February 1, 2017', format_date($date, false, false));
-    }
-
-    /**
-     * Test autoLocale with a simple value
-     */
-    public function testAutoLocaleValid()
-    {
-        $current = setlocale(LC_ALL, 0);
-        $header = 'de-de';
-        autoLocale($header);
-        $this->assertEquals('de_DE.utf8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale with an alternative locale value
-     */
-    public function testAutoLocaleValidAlternative()
-    {
-        $current = setlocale(LC_ALL, 0);
-        $header = 'de_de.UTF8';
-        autoLocale($header);
-        $this->assertEquals('de_DE.utf8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale with multiples value, the first one is valid
-     */
-    public function testAutoLocaleMultipleFirstValid()
-    {
-        $current = setlocale(LC_ALL, 0);
-        $header = 'de-de;en-us';
-        autoLocale($header);
-        $this->assertEquals('de_DE.utf8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale with multiples value, the second one is available
-     */
-    public function testAutoLocaleMultipleSecondAvailable()
-    {
-        $current = setlocale(LC_ALL, 0);
-        $header = 'mgg_IN,fr-fr';
-        autoLocale($header);
-        $this->assertEquals('fr_FR.utf8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale without value: defaults to en_US.
-     */
-    public function testAutoLocaleBlank()
-    {
-        $current = setlocale(LC_ALL, 0);
-        autoLocale('');
-        $this->assertEquals('en_US.UTF-8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale with an unavailable value: defaults to en_US.
-     */
-    public function testAutoLocaleUnavailable()
-    {
-        $current = setlocale(LC_ALL, 0);
-        autoLocale('mgg_IN');
-        $this->assertEquals('en_US.UTF-8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-}
diff --git a/tests/languages/fr/LanguagesFrTest.php b/tests/languages/fr/LanguagesFrTest.php
deleted file mode 100644
index 7e24251f..00000000
--- a/tests/languages/fr/LanguagesFrTest.php
+++ /dev/null
@@ -1,201 +0,0 @@
-conf = new ConfigManager(self::$configFile);
-        $this->conf->set('translation.language', 'fr');
-    }
-
-    /**
-     * Reset the locale since gettext seems to mess with it, making it too long
-     */
-    public static function tearDownAfterClass(): void
-    {
-        if (! empty(getenv('UT_LOCALE'))) {
-            setlocale(LC_ALL, getenv('UT_LOCALE'));
-        }
-    }
-
-    /**
-     * Test t() with a simple non identified value.
-     */
-    public function testTranslateSingleNotIDGettext()
-    {
-        $this->conf->set('translation.mode', 'gettext');
-        new Languages('en', $this->conf);
-        $text = 'abcdé 564 fgK';
-        $this->assertEquals($text, t($text));
-    }
-
-    /**
-     * Test t() with a simple identified value in gettext mode.
-     */
-    public function testTranslateSingleIDGettext()
-    {
-        $this->conf->set('translation.mode', 'gettext');
-        new Languages('en', $this->conf);
-        $text = 'permalink';
-        $this->assertEquals('permalien', t($text));
-    }
-
-    /**
-     * Test t() with a non identified plural form in gettext mode.
-     */
-    public function testTranslatePluralNotIDGettext()
-    {
-        $this->conf->set('translation.mode', 'gettext');
-        new Languages('en', $this->conf);
-        $text = 'sandwich';
-        $nText = 'sandwiches';
-        // Not ID, so English fallback, and in english, plural 0
-        $this->assertEquals('sandwiches', t($text, $nText, 0));
-        $this->assertEquals('sandwich', t($text, $nText, 1));
-        $this->assertEquals('sandwiches', t($text, $nText, 2));
-    }
-
-    /**
-     * Test t() with an identified plural form in gettext mode.
-     */
-    public function testTranslatePluralIDGettext()
-    {
-        $this->conf->set('translation.mode', 'gettext');
-        new Languages('en', $this->conf);
-        $text = 'shaare';
-        $nText = 'shaares';
-        $this->assertEquals('shaare', t($text, $nText, 0));
-        $this->assertEquals('shaare', t($text, $nText, 1));
-        $this->assertEquals('shaares', t($text, $nText, 2));
-    }
-
-    /**
-     * Test t() with a simple non identified value.
-     */
-    public function testTranslateSingleNotIDPhp()
-    {
-        $this->conf->set('translation.mode', 'php');
-        new Languages('en', $this->conf);
-        $text = 'abcdé 564 fgK';
-        $this->assertEquals($text, t($text));
-    }
-
-    /**
-     * Test t() with a simple identified value in PHP mode.
-     */
-    public function testTranslateSingleIDPhp()
-    {
-        $this->conf->set('translation.mode', 'php');
-        new Languages('en', $this->conf);
-        $text = 'permalink';
-        $this->assertEquals('permalien', t($text));
-    }
-
-    /**
-     * Test t() with a non identified plural form in PHP mode.
-     */
-    public function testTranslatePluralNotIDPhp()
-    {
-        $this->conf->set('translation.mode', 'php');
-        new Languages('en', $this->conf);
-        $text = 'sandwich';
-        $nText = 'sandwiches';
-        // Not ID, so English fallback, and in english, plural 0
-        $this->assertEquals('sandwiches', t($text, $nText, 0));
-        $this->assertEquals('sandwich', t($text, $nText, 1));
-        $this->assertEquals('sandwiches', t($text, $nText, 2));
-    }
-
-    /**
-     * Test t() with an identified plural form in PHP mode.
-     */
-    public function testTranslatePluralIDPhp()
-    {
-        $this->conf->set('translation.mode', 'php');
-        new Languages('en', $this->conf);
-        $text = 'shaare';
-        $nText = 'shaares';
-        // In english, zero is followed by plural form
-        $this->assertEquals('shaare', t($text, $nText, 0));
-        $this->assertEquals('shaare', t($text, $nText, 1));
-        $this->assertEquals('shaares', t($text, $nText, 2));
-    }
-
-    /**
-     * Test t() with an extension language file in gettext mode
-     */
-    public function testTranslationExtensionGettext()
-    {
-        $this->conf->set('translation.mode', 'gettext');
-        $this->conf->set('translation.extensions.test', 'tests/utils/languages/');
-        new Languages('en', $this->conf);
-        $txt = 'car'; // ignore me poedit
-        $this->assertEquals('voiture', t($txt, $txt, 1, 'test'));
-        $this->assertEquals('Fouille', t('Search', 'Search', 1, 'test'));
-    }
-
-    /**
-     * Test t() with an extension language file in PHP mode
-     */
-    public function testTranslationExtensionPhp()
-    {
-        $this->conf->set('translation.mode', 'php');
-        $this->conf->set('translation.extensions.test', 'tests/utils/languages/');
-        new Languages('en', $this->conf);
-        $txt = 'car'; // ignore me poedit
-        $this->assertEquals('voiture', t($txt, $txt, 1, 'test'));
-        $this->assertEquals('Fouille', t('Search', 'Search', 1, 'test'));
-    }
-
-    /**
-     * Test t() with an extension language file coming from the theme in gettext mode
-     */
-    public function testTranslationThemeExtensionGettext()
-    {
-        $this->conf->set('translation.mode', 'gettext');
-        $this->conf->set('raintpl_tpl', 'tests/utils/customtpl/');
-        $this->conf->set('theme', 'dummy');
-        new Languages('en', $this->conf);
-        $txt = 'rooster'; // ignore me poedit
-        $this->assertEquals('coq', t($txt, $txt, 1, 'dummy'));
-    }
-
-    /**
-     * Test t() with an extension language file coming from the theme in PHP mode
-     */
-    public function testTranslationThemeExtensionPhp()
-    {
-        $this->conf->set('translation.mode', 'php');
-        $this->conf->set('raintpl_tpl', 'tests/utils/customtpl/');
-        $this->conf->set('theme', 'dummy');
-        new Languages('en', $this->conf);
-        $txt = 'rooster'; // ignore me poedit
-        $this->assertEquals('coq', t($txt, $txt, 1, 'dummy'));
-    }
-}
diff --git a/tests/languages/fr/UtilsFrTest.php b/tests/languages/fr/UtilsFrTest.php
deleted file mode 100644
index 799fa5c9..00000000
--- a/tests/languages/fr/UtilsFrTest.php
+++ /dev/null
@@ -1,135 +0,0 @@
-assertTrue(class_exists('IntlDateFormatter'));
-    }
-
-    /**
-     * Test date_format().
-     */
-    public function testDateFormat()
-    {
-        $current = setlocale(LC_ALL, 0);
-        autoLocale('fr-fr');
-        $date = DateTime::createFromFormat('Ymd_His', '20170102_201112');
-        $this->assertRegExp('/2 janvier 2017 (à )?20:11:12 UTC\+0?3(:00)?/', format_date($date));
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test date_format() without time.
-     */
-    public function testDateFormatNoTime()
-    {
-        $current = setlocale(LC_ALL, 0);
-        autoLocale('fr-fr');
-        $date = DateTime::createFromFormat('Ymd_His', '20170102_201112');
-        $this->assertRegExp('/2 janvier 2017/', format_date($date, false, true));
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test date_format() using DateTime
-     */
-    public function testDateFormatDefault()
-    {
-        $date = DateTime::createFromFormat('Ymd_His', '20170102_101112');
-        $this->assertEquals('January 2, 2017 10:11:12 AM GMT+03:00', format_date($date, true, false));
-    }
-
-    /**
-     * Test date_format() using DateTime
-     */
-    public function testDateFormatDefaultNoTime()
-    {
-        $date = DateTime::createFromFormat('Ymd_His', '20170201_101112');
-        $this->assertEquals('February 1, 2017', format_date($date, false, false));
-    }
-
-    /**
-     * Test autoLocale with a simple value
-     */
-    public function testAutoLocaleValid()
-    {
-        $current = setlocale(LC_ALL, 0);
-        $header = 'de-de';
-        autoLocale($header);
-        $this->assertEquals('de_DE.utf8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale with an alternative locale value
-     */
-    public function testAutoLocaleValidAlternative()
-    {
-        $current = setlocale(LC_ALL, 0);
-        $header = 'de_de.UTF8';
-        autoLocale($header);
-        $this->assertEquals('de_DE.utf8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale with multiples value, the first one is valid
-     */
-    public function testAutoLocaleMultipleFirstValid()
-    {
-        $current = setlocale(LC_ALL, 0);
-        $header = 'de-de;en-us';
-        autoLocale($header);
-        $this->assertEquals('de_DE.utf8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale with multiples value, the second one is available
-     */
-    public function testAutoLocaleMultipleSecondAvailable()
-    {
-        $current = setlocale(LC_ALL, 0);
-        $header = 'mgg_IN,de-de';
-        autoLocale($header);
-        $this->assertEquals('de_DE.utf8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale without value: defaults to en_US.
-     */
-    public function testAutoLocaleBlank()
-    {
-        $current = setlocale(LC_ALL, 0);
-        autoLocale('');
-        $this->assertEquals('en_US.UTF-8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-
-    /**
-     * Test autoLocale with an unavailable value: defaults to en_US.
-     */
-    public function testAutoLocaleUnavailable()
-    {
-        $current = setlocale(LC_ALL, 0);
-        autoLocale('mgg_IN');
-        $this->assertEquals('en_US.UTF-8', setlocale(LC_ALL, 0));
-
-        setlocale(LC_ALL, $current);
-    }
-}
diff --git a/tests/legacy/LegacyControllerTest.php b/tests/legacy/LegacyControllerTest.php
deleted file mode 100644
index 1a2549a3..00000000
--- a/tests/legacy/LegacyControllerTest.php
+++ /dev/null
@@ -1,101 +0,0 @@
-createContainer();
-
-        $this->controller = new LegacyController($this->container);
-    }
-
-    /**
-     * @dataProvider getProcessProvider
-     */
-    public function testProcess(string $legacyRoute, array $queryParameters, string $slimRoute, bool $isLoggedIn): void
-    {
-        $request = $this->createMock(Request::class);
-        $request->method('getQueryParams')->willReturn($queryParameters);
-        $request
-            ->method('getParam')
-            ->willReturnCallback(function (string $key) use ($queryParameters): ?string {
-                return $queryParameters[$key] ?? null;
-            })
-        ;
-        $response = new Response();
-
-        $this->container->loginManager->method('isLoggedIn')->willReturn($isLoggedIn);
-
-        $result = $this->controller->process($request, $response, $legacyRoute);
-
-        static::assertSame('/subfolder' . $slimRoute, $result->getHeader('location')[0]);
-    }
-
-    public function testProcessNotFound(): void
-    {
-        $request = $this->createMock(Request::class);
-        $response = new Response();
-
-        $this->expectException(UnknowLegacyRouteException::class);
-
-        $this->controller->process($request, $response, 'nope');
-    }
-
-    /**
-     * @return array[] Parameters:
-     *                   - string legacyRoute
-     *                   - array  queryParameters
-     *                   - string slimRoute
-     *                   - bool   isLoggedIn
-     */
-    public function getProcessProvider(): array
-    {
-        return [
-            ['post', [], '/admin/shaare', true],
-            ['post', [], '/login?returnurl=/subfolder/admin/shaare', false],
-            ['post', ['title' => 'test'], '/admin/shaare?title=test', true],
-            ['post', ['title' => 'test'], '/login?returnurl=/subfolder/admin/shaare?title=test', false],
-            ['addlink', [], '/admin/add-shaare', true],
-            ['addlink', [], '/login?returnurl=/subfolder/admin/add-shaare', false],
-            ['login', [], '/login', true],
-            ['login', [], '/login', false],
-            ['logout', [], '/admin/logout', true],
-            ['logout', [], '/admin/logout', false],
-            ['picwall', [], '/picture-wall', false],
-            ['picwall', [], '/picture-wall', true],
-            ['tagcloud', [], '/tags/cloud', false],
-            ['tagcloud', [], '/tags/cloud', true],
-            ['taglist', [], '/tags/list', false],
-            ['taglist', [], '/tags/list', true],
-            ['daily', [], '/daily', false],
-            ['daily', [], '/daily', true],
-            ['daily', ['day' => '123456789', 'discard' => '1'], '/daily?day=123456789', false],
-            ['rss', [], '/feed/rss', false],
-            ['rss', [], '/feed/rss', true],
-            ['rss', ['search' => 'filter123', 'other' => 'param'], '/feed/rss?search=filter123&other=param', false],
-            ['atom', [], '/feed/atom', false],
-            ['atom', [], '/feed/atom', true],
-            ['atom', ['search' => 'filter123', 'other' => 'param'], '/feed/atom?search=filter123&other=param', false],
-            ['opensearch', [], '/open-search', false],
-            ['opensearch', [], '/open-search', true],
-            ['dailyrss', [], '/daily-rss', false],
-            ['dailyrss', [], '/daily-rss', true],
-            ['configure', [], '/login?returnurl=/subfolder/admin/configure', false],
-            ['configure', [], '/admin/configure', true],
-        ];
-    }
-}
diff --git a/tests/legacy/LegacyDummyUpdater.php b/tests/legacy/LegacyDummyUpdater.php
deleted file mode 100644
index d86bd9b6..00000000
--- a/tests/legacy/LegacyDummyUpdater.php
+++ /dev/null
@@ -1,75 +0,0 @@
-methods = $class->getMethods(ReflectionMethod::IS_FINAL);
-    }
-
-    /**
-     * Update method 1.
-     *
-     * @return bool true.
-     */
-    final private function updateMethodDummy1()
-    {
-        return true;
-    }
-
-    /**
-     * Update method 2.
-     *
-     * @return bool true.
-     */
-    final private function updateMethodDummy2()
-    {
-        return true;
-    }
-
-    /**
-     * Update method 3.
-     *
-     * @return bool true.
-     */
-    final private function updateMethodDummy3()
-    {
-        return true;
-    }
-
-    /**
-     * Update method 4, raise an exception.
-     *
-     * @throws Exception error.
-     */
-    final private function updateMethodException()
-    {
-        throw new Exception('whatever');
-    }
-}
diff --git a/tests/legacy/LegacyLinkDBTest.php b/tests/legacy/LegacyLinkDBTest.php
deleted file mode 100644
index 8114b06b..00000000
--- a/tests/legacy/LegacyLinkDBTest.php
+++ /dev/null
@@ -1,664 +0,0 @@
-write(self::$testDatastore);
-        self::$publicLinkDB = new LegacyLinkDB(self::$testDatastore, false, false);
-        self::$privateLinkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-    }
-
-    /**
-     * Allows to test LinkDB's private methods
-     *
-     * @see
-     *  https://sebastian-bergmann.de/archives/881-Testing-Your-Privates.html
-     *  http://stackoverflow.com/a/2798203
-     */
-    protected static function getMethod($name)
-    {
-        $class = new ReflectionClass('Shaarli\Legacy\LegacyLinkDB');
-        $method = $class->getMethod($name);
-        $method->setAccessible(true);
-        return $method;
-    }
-
-    /**
-     * Instantiate LinkDB objects - logged in user
-     */
-    public function testConstructLoggedIn()
-    {
-        new LegacyLinkDB(self::$testDatastore, true, false);
-        $this->assertFileExists(self::$testDatastore);
-    }
-
-    /**
-     * Instantiate LinkDB objects - logged out or public instance
-     */
-    public function testConstructLoggedOut()
-    {
-        new LegacyLinkDB(self::$testDatastore, false, false);
-        $this->assertFileExists(self::$testDatastore);
-    }
-
-    /**
-     * Attempt to instantiate a LinkDB whereas the datastore is not writable
-     */
-    public function testConstructDatastoreNotWriteable()
-    {
-        $this->expectException(\Shaarli\Exceptions\IOException::class);
-        $this->expectExceptionMessageRegExp('/Error accessing "null"/');
-
-        new LegacyLinkDB('null/store.db', false, false);
-    }
-
-    /**
-     * The DB doesn't exist, ensure it is created with dummy content
-     */
-    public function testCheckDBNew()
-    {
-        $linkDB = new LegacyLinkDB(self::$testDatastore, false, false);
-        unlink(self::$testDatastore);
-        $this->assertFileNotExists(self::$testDatastore);
-
-        $checkDB = self::getMethod('check');
-        $checkDB->invokeArgs($linkDB, []);
-        $this->assertFileExists(self::$testDatastore);
-
-        // ensure the correct data has been written
-        $this->assertGreaterThan(0, filesize(self::$testDatastore));
-    }
-
-    /**
-     * The DB exists, don't do anything
-     */
-    public function testCheckDBLoad()
-    {
-        $linkDB = new LegacyLinkDB(self::$testDatastore, false, false);
-        $datastoreSize = filesize(self::$testDatastore);
-        $this->assertGreaterThan(0, $datastoreSize);
-
-        $checkDB = self::getMethod('check');
-        $checkDB->invokeArgs($linkDB, []);
-
-        // ensure the datastore is left unmodified
-        $this->assertEquals(
-            $datastoreSize,
-            filesize(self::$testDatastore)
-        );
-    }
-
-    /**
-     * Load an empty DB
-     */
-    public function testReadEmptyDB()
-    {
-        file_put_contents(self::$testDatastore, '');
-        $emptyDB = new LegacyLinkDB(self::$testDatastore, false, false);
-        $this->assertEquals(0, sizeof($emptyDB));
-        $this->assertEquals(0, count($emptyDB));
-    }
-
-    /**
-     * Load public bookmarks from the DB
-     */
-    public function testReadPublicDB()
-    {
-        $this->assertEquals(
-            self::$refDB->countPublicLinks(),
-            sizeof(self::$publicLinkDB)
-        );
-    }
-
-    /**
-     * Load public and private bookmarks from the DB
-     */
-    public function testReadPrivateDB()
-    {
-        $this->assertEquals(
-            self::$refDB->countLinks(),
-            sizeof(self::$privateLinkDB)
-        );
-    }
-
-    /**
-     * Save the bookmarks to the DB
-     */
-    public function testSave()
-    {
-        $testDB = new LegacyLinkDB(self::$testDatastore, true, false);
-        $dbSize = sizeof($testDB);
-
-        $link = [
-            'id' => 43,
-            'title' => 'an additional link',
-            'url' => 'http://dum.my',
-            'description' => 'One more',
-            'private' => 0,
-            'created' => DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150518_190000'),
-            'tags' => 'unit test'
-        ];
-        $testDB[$link['id']] = $link;
-        $testDB->save('tests');
-
-        $testDB = new LegacyLinkDB(self::$testDatastore, true, false);
-        $this->assertEquals($dbSize + 1, sizeof($testDB));
-    }
-
-    /**
-     * Count existing bookmarks
-     */
-    public function testCount()
-    {
-        $this->assertEquals(
-            self::$refDB->countPublicLinks(),
-            self::$publicLinkDB->count()
-        );
-        $this->assertEquals(
-            self::$refDB->countLinks(),
-            self::$privateLinkDB->count()
-        );
-    }
-
-    /**
-     * Count existing bookmarks - public bookmarks hidden
-     */
-    public function testCountHiddenPublic()
-    {
-        $linkDB = new LegacyLinkDB(self::$testDatastore, false, true);
-
-        $this->assertEquals(
-            0,
-            $linkDB->count()
-        );
-        $this->assertEquals(
-            0,
-            $linkDB->count()
-        );
-    }
-
-    /**
-     * List the days for which bookmarks have been posted
-     */
-    public function testDays()
-    {
-        $this->assertEquals(
-            ['20100309', '20100310', '20121206', '20121207', '20130614', '20150310'],
-            self::$publicLinkDB->days()
-        );
-
-        $this->assertEquals(
-            ['20100309', '20100310', '20121206', '20121207', '20130614', '20141125', '20150310'],
-            self::$privateLinkDB->days()
-        );
-    }
-
-    /**
-     * The URL corresponds to an existing entry in the DB
-     */
-    public function testGetKnownLinkFromURL()
-    {
-        $link = self::$publicLinkDB->getLinkFromUrl('http://mediagoblin.org/');
-
-        $this->assertNotEquals(false, $link);
-        $this->assertContainsPolyfill(
-            'A free software media publishing platform',
-            $link['description']
-        );
-    }
-
-    /**
-     * The URL is not in the DB
-     */
-    public function testGetUnknownLinkFromURL()
-    {
-        $this->assertEquals(
-            false,
-            self::$publicLinkDB->getLinkFromUrl('http://dev.null')
-        );
-    }
-
-    /**
-     * Lists all tags
-     */
-    public function testAllTags()
-    {
-        $this->assertEquals(
-            [
-                'web' => 3,
-                'cartoon' => 2,
-                'gnu' => 2,
-                'dev' => 1,
-                'samba' => 1,
-                'media' => 1,
-                'software' => 1,
-                'stallman' => 1,
-                'free' => 1,
-                '-exclude' => 1,
-                'hashtag' => 2,
-                // The DB contains a link with `sTuff` and another one with `stuff` tag.
-                // They need to be grouped with the first case found - order by date DESC: `sTuff`.
-                'sTuff' => 2,
-                'ut' => 1,
-                'assurance' => 1,
-                'coding-style' => 1,
-                'quality' => 1,
-                'standards' => 1,
-            ],
-            self::$publicLinkDB->linksCountPerTag()
-        );
-
-        $this->assertEquals(
-            [
-                'web' => 4,
-                'cartoon' => 3,
-                'gnu' => 2,
-                'dev' => 2,
-                'samba' => 1,
-                'media' => 1,
-                'software' => 1,
-                'stallman' => 1,
-                'free' => 1,
-                'html' => 1,
-                'w3c' => 1,
-                'css' => 1,
-                'Mercurial' => 1,
-                'sTuff' => 2,
-                '-exclude' => 1,
-                '.hidden' => 1,
-                'hashtag' => 2,
-                'tag1' => 1,
-                'tag2' => 1,
-                'tag3' => 1,
-                'tag4' => 1,
-                'ut' => 1,
-                'assurance' => 1,
-                'coding-style' => 1,
-                'quality' => 1,
-                'standards' => 1,
-            ],
-            self::$privateLinkDB->linksCountPerTag()
-        );
-        $this->assertEquals(
-            [
-                'web' => 4,
-                'cartoon' => 2,
-                'gnu' => 1,
-                'dev' => 1,
-                'samba' => 1,
-                'media' => 1,
-                'html' => 1,
-                'w3c' => 1,
-                'css' => 1,
-                'Mercurial' => 1,
-                '.hidden' => 1,
-                'hashtag' => 1,
-            ],
-            self::$privateLinkDB->linksCountPerTag(['web'])
-        );
-        $this->assertEquals(
-            [
-                'web' => 1,
-                'html' => 1,
-                'w3c' => 1,
-                'css' => 1,
-                'Mercurial' => 1,
-            ],
-            self::$privateLinkDB->linksCountPerTag(['web'], 'private')
-        );
-    }
-
-    /**
-     * Test filter with string.
-     */
-    public function testFilterString()
-    {
-        $tags = 'dev cartoon';
-        $request = ['searchtags' => $tags];
-        $this->assertEquals(
-            2,
-            count(self::$privateLinkDB->filterSearch($request, true, false))
-        );
-    }
-
-    /**
-     * Test filter with string.
-     */
-    public function testFilterArray()
-    {
-        $tags = ['dev', 'cartoon'];
-        $request = ['searchtags' => $tags];
-        $this->assertEquals(
-            2,
-            count(self::$privateLinkDB->filterSearch($request, true, false))
-        );
-    }
-
-    /**
-     * Test hidden tags feature:
-     *  tags starting with a dot '.' are only visible when logged in.
-     */
-    public function testHiddenTags()
-    {
-        $tags = '.hidden';
-        $request = ['searchtags' => $tags];
-        $this->assertEquals(
-            1,
-            count(self::$privateLinkDB->filterSearch($request, true, false))
-        );
-
-        $this->assertEquals(
-            0,
-            count(self::$publicLinkDB->filterSearch($request, true, false))
-        );
-    }
-
-    /**
-     * Test filterHash() with a valid smallhash.
-     */
-    public function testFilterHashValid()
-    {
-        $request = smallHash('20150310_114651');
-        $this->assertEquals(
-            1,
-            count(self::$publicLinkDB->filterHash($request))
-        );
-        $request = smallHash('20150310_114633' . 8);
-        $this->assertEquals(
-            1,
-            count(self::$publicLinkDB->filterHash($request))
-        );
-    }
-
-    /**
-     * Test filterHash() with an invalid smallhash.
-     */
-    public function testFilterHashInValid1()
-    {
-        $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
-
-        $request = 'blabla';
-        self::$publicLinkDB->filterHash($request);
-    }
-
-    /**
-     * Test filterHash() with an empty smallhash.
-     */
-    public function testFilterHashInValid()
-    {
-        $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
-
-        self::$publicLinkDB->filterHash('');
-    }
-
-    /**
-     * Test reorder with asc/desc parameter.
-     */
-    public function testReorderLinksDesc()
-    {
-        self::$privateLinkDB->reorder('ASC');
-        $stickyIds = [11, 10];
-        $standardIds = [42, 4, 9, 1, 0, 7, 6, 8, 41];
-        $linkIds = array_merge($stickyIds, $standardIds);
-        $cpt = 0;
-        foreach (self::$privateLinkDB as $key => $value) {
-            $this->assertEquals($linkIds[$cpt++], $key);
-        }
-        self::$privateLinkDB->reorder('DESC');
-        $linkIds = array_merge(array_reverse($stickyIds), array_reverse($standardIds));
-        $cpt = 0;
-        foreach (self::$privateLinkDB as $key => $value) {
-            $this->assertEquals($linkIds[$cpt++], $key);
-        }
-    }
-
-    /**
-     * Test rename tag with a valid value present in multiple bookmarks
-     */
-    public function testRenameTagMultiple()
-    {
-        self::$refDB->write(self::$testDatastore);
-        $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-
-        $res = $linkDB->renameTag('cartoon', 'Taz');
-        $this->assertEquals(3, count($res));
-        $this->assertContainsPolyfill(' Taz ', $linkDB[4]['tags']);
-        $this->assertContainsPolyfill(' Taz ', $linkDB[1]['tags']);
-        $this->assertContainsPolyfill(' Taz ', $linkDB[0]['tags']);
-    }
-
-    /**
-     * Test rename tag with a valid value
-     */
-    public function testRenameTagCaseSensitive()
-    {
-        self::$refDB->write(self::$testDatastore);
-        $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-
-        $res = $linkDB->renameTag('sTuff', 'Taz');
-        $this->assertEquals(1, count($res));
-        $this->assertEquals('Taz', $linkDB[41]['tags']);
-    }
-
-    /**
-     * Test rename tag with invalid values
-     */
-    public function testRenameTagInvalid()
-    {
-        $linkDB = new LegacyLinkDB(self::$testDatastore, false, false);
-
-        $this->assertFalse($linkDB->renameTag('', 'test'));
-        $this->assertFalse($linkDB->renameTag('', ''));
-        // tag non existent
-        $this->assertEquals([], $linkDB->renameTag('test', ''));
-        $this->assertEquals([], $linkDB->renameTag('test', 'retest'));
-    }
-
-    /**
-     * Test delete tag with a valid value
-     */
-    public function testDeleteTag()
-    {
-        self::$refDB->write(self::$testDatastore);
-        $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-
-        $res = $linkDB->renameTag('cartoon', null);
-        $this->assertEquals(3, count($res));
-        $this->assertNotContainsPolyfill('cartoon', $linkDB[4]['tags']);
-    }
-
-    /**
-     * Test linksCountPerTag all tags without filter.
-     * Equal occurrences should be sorted alphabetically.
-     */
-    public function testCountLinkPerTagAllNoFilter()
-    {
-        $expected = [
-            'web' => 4,
-            'cartoon' => 3,
-            'dev' => 2,
-            'gnu' => 2,
-            'hashtag' => 2,
-            'sTuff' => 2,
-            '-exclude' => 1,
-            '.hidden' => 1,
-            'Mercurial' => 1,
-            'css' => 1,
-            'free' => 1,
-            'html' => 1,
-            'media' => 1,
-            'samba' => 1,
-            'software' => 1,
-            'stallman' => 1,
-            'tag1' => 1,
-            'tag2' => 1,
-            'tag3' => 1,
-            'tag4' => 1,
-            'ut' => 1,
-            'w3c' => 1,
-            'assurance' => 1,
-            'coding-style' => 1,
-            'quality' => 1,
-            'standards' => 1,
-        ];
-        $tags = self::$privateLinkDB->linksCountPerTag();
-
-        $this->assertEquals($expected, $tags, var_export($tags, true));
-    }
-
-    /**
-     * Test linksCountPerTag all tags with filter.
-     * Equal occurrences should be sorted alphabetically.
-     */
-    public function testCountLinkPerTagAllWithFilter()
-    {
-        $expected = [
-            'gnu' => 2,
-            'hashtag' => 2,
-            '-exclude' => 1,
-            '.hidden' => 1,
-            'free' => 1,
-            'media' => 1,
-            'software' => 1,
-            'stallman' => 1,
-            'stuff' => 1,
-            'web' => 1,
-        ];
-        $tags = self::$privateLinkDB->linksCountPerTag(['gnu']);
-
-        $this->assertEquals($expected, $tags, var_export($tags, true));
-    }
-
-    /**
-     * Test linksCountPerTag public tags with filter.
-     * Equal occurrences should be sorted alphabetically.
-     */
-    public function testCountLinkPerTagPublicWithFilter()
-    {
-        $expected = [
-            'gnu' => 2,
-            'hashtag' => 2,
-            '-exclude' => 1,
-            '.hidden' => 1,
-            'free' => 1,
-            'media' => 1,
-            'software' => 1,
-            'stallman' => 1,
-            'stuff' => 1,
-            'web' => 1,
-        ];
-        $tags = self::$privateLinkDB->linksCountPerTag(['gnu'], 'public');
-
-        $this->assertEquals($expected, $tags, var_export($tags, true));
-    }
-
-    /**
-     * Test linksCountPerTag public tags with filter.
-     * Equal occurrences should be sorted alphabetically.
-     */
-    public function testCountLinkPerTagPrivateWithFilter()
-    {
-        $expected = [
-            'cartoon' => 1,
-            'dev' => 1,
-            'tag1' => 1,
-            'tag2' => 1,
-            'tag3' => 1,
-            'tag4' => 1,
-        ];
-        $tags = self::$privateLinkDB->linksCountPerTag(['dev'], 'private');
-
-        $this->assertEquals($expected, $tags, var_export($tags, true));
-    }
-
-    /**
-     * Make sure that bookmarks with the same timestamp have a consistent order:
-     * if their creation date is equal, bookmarks are sorted by ID DESC.
-     */
-    public function testConsistentOrder()
-    {
-        $nextId = 43;
-        $creation = DateTime::createFromFormat('Ymd_His', '20190807_130444');
-        $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-        for ($i = 0; $i < 4; ++$i) {
-            $linkDB[$nextId + $i] = [
-                'id' => $nextId + $i,
-                'url' => 'http://' . $i,
-                'created' => $creation,
-                'title' => true,
-                'description' => true,
-                'tags' => true,
-            ];
-        }
-
-        // Check 4 new links 4 times
-        for ($i = 0; $i < 4; ++$i) {
-            $linkDB->save('tests');
-            $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-            $count = 3;
-            foreach ($linkDB as $link) {
-                if ($link['sticky'] === true) {
-                    continue;
-                }
-                $this->assertEquals($nextId + $count, $link['id']);
-                $this->assertEquals('http://' . $count, $link['url']);
-                if (--$count < 0) {
-                    break;
-                }
-            }
-        }
-    }
-}
diff --git a/tests/legacy/LegacyLinkFilterTest.php b/tests/legacy/LegacyLinkFilterTest.php
deleted file mode 100644
index 78e27a7b..00000000
--- a/tests/legacy/LegacyLinkFilterTest.php
+++ /dev/null
@@ -1,510 +0,0 @@
-write(self::$testDatastore);
-        self::$linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-        self::$linkFilter = new LegacyLinkFilter(self::$linkDB);
-    }
-
-    /**
-     * Blank filter.
-     */
-    public function testFilter()
-    {
-        $this->assertEquals(
-            self::$refDB->countLinks(),
-            count(self::$linkFilter->filter('', ''))
-        );
-
-        $this->assertEquals(
-            self::$refDB->countLinks(),
-            count(self::$linkFilter->filter('', '', 'all'))
-        );
-
-        $this->assertEquals(
-            self::$refDB->countLinks(),
-            count(self::$linkFilter->filter('', '', 'randomstr'))
-        );
-
-        // Private only.
-        $this->assertEquals(
-            self::$refDB->countPrivateLinks(),
-            count(self::$linkFilter->filter('', '', false, 'private'))
-        );
-
-        // Public only.
-        $this->assertEquals(
-            self::$refDB->countPublicLinks(),
-            count(self::$linkFilter->filter('', '', false, 'public'))
-        );
-
-        $this->assertEquals(
-            ReferenceLinkDB::$NB_LINKS_TOTAL,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, ''))
-        );
-
-        $this->assertEquals(
-            self::$refDB->countUntaggedLinks(),
-            count(
-                self::$linkFilter->filter(
-                    LegacyLinkFilter::$FILTER_TAG,
-                    /*$request=*/
-                    '',
-                    /*$casesensitive=*/
-                    false,
-                    /*$visibility=*/
-                    'all',
-                    /*$untaggedonly=*/
-                    true
-                )
-            )
-        );
-
-        $this->assertEquals(
-            ReferenceLinkDB::$NB_LINKS_TOTAL,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, ''))
-        );
-    }
-
-    /**
-     * Filter bookmarks using a tag
-     */
-    public function testFilterOneTag()
-    {
-        $this->assertEquals(
-            4,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'web', false))
-        );
-
-        $this->assertEquals(
-            4,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'web', false, 'all'))
-        );
-
-        $this->assertEquals(
-            4,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'web', false, 'default-blabla'))
-        );
-
-        // Private only.
-        $this->assertEquals(
-            1,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'web', false, 'private'))
-        );
-
-        // Public only.
-        $this->assertEquals(
-            3,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'web', false, 'public'))
-        );
-    }
-
-    /**
-     * Filter bookmarks using a tag - case-sensitive
-     */
-    public function testFilterCaseSensitiveTag()
-    {
-        $this->assertEquals(
-            0,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'mercurial', true))
-        );
-
-        $this->assertEquals(
-            1,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'Mercurial', true))
-        );
-    }
-
-    /**
-     * Filter bookmarks using a tag combination
-     */
-    public function testFilterMultipleTags()
-    {
-        $this->assertEquals(
-            2,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'dev cartoon', false))
-        );
-    }
-
-    /**
-     * Filter bookmarks using a non-existent tag
-     */
-    public function testFilterUnknownTag()
-    {
-        $this->assertEquals(
-            0,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'null', false))
-        );
-    }
-
-    /**
-     * Return bookmarks for a given day
-     */
-    public function testFilterDay()
-    {
-        $this->assertEquals(
-            4,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_DAY, '20121206'))
-        );
-    }
-
-    /**
-     * 404 - day not found
-     */
-    public function testFilterUnknownDay()
-    {
-        $this->assertEquals(
-            0,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_DAY, '19700101'))
-        );
-    }
-
-    /**
-     * Use an invalid date format
-     */
-    public function testFilterInvalidDayWithChars()
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessageRegExp('/Invalid date format/');
-
-        self::$linkFilter->filter(LegacyLinkFilter::$FILTER_DAY, 'Rainy day, dream away');
-    }
-
-    /**
-     * Use an invalid date format
-     */
-    public function testFilterInvalidDayDigits()
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessageRegExp('/Invalid date format/');
-
-        self::$linkFilter->filter(LegacyLinkFilter::$FILTER_DAY, '20');
-    }
-
-    /**
-     * Retrieve a link entry with its hash
-     */
-    public function testFilterSmallHash()
-    {
-        $links = self::$linkFilter->filter(LegacyLinkFilter::$FILTER_HASH, 'IuWvgA');
-
-        $this->assertEquals(
-            1,
-            count($links)
-        );
-
-        $this->assertEquals(
-            'MediaGoblin',
-            $links[7]['title']
-        );
-    }
-
-    /**
-     * No link for this hash
-     */
-    public function testFilterUnknownSmallHash()
-    {
-        $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
-
-        self::$linkFilter->filter(LegacyLinkFilter::$FILTER_HASH, 'Iblaah');
-    }
-
-    /**
-     * Full-text search - no result found.
-     */
-    public function testFilterFullTextNoResult()
-    {
-        $this->assertEquals(
-            0,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'azertyuiop'))
-        );
-    }
-
-    /**
-     * Full-text search - result from a link's URL
-     */
-    public function testFilterFullTextURL()
-    {
-        $this->assertEquals(
-            2,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'ars.userfriendly.org'))
-        );
-
-        $this->assertEquals(
-            2,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'ars org'))
-        );
-    }
-
-    /**
-     * Full-text search - result from a link's title only
-     */
-    public function testFilterFullTextTitle()
-    {
-        // use miscellaneous cases
-        $this->assertEquals(
-            2,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'userfriendly -'))
-        );
-        $this->assertEquals(
-            2,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'UserFriendly -'))
-        );
-        $this->assertEquals(
-            2,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'uSeRFrIendlY -'))
-        );
-
-        // use miscellaneous case and offset
-        $this->assertEquals(
-            2,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'RFrIendL'))
-        );
-    }
-
-    /**
-     * Full-text search - result from the link's description only
-     */
-    public function testFilterFullTextDescription()
-    {
-        $this->assertEquals(
-            1,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'publishing media'))
-        );
-
-        $this->assertEquals(
-            1,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'mercurial w3c'))
-        );
-
-        $this->assertEquals(
-            3,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, '"free software"'))
-        );
-    }
-
-    /**
-     * Full-text search - result from the link's tags only
-     */
-    public function testFilterFullTextTags()
-    {
-        $this->assertEquals(
-            6,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'web'))
-        );
-
-        $this->assertEquals(
-            6,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'web', 'all'))
-        );
-
-        $this->assertEquals(
-            6,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'web', 'bla'))
-        );
-
-        // Private only.
-        $this->assertEquals(
-            1,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'web', false, 'private'))
-        );
-
-        // Public only.
-        $this->assertEquals(
-            5,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'web', false, 'public'))
-        );
-    }
-
-    /**
-     * Full-text search - result set from mixed sources
-     */
-    public function testFilterFullTextMixed()
-    {
-        $this->assertEquals(
-            3,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'free software'))
-        );
-    }
-
-    /**
-     * Full-text search - test exclusion with '-'.
-     */
-    public function testExcludeSearch()
-    {
-        $this->assertEquals(
-            1,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'free -gnu'))
-        );
-
-        $this->assertEquals(
-            ReferenceLinkDB::$NB_LINKS_TOTAL - 1,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, '-revolution'))
-        );
-    }
-
-    /**
-     * Full-text search - test AND, exact terms and exclusion combined, across fields.
-     */
-    public function testMultiSearch()
-    {
-        $this->assertEquals(
-            2,
-            count(self::$linkFilter->filter(
-                LegacyLinkFilter::$FILTER_TEXT,
-                '"Free Software " stallman "read this" @website stuff'
-            ))
-        );
-
-        $this->assertEquals(
-            1,
-            count(self::$linkFilter->filter(
-                LegacyLinkFilter::$FILTER_TEXT,
-                '"free software " stallman "read this" -beard @website stuff'
-            ))
-        );
-    }
-
-    /**
-     * Full-text search - make sure that exact search won't work across fields.
-     */
-    public function testSearchExactTermMultiFieldsKo()
-    {
-        $this->assertEquals(
-            0,
-            count(self::$linkFilter->filter(
-                LegacyLinkFilter::$FILTER_TEXT,
-                '"designer naming"'
-            ))
-        );
-
-        $this->assertEquals(
-            0,
-            count(self::$linkFilter->filter(
-                LegacyLinkFilter::$FILTER_TEXT,
-                '"designernaming"'
-            ))
-        );
-    }
-
-    /**
-     * Tag search with exclusion.
-     */
-    public function testTagFilterWithExclusion()
-    {
-        $this->assertEquals(
-            1,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'gnu -free'))
-        );
-
-        $this->assertEquals(
-            ReferenceLinkDB::$NB_LINKS_TOTAL - 1,
-            count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, '-free'))
-        );
-    }
-
-    /**
-     * Test crossed search (terms + tags).
-     */
-    public function testFilterCrossedSearch()
-    {
-        $terms = '"Free Software " stallman "read this" @website stuff';
-        $tags = 'free';
-        $this->assertEquals(
-            1,
-            count(self::$linkFilter->filter(
-                LegacyLinkFilter::$FILTER_TAG | LegacyLinkFilter::$FILTER_TEXT,
-                [$tags, $terms]
-            ))
-        );
-        $this->assertEquals(
-            2,
-            count(self::$linkFilter->filter(
-                LegacyLinkFilter::$FILTER_TAG | LegacyLinkFilter::$FILTER_TEXT,
-                ['', $terms]
-            ))
-        );
-        $this->assertEquals(
-            1,
-            count(self::$linkFilter->filter(
-                LegacyLinkFilter::$FILTER_TAG | LegacyLinkFilter::$FILTER_TEXT,
-                [false, 'PSR-2']
-            ))
-        );
-        $this->assertEquals(
-            1,
-            count(self::$linkFilter->filter(
-                LegacyLinkFilter::$FILTER_TAG | LegacyLinkFilter::$FILTER_TEXT,
-                [$tags, '']
-            ))
-        );
-        $this->assertEquals(
-            ReferenceLinkDB::$NB_LINKS_TOTAL,
-            count(self::$linkFilter->filter(
-                LegacyLinkFilter::$FILTER_TAG | LegacyLinkFilter::$FILTER_TEXT,
-                ''
-            ))
-        );
-    }
-
-    /**
-     * Filter bookmarks by #hashtag.
-     */
-    public function testFilterByHashtag()
-    {
-        $hashtag = 'hashtag';
-        $this->assertEquals(
-            3,
-            count(self::$linkFilter->filter(
-                LegacyLinkFilter::$FILTER_TAG,
-                $hashtag
-            ))
-        );
-
-        $hashtag = 'private';
-        $this->assertEquals(
-            1,
-            count(self::$linkFilter->filter(
-                LegacyLinkFilter::$FILTER_TAG,
-                $hashtag,
-                false,
-                'private'
-            ))
-        );
-    }
-}
diff --git a/tests/legacy/LegacyUpdaterTest.php b/tests/legacy/LegacyUpdaterTest.php
deleted file mode 100644
index 293fa719..00000000
--- a/tests/legacy/LegacyUpdaterTest.php
+++ /dev/null
@@ -1,885 +0,0 @@
-conf = new ConfigManager(self::$configFile);
-    }
-
-    /**
-     * Test UpdaterUtils::read_updates_file with an empty/missing file.
-     */
-    public function testReadEmptyUpdatesFile()
-    {
-        $this->assertEquals([], UpdaterUtils::readUpdatesFile(''));
-        $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt';
-        touch($updatesFile);
-        $this->assertEquals([], UpdaterUtils::readUpdatesFile($updatesFile));
-        unlink($updatesFile);
-    }
-
-    /**
-     * Test read/write updates file.
-     */
-    public function testReadWriteUpdatesFile()
-    {
-        $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt';
-        $updatesMethods = ['m1', 'm2', 'm3'];
-
-        UpdaterUtils::writeUpdatesFile($updatesFile, $updatesMethods);
-        $readMethods = UpdaterUtils::readUpdatesFile($updatesFile);
-        $this->assertEquals($readMethods, $updatesMethods);
-
-        // Update
-        $updatesMethods[] = 'm4';
-        UpdaterUtils::writeUpdatesFile($updatesFile, $updatesMethods);
-        $readMethods = UpdaterUtils::readUpdatesFile($updatesFile);
-        $this->assertEquals($readMethods, $updatesMethods);
-        unlink($updatesFile);
-    }
-
-    /**
-     * Test errors in UpdaterUtils::write_updates_file(): empty updates file.
-     */
-    public function testWriteEmptyUpdatesFile()
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessageRegExp('/Updates file path is not set(.*)/');
-
-        UpdaterUtils::writeUpdatesFile('', ['test']);
-    }
-
-    /**
-     * Test errors in UpdaterUtils::write_updates_file(): not writable updates file.
-     */
-    public function testWriteUpdatesFileNotWritable()
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessageRegExp('/Unable to write(.*)/');
-
-        $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt';
-        touch($updatesFile);
-        chmod($updatesFile, 0444);
-        try {
-            @UpdaterUtils::writeUpdatesFile($updatesFile, ['test']);
-        } catch (Exception $e) {
-            unlink($updatesFile);
-            throw $e;
-        }
-    }
-
-    /**
-     * Test the update() method, with no update to run.
-     *   1. Everything already run.
-     *   2. User is logged out.
-     */
-    public function testNoUpdates()
-    {
-        $updates = [
-            'updateMethodDummy1',
-            'updateMethodDummy2',
-            'updateMethodDummy3',
-            'updateMethodException',
-        ];
-        $updater = new DummyUpdater($updates, [], $this->conf, true);
-        $this->assertEquals([], $updater->update());
-
-        $updater = new DummyUpdater([], [], $this->conf, false);
-        $this->assertEquals([], $updater->update());
-    }
-
-    /**
-     * Test the update() method, with all updates to run (except the failing one).
-     */
-    public function testUpdatesFirstTime()
-    {
-        $updates = ['updateMethodException',];
-        $expectedUpdates = [
-            'updateMethodDummy1',
-            'updateMethodDummy2',
-            'updateMethodDummy3',
-        ];
-        $updater = new DummyUpdater($updates, [], $this->conf, true);
-        $this->assertEquals($expectedUpdates, $updater->update());
-    }
-
-    /**
-     * Test the update() method, only one update to run.
-     */
-    public function testOneUpdate()
-    {
-        $updates = [
-            'updateMethodDummy1',
-            'updateMethodDummy3',
-            'updateMethodException',
-        ];
-        $expectedUpdate = ['updateMethodDummy2'];
-
-        $updater = new DummyUpdater($updates, [], $this->conf, true);
-        $this->assertEquals($expectedUpdate, $updater->update());
-    }
-
-    /**
-     * Test Update failed.
-     */
-    public function testUpdateFailed()
-    {
-        $this->expectException(\Exception::class);
-
-        $updates = [
-            'updateMethodDummy1',
-            'updateMethodDummy2',
-            'updateMethodDummy3',
-        ];
-
-        $updater = new DummyUpdater($updates, [], $this->conf, true);
-        $updater->update();
-    }
-
-    /**
-     * Test update mergeDeprecatedConfig:
-     *      1. init a config file.
-     *      2. init a options.php file with update value.
-     *      3. merge.
-     *      4. check updated value in config file.
-     */
-    public function testUpdateMergeDeprecatedConfig()
-    {
-        $this->conf->setConfigFile('tests/utils/config/configPhp');
-        $this->conf->reset();
-
-        $optionsFile = 'tests/updater/options.php';
-        $options = 'conf->setConfigFile('tests/updater/config');
-
-        // merge configs
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        // This writes a new config file in tests/updater/config.php
-        $updater->updateMethodMergeDeprecatedConfigFile();
-
-        // make sure updated field is changed
-        $this->conf->reload();
-        $this->assertTrue($this->conf->get('privacy.default_private_links'));
-        $this->assertFalse(is_file($optionsFile));
-        // Delete the generated file.
-        unlink($this->conf->getConfigFileExt());
-    }
-
-    /**
-     * Test mergeDeprecatedConfig in without options file.
-     */
-    public function testMergeDeprecatedConfigNoFile()
-    {
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $updater->updateMethodMergeDeprecatedConfigFile();
-
-        $this->assertEquals('root', $this->conf->get('credentials.login'));
-    }
-
-    /**
-     * Test renameDashTags update method.
-     */
-    public function testRenameDashTags()
-    {
-        $refDB = new ReferenceLinkDB(true);
-        $refDB->write(self::$testDatastore);
-        $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-
-        $this->assertEmpty($linkDB->filterSearch(['searchtags' => 'exclude']));
-        $updater = new LegacyUpdater([], $linkDB, $this->conf, true);
-        $updater->updateMethodRenameDashTags();
-        $this->assertNotEmpty($linkDB->filterSearch(['searchtags' =>  'exclude']));
-    }
-
-    /**
-     * Convert old PHP config file to JSON config.
-     */
-    public function testConfigToJson()
-    {
-        $configFile = 'tests/utils/config/configPhp';
-        $this->conf->setConfigFile($configFile);
-        $this->conf->reset();
-
-        // The ConfigIO is initialized with ConfigPhp.
-        $this->assertTrue($this->conf->getConfigIO() instanceof ConfigPhp);
-
-        $updater = new LegacyUpdater([], [], $this->conf, false);
-        $done = $updater->updateMethodConfigToJson();
-        $this->assertTrue($done);
-
-        // The ConfigIO has been updated to ConfigJson.
-        $this->assertTrue($this->conf->getConfigIO() instanceof ConfigJson);
-        $this->assertTrue(file_exists($this->conf->getConfigFileExt()));
-
-        // Check JSON config data.
-        $this->conf->reload();
-        $this->assertEquals('root', $this->conf->get('credentials.login'));
-        $this->assertEquals('lala', $this->conf->get('redirector.url'));
-        $this->assertEquals('data/datastore.php', $this->conf->get('resource.datastore'));
-        $this->assertEquals('1', $this->conf->get('plugins.WALLABAG_VERSION'));
-
-        rename($configFile . '.save.php', $configFile . '.php');
-        unlink($this->conf->getConfigFileExt());
-    }
-
-    /**
-     * Launch config conversion update with an existing JSON file => nothing to do.
-     */
-    public function testConfigToJsonNothingToDo()
-    {
-        $filetime = filemtime($this->conf->getConfigFileExt());
-        $updater = new LegacyUpdater([], [], $this->conf, false);
-        $done = $updater->updateMethodConfigToJson();
-        $this->assertTrue($done);
-        $expected = filemtime($this->conf->getConfigFileExt());
-        $this->assertEquals($expected, $filetime);
-    }
-
-    /**
-     * Test escapeUnescapedConfig with valid data.
-     */
-    public function testEscapeConfig()
-    {
-        $sandbox = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandbox . '.json.php');
-        $this->conf = new ConfigManager($sandbox);
-        $title = '';
-        $headerLink = '';
-        $this->conf->set('general.title', $title);
-        $this->conf->set('general.header_link', $headerLink);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $done = $updater->updateMethodEscapeUnescapedConfig();
-        $this->assertTrue($done);
-        $this->conf->reload();
-        $this->assertEquals(escape($title), $this->conf->get('general.title'));
-        $this->assertEquals(escape($headerLink), $this->conf->get('general.header_link'));
-        unlink($sandbox . '.json.php');
-    }
-
-    /**
-     * Test updateMethodApiSettings(): create default settings for the API (enabled + secret).
-     */
-    public function testUpdateApiSettings()
-    {
-        $confFile = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $confFile . '.json.php');
-        $conf = new ConfigManager($confFile);
-        $updater = new LegacyUpdater([], [], $conf, true);
-
-        $this->assertFalse($conf->exists('api.enabled'));
-        $this->assertFalse($conf->exists('api.secret'));
-        $updater->updateMethodApiSettings();
-        $conf->reload();
-        $this->assertTrue($conf->get('api.enabled'));
-        $this->assertTrue($conf->exists('api.secret'));
-        unlink($confFile . '.json.php');
-    }
-
-    /**
-     * Test updateMethodApiSettings(): already set, do nothing.
-     */
-    public function testUpdateApiSettingsNothingToDo()
-    {
-        $confFile = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $confFile . '.json.php');
-        $conf = new ConfigManager($confFile);
-        $conf->set('api.enabled', false);
-        $conf->set('api.secret', '');
-        $updater = new LegacyUpdater([], [], $conf, true);
-        $updater->updateMethodApiSettings();
-        $this->assertFalse($conf->get('api.enabled'));
-        $this->assertEmpty($conf->get('api.secret'));
-        unlink($confFile . '.json.php');
-    }
-
-    /**
-     * Test updateMethodDatastoreIds().
-     */
-    public function testDatastoreIds()
-    {
-        $links = [
-            '20121206_182539' => [
-                'linkdate' => '20121206_182539',
-                'title' => 'Geek and Poke',
-                'url' => 'http://geek-and-poke.com/',
-                'description' => 'desc',
-                'tags' => 'dev cartoon tag1  tag2   tag3  tag4   ',
-                'updated' => '20121206_190301',
-                'private' => false,
-            ],
-            '20121206_172539' => [
-                'linkdate' => '20121206_172539',
-                'title' => 'UserFriendly - Samba',
-                'url' => 'http://ars.userfriendly.org/cartoons/?id=20010306',
-                'description' => '',
-                'tags' => 'samba cartoon web',
-                'private' => false,
-            ],
-            '20121206_142300' => [
-                'linkdate' => '20121206_142300',
-                'title' => 'UserFriendly - Web Designer',
-                'url' => 'http://ars.userfriendly.org/cartoons/?id=20121206',
-                'description' => 'Naming conventions... #private',
-                'tags' => 'samba cartoon web',
-                'private' => true,
-            ],
-        ];
-        $refDB = new ReferenceLinkDB(true);
-        $refDB->setLinks($links);
-        $refDB->write(self::$testDatastore);
-        $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-
-        $checksum = hash_file('sha1', self::$testDatastore);
-
-        $this->conf->set('resource.data_dir', 'sandbox');
-        $this->conf->set('resource.datastore', self::$testDatastore);
-
-        $updater = new LegacyUpdater([], $linkDB, $this->conf, true);
-        $this->assertTrue($updater->updateMethodDatastoreIds());
-
-        $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-
-        $backupFiles = glob($this->conf->get('resource.data_dir') . '/datastore.' . date('YmdH') . '*.php');
-        $backup = null;
-        foreach ($backupFiles as $backupFile) {
-            if (strpos($backupFile, '_1') === false) {
-                $backup = $backupFile;
-            }
-        }
-        $this->assertNotNull($backup);
-        $this->assertFileExists($backup);
-        $this->assertEquals($checksum, hash_file('sha1', $backup));
-        unlink($backup);
-
-        $this->assertEquals(3, count($linkDB));
-        $this->assertTrue(isset($linkDB[0]));
-        $this->assertFalse(isset($linkDB[0]['linkdate']));
-        $this->assertEquals(0, $linkDB[0]['id']);
-        $this->assertEquals('UserFriendly - Web Designer', $linkDB[0]['title']);
-        $this->assertEquals('http://ars.userfriendly.org/cartoons/?id=20121206', $linkDB[0]['url']);
-        $this->assertEquals('Naming conventions... #private', $linkDB[0]['description']);
-        $this->assertEquals('samba cartoon web', $linkDB[0]['tags']);
-        $this->assertTrue($linkDB[0]['private']);
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_142300'),
-            $linkDB[0]['created']
-        );
-
-        $this->assertTrue(isset($linkDB[1]));
-        $this->assertFalse(isset($linkDB[1]['linkdate']));
-        $this->assertEquals(1, $linkDB[1]['id']);
-        $this->assertEquals('UserFriendly - Samba', $linkDB[1]['title']);
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_172539'),
-            $linkDB[1]['created']
-        );
-
-        $this->assertTrue(isset($linkDB[2]));
-        $this->assertFalse(isset($linkDB[2]['linkdate']));
-        $this->assertEquals(2, $linkDB[2]['id']);
-        $this->assertEquals('Geek and Poke', $linkDB[2]['title']);
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_182539'),
-            $linkDB[2]['created']
-        );
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_190301'),
-            $linkDB[2]['updated']
-        );
-    }
-
-    /**
-     * Test updateMethodDatastoreIds() with the update already applied: nothing to do.
-     */
-    public function testDatastoreIdsNothingToDo()
-    {
-        $refDB = new ReferenceLinkDB(true);
-        $refDB->write(self::$testDatastore);
-        $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-
-        $this->conf->set('resource.data_dir', 'sandbox');
-        $this->conf->set('resource.datastore', self::$testDatastore);
-
-        $checksum = hash_file('sha1', self::$testDatastore);
-        $updater = new LegacyUpdater([], $linkDB, $this->conf, true);
-        $this->assertTrue($updater->updateMethodDatastoreIds());
-        $this->assertEquals($checksum, hash_file('sha1', self::$testDatastore));
-    }
-
-    /**
-     * Test defaultTheme update with default settings: nothing to do.
-     */
-    public function testDefaultThemeWithDefaultSettings()
-    {
-        $sandbox = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandbox . '.json.php');
-        $this->conf = new ConfigManager($sandbox);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodDefaultTheme());
-
-        $this->assertEquals('tpl/', $this->conf->get('resource.raintpl_tpl'));
-        $this->assertEquals('default', $this->conf->get('resource.theme'));
-        $this->conf = new ConfigManager($sandbox);
-        $this->assertEquals('tpl/', $this->conf->get('resource.raintpl_tpl'));
-        $this->assertEquals('default', $this->conf->get('resource.theme'));
-        unlink($sandbox . '.json.php');
-    }
-
-    /**
-     * Test defaultTheme update with a custom theme in a subfolder
-     */
-    public function testDefaultThemeWithCustomTheme()
-    {
-        $theme = 'iamanartist';
-        $sandbox = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandbox . '.json.php');
-        $this->conf = new ConfigManager($sandbox);
-        mkdir('sandbox/' . $theme);
-        touch('sandbox/' . $theme . '/linklist.html');
-        $this->conf->set('resource.raintpl_tpl', 'sandbox/' . $theme . '/');
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodDefaultTheme());
-
-        $this->assertEquals('sandbox', $this->conf->get('resource.raintpl_tpl'));
-        $this->assertEquals($theme, $this->conf->get('resource.theme'));
-        $this->conf = new ConfigManager($sandbox);
-        $this->assertEquals('sandbox', $this->conf->get('resource.raintpl_tpl'));
-        $this->assertEquals($theme, $this->conf->get('resource.theme'));
-        unlink($sandbox . '.json.php');
-        unlink('sandbox/' . $theme . '/linklist.html');
-        rmdir('sandbox/' . $theme);
-    }
-
-    /**
-     * Test updateMethodEscapeMarkdown with markdown plugin enabled
-     * => setting markdown_escape set to false.
-     */
-    public function testEscapeMarkdownSettingToFalse()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-
-        $this->conf->set('general.enabled_plugins', ['markdown']);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodEscapeMarkdown());
-        $this->assertFalse($this->conf->get('security.markdown_escape'));
-
-        // reload from file
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->assertFalse($this->conf->get('security.markdown_escape'));
-    }
-
-
-    /**
-     * Test updateMethodEscapeMarkdown with markdown plugin disabled
-     * => setting markdown_escape set to true.
-     */
-    public function testEscapeMarkdownSettingToTrue()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-
-        $this->conf->set('general.enabled_plugins', []);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodEscapeMarkdown());
-        $this->assertTrue($this->conf->get('security.markdown_escape'));
-
-        // reload from file
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->assertTrue($this->conf->get('security.markdown_escape'));
-    }
-
-    /**
-     * Test updateMethodEscapeMarkdown with nothing to do (setting already enabled)
-     */
-    public function testEscapeMarkdownSettingNothingToDoEnabled()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->conf->set('security.markdown_escape', true);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodEscapeMarkdown());
-        $this->assertTrue($this->conf->get('security.markdown_escape'));
-    }
-
-    /**
-     * Test updateMethodEscapeMarkdown with nothing to do (setting already disabled)
-     */
-    public function testEscapeMarkdownSettingNothingToDoDisabled()
-    {
-        $this->conf->set('security.markdown_escape', false);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodEscapeMarkdown());
-        $this->assertFalse($this->conf->get('security.markdown_escape'));
-    }
-
-    /**
-     * Test updateMethodPiwikUrl with valid data
-     */
-    public function testUpdatePiwikUrlValid()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-        $url = 'mypiwik.tld';
-        $this->conf->set('plugins.PIWIK_URL', $url);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodPiwikUrl());
-        $this->assertEquals('http://' . $url, $this->conf->get('plugins.PIWIK_URL'));
-
-        // reload from file
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->assertEquals('http://' . $url, $this->conf->get('plugins.PIWIK_URL'));
-    }
-
-    /**
-     * Test updateMethodPiwikUrl without setting
-     */
-    public function testUpdatePiwikUrlEmpty()
-    {
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodPiwikUrl());
-        $this->assertEmpty($this->conf->get('plugins.PIWIK_URL'));
-    }
-
-    /**
-     * Test updateMethodPiwikUrl: valid URL, nothing to do
-     */
-    public function testUpdatePiwikUrlNothingToDo()
-    {
-        $url = 'https://mypiwik.tld';
-        $this->conf->set('plugins.PIWIK_URL', $url);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodPiwikUrl());
-        $this->assertEquals($url, $this->conf->get('plugins.PIWIK_URL'));
-    }
-
-    /**
-     * Test updateMethodAtomDefault with show_atom set to false
-     * => update to true.
-     */
-    public function testUpdateMethodAtomDefault()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->conf->set('feed.show_atom', false);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodAtomDefault());
-        $this->assertTrue($this->conf->get('feed.show_atom'));
-        // reload from file
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->assertTrue($this->conf->get('feed.show_atom'));
-    }
-    /**
-     * Test updateMethodAtomDefault with show_atom not set.
-     * => nothing to do
-     */
-    public function testUpdateMethodAtomDefaultNoExist()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodAtomDefault());
-        $this->assertTrue($this->conf->get('feed.show_atom'));
-    }
-    /**
-     * Test updateMethodAtomDefault with show_atom set to true.
-     * => nothing to do
-     */
-    public function testUpdateMethodAtomDefaultAlreadyTrue()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->conf->set('feed.show_atom', true);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodAtomDefault());
-        $this->assertTrue($this->conf->get('feed.show_atom'));
-    }
-
-    /**
-     * Test updateMethodDownloadSizeAndTimeoutConf, it should be set if none is already defined.
-     */
-    public function testUpdateMethodDownloadSizeAndTimeoutConf()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf());
-        $this->assertEquals(4194304, $this->conf->get('general.download_max_size'));
-        $this->assertEquals(30, $this->conf->get('general.download_timeout'));
-
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->assertEquals(4194304, $this->conf->get('general.download_max_size'));
-        $this->assertEquals(30, $this->conf->get('general.download_timeout'));
-    }
-
-    /**
-     * Test updateMethodDownloadSizeAndTimeoutConf, it shouldn't be set if it is already defined.
-     */
-    public function testUpdateMethodDownloadSizeAndTimeoutConfIgnore()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->conf->set('general.download_max_size', 38);
-        $this->conf->set('general.download_timeout', 70);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf());
-        $this->assertEquals(38, $this->conf->get('general.download_max_size'));
-        $this->assertEquals(70, $this->conf->get('general.download_timeout'));
-    }
-
-    /**
-     * Test updateMethodDownloadSizeAndTimeoutConf, only the maz size should be set here.
-     */
-    public function testUpdateMethodDownloadSizeAndTimeoutConfOnlySize()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->conf->set('general.download_max_size', 38);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf());
-        $this->assertEquals(38, $this->conf->get('general.download_max_size'));
-        $this->assertEquals(30, $this->conf->get('general.download_timeout'));
-    }
-
-    /**
-     * Test updateMethodDownloadSizeAndTimeoutConf, only the time out should be set here.
-     */
-    public function testUpdateMethodDownloadSizeAndTimeoutConfOnlyTimeout()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->conf->set('general.download_timeout', 3);
-        $updater = new LegacyUpdater([], [], $this->conf, true);
-        $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf());
-        $this->assertEquals(4194304, $this->conf->get('general.download_max_size'));
-        $this->assertEquals(3, $this->conf->get('general.download_timeout'));
-    }
-
-    /**
-     * Test updateMethodWebThumbnailer with thumbnails enabled.
-     */
-    public function testUpdateMethodWebThumbnailerEnabled()
-    {
-        $this->conf->remove('thumbnails');
-        $this->conf->set('thumbnail.enable_thumbnails', true);
-        $updater = new LegacyUpdater([], [], $this->conf, true, $_SESSION);
-        $this->assertTrue($updater->updateMethodWebThumbnailer());
-        $this->assertFalse($this->conf->exists('thumbnail'));
-        $this->assertEquals(\Shaarli\Thumbnailer::MODE_ALL, $this->conf->get('thumbnails.mode'));
-        $this->assertEquals(125, $this->conf->get('thumbnails.width'));
-        $this->assertEquals(90, $this->conf->get('thumbnails.height'));
-        $this->assertContainsPolyfill('You have enabled or changed thumbnails', $_SESSION['warnings'][0]);
-    }
-
-    /**
-     * Test updateMethodWebThumbnailer with thumbnails disabled.
-     */
-    public function testUpdateMethodWebThumbnailerDisabled()
-    {
-        if (isset($_SESSION['warnings'])) {
-            unset($_SESSION['warnings']);
-        }
-
-        $this->conf->remove('thumbnails');
-        $this->conf->set('thumbnail.enable_thumbnails', false);
-        $updater = new LegacyUpdater([], [], $this->conf, true, $_SESSION);
-        $this->assertTrue($updater->updateMethodWebThumbnailer());
-        $this->assertFalse($this->conf->exists('thumbnail'));
-        $this->assertEquals(Thumbnailer::MODE_NONE, $this->conf->get('thumbnails.mode'));
-        $this->assertEquals(125, $this->conf->get('thumbnails.width'));
-        $this->assertEquals(90, $this->conf->get('thumbnails.height'));
-        $this->assertTrue(empty($_SESSION['warnings']));
-    }
-
-    /**
-     * Test updateMethodWebThumbnailer with thumbnails disabled.
-     */
-    public function testUpdateMethodWebThumbnailerNothingToDo()
-    {
-        if (isset($_SESSION['warnings'])) {
-            unset($_SESSION['warnings']);
-        }
-
-        $updater = new LegacyUpdater([], [], $this->conf, true, $_SESSION);
-        $this->assertTrue($updater->updateMethodWebThumbnailer());
-        $this->assertFalse($this->conf->exists('thumbnail'));
-        $this->assertEquals(Thumbnailer::MODE_COMMON, $this->conf->get('thumbnails.mode'));
-        $this->assertEquals(90, $this->conf->get('thumbnails.width'));
-        $this->assertEquals(53, $this->conf->get('thumbnails.height'));
-        $this->assertTrue(empty($_SESSION['warnings']));
-    }
-
-    /**
-     * Test updateMethodSetSticky().
-     */
-    public function testUpdateStickyValid()
-    {
-        $blank = [
-            'id' => 1,
-            'url' => 'z',
-            'title' => '',
-            'description' => '',
-            'tags' => '',
-            'created' => new DateTime(),
-        ];
-        $links = [
-            1 => ['id' => 1] + $blank,
-            2 => ['id' => 2] + $blank,
-        ];
-        $refDB = new ReferenceLinkDB(true);
-        $refDB->setLinks($links);
-        $refDB->write(self::$testDatastore);
-        $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-
-        $updater = new LegacyUpdater([], $linkDB, $this->conf, true);
-        $this->assertTrue($updater->updateMethodSetSticky());
-
-        $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-        foreach ($linkDB as $link) {
-            $this->assertFalse($link['sticky']);
-        }
-    }
-
-    /**
-     * Test updateMethodSetSticky().
-     */
-    public function testUpdateStickyNothingToDo()
-    {
-        $blank = [
-            'id' => 1,
-            'url' => 'z',
-            'title' => '',
-            'description' => '',
-            'tags' => '',
-            'created' => new DateTime(),
-        ];
-        $links = [
-            1 => ['id' => 1, 'sticky' => true] + $blank,
-            2 => ['id' => 2] + $blank,
-        ];
-        $refDB = new ReferenceLinkDB(true);
-        $refDB->setLinks($links);
-        $refDB->write(self::$testDatastore);
-        $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-
-        $updater = new LegacyUpdater([], $linkDB, $this->conf, true);
-        $this->assertTrue($updater->updateMethodSetSticky());
-
-        $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
-        $this->assertTrue($linkDB[1]['sticky']);
-    }
-
-    /**
-     * Test updateMethodRemoveRedirector().
-     */
-    public function testUpdateRemoveRedirector()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-        $updater = new LegacyUpdater([], null, $this->conf, true);
-        $this->assertTrue($updater->updateMethodRemoveRedirector());
-        $this->assertFalse($this->conf->exists('redirector'));
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->assertFalse($this->conf->exists('redirector'));
-    }
-
-    /**
-     * Test updateMethodFormatterSetting()
-     */
-    public function testUpdateMethodFormatterSettingDefault()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->conf->set('formatter', 'default');
-        $updater = new LegacyUpdater([], null, $this->conf, true);
-        $enabledPlugins = $this->conf->get('general.enabled_plugins');
-        $this->assertFalse(in_array('markdown', $enabledPlugins));
-        $this->assertTrue($updater->updateMethodFormatterSetting());
-        $this->assertEquals('default', $this->conf->get('formatter'));
-        $this->assertEquals($enabledPlugins, $this->conf->get('general.enabled_plugins'));
-
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->assertEquals('default', $this->conf->get('formatter'));
-        $this->assertEquals($enabledPlugins, $this->conf->get('general.enabled_plugins'));
-    }
-
-    /**
-     * Test updateMethodFormatterSetting()
-     */
-    public function testUpdateMethodFormatterSettingMarkdown()
-    {
-        $sandboxConf = 'sandbox/config';
-        copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->conf->set('formatter', 'default');
-        $updater = new LegacyUpdater([], null, $this->conf, true);
-        $enabledPlugins = $this->conf->get('general.enabled_plugins');
-        $enabledPlugins[] = 'markdown';
-        $this->conf->set('general.enabled_plugins', $enabledPlugins);
-
-        $this->assertTrue(in_array('markdown', $this->conf->get('general.enabled_plugins')));
-        $this->assertTrue($updater->updateMethodFormatterSetting());
-        $this->assertEquals('markdown', $this->conf->get('formatter'));
-        $this->assertFalse(in_array('markdown', $this->conf->get('general.enabled_plugins')));
-
-        $this->conf = new ConfigManager($sandboxConf);
-        $this->assertEquals('markdown', $this->conf->get('formatter'));
-        $this->assertFalse(in_array('markdown', $this->conf->get('general.enabled_plugins')));
-    }
-}
diff --git a/tests/netscape/BookmarkExportTest.php b/tests/netscape/BookmarkExportTest.php
deleted file mode 100644
index ce054286..00000000
--- a/tests/netscape/BookmarkExportTest.php
+++ /dev/null
@@ -1,215 +0,0 @@
-set('resource.datastore', static::$testDatastore);
-        static::$refDb = new ReferenceLinkDB();
-        static::$refDb->write(static::$testDatastore);
-        static::$history = new History('sandbox/history.php');
-        static::$pluginManager = new PluginManager(static::$conf);
-        static::$bookmarkService = new BookmarkFileService(
-            static::$conf,
-            static::$pluginManager,
-            static::$history,
-            $mutex,
-            true
-        );
-        $factory = new FormatterFactory(static::$conf, true);
-        static::$formatter = $factory->getFormatter('raw');
-    }
-
-    public function setUp(): void
-    {
-        $this->netscapeBookmarkUtils = new NetscapeBookmarkUtils(
-            static::$bookmarkService,
-            static::$conf,
-            static::$history
-        );
-    }
-
-    /**
-     * Attempt to export an invalid link selection
-     */
-    public function testFilterAndFormatInvalid()
-    {
-        $this->expectException(\Exception::class);
-        $this->expectExceptionMessageRegExp('/Invalid export selection/');
-
-        $this->netscapeBookmarkUtils->filterAndFormat(
-            self::$formatter,
-            'derp',
-            false,
-            ''
-        );
-    }
-
-    /**
-     * Prepare all bookmarks for export
-     */
-    public function testFilterAndFormatAll()
-    {
-        $links = $this->netscapeBookmarkUtils->filterAndFormat(
-            self::$formatter,
-            'all',
-            false,
-            ''
-        );
-        $this->assertEquals(self::$refDb->countLinks(), sizeof($links));
-        foreach ($links as $link) {
-            $date = $link['created'];
-            $this->assertEquals(
-                $date->getTimestamp(),
-                $link['timestamp']
-            );
-            $this->assertEquals(
-                str_replace(' ', ',', $link['tags']),
-                $link['taglist']
-            );
-        }
-    }
-
-    /**
-     * Prepare private bookmarks for export
-     */
-    public function testFilterAndFormatPrivate()
-    {
-        $links = $this->netscapeBookmarkUtils->filterAndFormat(
-            self::$formatter,
-            'private',
-            false,
-            ''
-        );
-        $this->assertEquals(self::$refDb->countPrivateLinks(), sizeof($links));
-        foreach ($links as $link) {
-            $date = $link['created'];
-            $this->assertEquals(
-                $date->getTimestamp(),
-                $link['timestamp']
-            );
-            $this->assertEquals(
-                str_replace(' ', ',', $link['tags']),
-                $link['taglist']
-            );
-        }
-    }
-
-    /**
-     * Prepare public bookmarks for export
-     */
-    public function testFilterAndFormatPublic()
-    {
-        $links = $this->netscapeBookmarkUtils->filterAndFormat(
-            self::$formatter,
-            'public',
-            false,
-            ''
-        );
-        $this->assertEquals(self::$refDb->countPublicLinks(), sizeof($links));
-        foreach ($links as $link) {
-            $date = $link['created'];
-            $this->assertEquals(
-                $date->getTimestamp(),
-                $link['timestamp']
-            );
-            $this->assertEquals(
-                str_replace(' ', ',', $link['tags']),
-                $link['taglist']
-            );
-        }
-    }
-
-    /**
-     * Do not prepend notes with the Shaarli index's URL
-     */
-    public function testFilterAndFormatDoNotPrependNoteUrl()
-    {
-        $links = $this->netscapeBookmarkUtils->filterAndFormat(
-            self::$formatter,
-            'public',
-            false,
-            ''
-        );
-        $this->assertEquals(
-            '/shaare/WDWyig',
-            $links[2]['url']
-        );
-    }
-
-    /**
-     * Prepend notes with the Shaarli index's URL
-     */
-    public function testFilterAndFormatPrependNoteUrl()
-    {
-        $indexUrl = 'http://localhost:7469/shaarli/';
-        $links = $this->netscapeBookmarkUtils->filterAndFormat(
-            self::$formatter,
-            'public',
-            true,
-            $indexUrl
-        );
-        $this->assertEquals(
-            $indexUrl . 'shaare/WDWyig',
-            $links[2]['url']
-        );
-    }
-}
diff --git a/tests/netscape/BookmarkImportTest.php b/tests/netscape/BookmarkImportTest.php
deleted file mode 100644
index d880c3aa..00000000
--- a/tests/netscape/BookmarkImportTest.php
+++ /dev/null
@@ -1,665 +0,0 @@
-');
-
-        $this->conf = new ConfigManager('tests/utils/config/configJson');
-        $this->conf->set('resource.page_cache', $this->pagecache);
-        $this->conf->set('resource.datastore', self::$testDatastore);
-        $this->history = new History(self::$historyFilePath);
-        $this->pluginManager = new PluginManager($this->conf);
-        $this->bookmarkService = new BookmarkFileService(
-            $this->conf,
-            $this->pluginManager,
-            $this->history,
-            $mutex,
-            true
-        );
-        $this->netscapeBookmarkUtils = new NetscapeBookmarkUtils($this->bookmarkService, $this->conf, $this->history);
-    }
-
-    /**
-     * Delete history file.
-     */
-    protected function tearDown(): void
-    {
-        @unlink(self::$historyFilePath);
-    }
-
-    public static function tearDownAfterClass(): void
-    {
-        date_default_timezone_set(self::$defaultTimeZone);
-    }
-
-    /**
-     * Attempt to import bookmarks from an empty file
-     */
-    public function testImportEmptyData()
-    {
-        $files = file2array('empty.htm');
-        $this->assertEquals(
-            'File empty.htm (0 bytes) has an unknown file format.'
-            . ' Nothing was imported.',
-            $this->netscapeBookmarkUtils->import(null, $files)
-        );
-        $this->assertEquals(0, $this->bookmarkService->count());
-    }
-
-    /**
-     * Attempt to import bookmarks from a file with no Doctype
-     */
-    public function testImportNoDoctype()
-    {
-        $files = file2array('no_doctype.htm');
-        $this->assertEquals(
-            'File no_doctype.htm (350 bytes) has an unknown file format. Nothing was imported.',
-            $this->netscapeBookmarkUtils->import(null, $files)
-        );
-        $this->assertEquals(0, $this->bookmarkService->count());
-    }
-
-    /**
-     * Attempt to import bookmarks from a file with a lowercase Doctype
-     */
-    public function testImportLowecaseDoctype()
-    {
-        $files = file2array('lowercase_doctype.htm');
-        $this->assertStringMatchesFormat(
-            'File lowercase_doctype.htm (386 bytes) was successfully processed in %d seconds:'
-            . ' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import(null, $files)
-        );
-        $this->assertEquals(2, $this->bookmarkService->count());
-    }
-
-
-    /**
-     * Ensure IE dumps are supported
-     */
-    public function testImportInternetExplorerEncoding()
-    {
-        $files = file2array('internet_explorer_encoding.htm');
-        $this->assertStringMatchesFormat(
-            'File internet_explorer_encoding.htm (356 bytes) was successfully processed in %d seconds:'
-            . ' 1 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import([], $files)
-        );
-        $this->assertEquals(1, $this->bookmarkService->count());
-        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-
-        $bookmark = $this->bookmarkService->findByUrl('http://hginit.com/');
-        $this->assertEquals(0, $bookmark->getId());
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160618_203944'),
-            $bookmark->getCreated()
-        );
-        $this->assertEquals('Hg Init a Mercurial tutorial by Joel Spolsky', $bookmark->getTitle());
-        $this->assertEquals('http://hginit.com/', $bookmark->getUrl());
-        $this->assertEquals('', $bookmark->getDescription());
-        $this->assertFalse($bookmark->isPrivate());
-        $this->assertEquals('', $bookmark->getTagsString());
-        $this->assertEquals('La37cg', $bookmark->getShortUrl());
-    }
-
-    /**
-     * Import bookmarks nested in a folder hierarchy
-     */
-    public function testImportNested()
-    {
-        $files = file2array('netscape_nested.htm');
-        $this->assertStringMatchesFormat(
-            'File netscape_nested.htm (1337 bytes) was successfully processed in %d seconds:'
-            . ' 8 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import([], $files)
-        );
-        $this->assertEquals(8, $this->bookmarkService->count());
-        $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-
-        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/1');
-        $this->assertEquals(0, $bookmark->getId());
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235541'),
-            $bookmark->getCreated()
-        );
-        $this->assertEquals('Nested 1', $bookmark->getTitle());
-        $this->assertEquals('http://nest.ed/1', $bookmark->getUrl());
-        $this->assertEquals('', $bookmark->getDescription());
-        $this->assertFalse($bookmark->isPrivate());
-        $this->assertEquals('tag1 tag2', $bookmark->getTagsString());
-        $this->assertEquals('KyDNKA', $bookmark->getShortUrl());
-
-        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/1-1');
-        $this->assertEquals(1, $bookmark->getId());
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235542'),
-            $bookmark->getCreated()
-        );
-        $this->assertEquals('Nested 1-1', $bookmark->getTitle());
-        $this->assertEquals('http://nest.ed/1-1', $bookmark->getUrl());
-        $this->assertEquals('', $bookmark->getDescription());
-        $this->assertFalse($bookmark->isPrivate());
-        $this->assertEquals('folder1 tag1 tag2', $bookmark->getTagsString());
-        $this->assertEquals('T2LnXg', $bookmark->getShortUrl());
-
-        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/1-2');
-        $this->assertEquals(2, $bookmark->getId());
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235547'),
-            $bookmark->getCreated()
-        );
-        $this->assertEquals('Nested 1-2', $bookmark->getTitle());
-        $this->assertEquals('http://nest.ed/1-2', $bookmark->getUrl());
-        $this->assertEquals('', $bookmark->getDescription());
-        $this->assertFalse($bookmark->isPrivate());
-        $this->assertEquals('folder1 tag3 tag4', $bookmark->getTagsString());
-        $this->assertEquals('46SZxA', $bookmark->getShortUrl());
-
-        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/2-1');
-        $this->assertEquals(3, $bookmark->getId());
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160202_202222'),
-            $bookmark->getCreated()
-        );
-        $this->assertEquals('Nested 2-1', $bookmark->getTitle());
-        $this->assertEquals('http://nest.ed/2-1', $bookmark->getUrl());
-        $this->assertEquals('First link of the second section', $bookmark->getDescription());
-        $this->assertTrue($bookmark->isPrivate());
-        $this->assertEquals('folder2', $bookmark->getTagsString());
-        $this->assertEquals('4UHOSw', $bookmark->getShortUrl());
-
-        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/2-2');
-        $this->assertEquals(4, $bookmark->getId());
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160119_230227'),
-            $bookmark->getCreated()
-        );
-        $this->assertEquals('Nested 2-2', $bookmark->getTitle());
-        $this->assertEquals('http://nest.ed/2-2', $bookmark->getUrl());
-        $this->assertEquals('Second link of the second section', $bookmark->getDescription());
-        $this->assertTrue($bookmark->isPrivate());
-        $this->assertEquals('folder2', $bookmark->getTagsString());
-        $this->assertEquals('yfzwbw', $bookmark->getShortUrl());
-
-        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/3-1');
-        $this->assertEquals(5, $bookmark->getId());
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160202_202222'),
-            $bookmark->getCreated()
-        );
-        $this->assertEquals('Nested 3-1', $bookmark->getTitle());
-        $this->assertEquals('http://nest.ed/3-1', $bookmark->getUrl());
-        $this->assertEquals('', $bookmark->getDescription());
-        $this->assertFalse($bookmark->isPrivate());
-        $this->assertEquals('folder3 folder3-1 tag3', $bookmark->getTagsString());
-        $this->assertEquals('UwxIUQ', $bookmark->getShortUrl());
-
-        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/3-2');
-        $this->assertEquals(6, $bookmark->getId());
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160119_230227'),
-            $bookmark->getCreated()
-        );
-        $this->assertEquals('Nested 3-2', $bookmark->getTitle());
-        $this->assertEquals('http://nest.ed/3-2', $bookmark->getUrl());
-        $this->assertEquals('', $bookmark->getDescription());
-        $this->assertFalse($bookmark->isPrivate());
-        $this->assertEquals('folder3 folder3-1', $bookmark->getTagsString());
-        $this->assertEquals('p8dyZg', $bookmark->getShortUrl());
-
-        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/2');
-        $this->assertEquals(7, $bookmark->getId());
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160229_111541'),
-            $bookmark->getCreated()
-        );
-        $this->assertEquals('Nested 2', $bookmark->getTitle());
-        $this->assertEquals('http://nest.ed/2', $bookmark->getUrl());
-        $this->assertEquals('', $bookmark->getDescription());
-        $this->assertFalse($bookmark->isPrivate());
-        $this->assertEquals('tag4', $bookmark->getTagsString());
-        $this->assertEquals('Gt3Uug', $bookmark->getShortUrl());
-    }
-
-    /**
-     * Import bookmarks with the default privacy setting (reuse from file)
-     *
-     * The $_POST array is not set.
-     */
-    public function testImportDefaultPrivacyNoPost()
-    {
-        $files = file2array('netscape_basic.htm');
-        $this->assertStringMatchesFormat(
-            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            . ' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import([], $files)
-        );
-
-        $this->assertEquals(2, $this->bookmarkService->count());
-        $this->assertEquals(1, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-
-        $bookmark = $this->bookmarkService->findByUrl('https://private.tld');
-        $this->assertEquals(0, $bookmark->getId());
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20001010_135536'),
-            $bookmark->getCreated()
-        );
-        $this->assertEquals('Secret stuff', $bookmark->getTitle());
-        $this->assertEquals('https://private.tld', $bookmark->getUrl());
-        $this->assertEquals('Super-secret stuff you\'re not supposed to know about', $bookmark->getDescription());
-        $this->assertTrue($bookmark->isPrivate());
-        $this->assertEquals('private secret', $bookmark->getTagsString());
-        $this->assertEquals('EokDtA', $bookmark->getShortUrl());
-
-        $bookmark = $this->bookmarkService->findByUrl('http://public.tld');
-        $this->assertEquals(1, $bookmark->getId());
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235548'),
-            $bookmark->getCreated()
-        );
-        $this->assertEquals('Public stuff', $bookmark->getTitle());
-        $this->assertEquals('http://public.tld', $bookmark->getUrl());
-        $this->assertEquals('', $bookmark->getDescription());
-        $this->assertFalse($bookmark->isPrivate());
-        $this->assertEquals('public hello world', $bookmark->getTagsString());
-        $this->assertEquals('Er9ddA', $bookmark->getShortUrl());
-    }
-
-    /**
-     * Import bookmarks with the default privacy setting (reuse from file)
-     */
-    public function testImportKeepPrivacy()
-    {
-        $post = ['privacy' => 'default'];
-        $files = file2array('netscape_basic.htm');
-        $this->assertStringMatchesFormat(
-            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            . ' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import($post, $files)
-        );
-
-        $this->assertEquals(2, $this->bookmarkService->count());
-        $this->assertEquals(1, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-
-        $bookmark = $this->bookmarkService->findByUrl('https://private.tld');
-        $this->assertEquals(0, $bookmark->getId());
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20001010_135536'),
-            $bookmark->getCreated()
-        );
-        $this->assertEquals('Secret stuff', $bookmark->getTitle());
-        $this->assertEquals('https://private.tld', $bookmark->getUrl());
-        $this->assertEquals('Super-secret stuff you\'re not supposed to know about', $bookmark->getDescription());
-        $this->assertTrue($bookmark->isPrivate());
-        $this->assertEquals('private secret', $bookmark->getTagsString());
-        $this->assertEquals('EokDtA', $bookmark->getShortUrl());
-
-        $bookmark = $this->bookmarkService->findByUrl('http://public.tld');
-        $this->assertEquals(1, $bookmark->getId());
-        $this->assertEquals(
-            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235548'),
-            $bookmark->getCreated()
-        );
-        $this->assertEquals('Public stuff', $bookmark->getTitle());
-        $this->assertEquals('http://public.tld', $bookmark->getUrl());
-        $this->assertEquals('', $bookmark->getDescription());
-        $this->assertFalse($bookmark->isPrivate());
-        $this->assertEquals('public hello world', $bookmark->getTagsString());
-        $this->assertEquals('Er9ddA', $bookmark->getShortUrl());
-    }
-
-    /**
-     * Import bookmarks as public
-     */
-    public function testImportAsPublic()
-    {
-        $post = ['privacy' => 'public'];
-        $files = file2array('netscape_basic.htm');
-        $this->assertStringMatchesFormat(
-            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            . ' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import($post, $files)
-        );
-        $this->assertEquals(2, $this->bookmarkService->count());
-        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-        $this->assertFalse($this->bookmarkService->get(0)->isPrivate());
-        $this->assertFalse($this->bookmarkService->get(1)->isPrivate());
-    }
-
-    /**
-     * Import bookmarks as private
-     */
-    public function testImportAsPrivate()
-    {
-        $post = ['privacy' => 'private'];
-        $files = file2array('netscape_basic.htm');
-        $this->assertStringMatchesFormat(
-            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            . ' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import($post, $files)
-        );
-        $this->assertEquals(2, $this->bookmarkService->count());
-        $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-        $this->assertTrue($this->bookmarkService->get(0)->isPrivate());
-        $this->assertTrue($this->bookmarkService->get(1)->isPrivate());
-    }
-
-    /**
-     * Overwrite private bookmarks so they become public
-     */
-    public function testOverwriteAsPublic()
-    {
-        $files = file2array('netscape_basic.htm');
-
-        // import bookmarks as private
-        $post = ['privacy' => 'private'];
-        $this->assertStringMatchesFormat(
-            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            . ' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import($post, $files)
-        );
-        $this->assertEquals(2, $this->bookmarkService->count());
-        $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-        $this->assertTrue($this->bookmarkService->get(0)->isPrivate());
-        $this->assertTrue($this->bookmarkService->get(1)->isPrivate());
-
-        // re-import as public, enable overwriting
-        $post = [
-            'privacy' => 'public',
-            'overwrite' => 'true'
-        ];
-        $this->assertStringMatchesFormat(
-            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            . ' 2 bookmarks imported, 2 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import($post, $files)
-        );
-        $this->assertEquals(2, $this->bookmarkService->count());
-        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-        $this->assertFalse($this->bookmarkService->get(0)->isPrivate());
-        $this->assertFalse($this->bookmarkService->get(1)->isPrivate());
-    }
-
-    /**
-     * Overwrite public bookmarks so they become private
-     */
-    public function testOverwriteAsPrivate()
-    {
-        $files = file2array('netscape_basic.htm');
-
-        // import bookmarks as public
-        $post = ['privacy' => 'public'];
-        $this->assertStringMatchesFormat(
-            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            . ' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import($post, $files)
-        );
-        $this->assertEquals(2, $this->bookmarkService->count());
-        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-        $this->assertFalse($this->bookmarkService->get(0)->isPrivate());
-        $this->assertFalse($this->bookmarkService->get(1)->isPrivate());
-
-        // re-import as private, enable overwriting
-        $post = [
-            'privacy' => 'private',
-            'overwrite' => 'true'
-        ];
-        $this->assertStringMatchesFormat(
-            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            . ' 2 bookmarks imported, 2 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import($post, $files)
-        );
-        $this->assertEquals(2, $this->bookmarkService->count());
-        $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-        $this->assertTrue($this->bookmarkService->get(0)->isPrivate());
-        $this->assertTrue($this->bookmarkService->get(1)->isPrivate());
-    }
-
-    /**
-     * Attept to import the same bookmarks twice without enabling overwriting
-     */
-    public function testSkipOverwrite()
-    {
-        $post = ['privacy' => 'public'];
-        $files = file2array('netscape_basic.htm');
-        $this->assertStringMatchesFormat(
-            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            . ' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import($post, $files)
-        );
-        $this->assertEquals(2, $this->bookmarkService->count());
-        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-
-        // re-import as private, DO NOT enable overwriting
-        $post = ['privacy' => 'private'];
-        $this->assertStringMatchesFormat(
-            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            . ' 0 bookmarks imported, 0 bookmarks overwritten, 2 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import($post, $files)
-        );
-        $this->assertEquals(2, $this->bookmarkService->count());
-        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-    }
-
-    /**
-     * Add user-specified tags to all imported bookmarks
-     */
-    public function testSetDefaultTags()
-    {
-        $post = [
-            'privacy' => 'public',
-            'default_tags' => 'tag1 tag2 tag3'
-        ];
-        $files = file2array('netscape_basic.htm');
-        $this->assertStringMatchesFormat(
-            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            . ' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import($post, $files)
-        );
-        $this->assertEquals(2, $this->bookmarkService->count());
-        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-        $this->assertEquals('tag1 tag2 tag3 private secret', $this->bookmarkService->get(0)->getTagsString());
-        $this->assertEquals('tag1 tag2 tag3 public hello world', $this->bookmarkService->get(1)->getTagsString());
-    }
-
-    /**
-     * The user-specified tags contain characters to be escaped
-     */
-    public function testSanitizeDefaultTags()
-    {
-        $post = [
-            'privacy' => 'public',
-            'default_tags' => 'tag1& tag2 "tag3"'
-        ];
-        $files = file2array('netscape_basic.htm');
-        $this->assertStringMatchesFormat(
-            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            . ' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import($post, $files)
-        );
-        $this->assertEquals(2, $this->bookmarkService->count());
-        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-        $this->assertEquals(
-            'tag1& tag2 "tag3" private secret',
-            $this->bookmarkService->get(0)->getTagsString()
-        );
-        $this->assertEquals(
-            'tag1& tag2 "tag3" public hello world',
-            $this->bookmarkService->get(1)->getTagsString()
-        );
-    }
-
-    /**
-     * Add user-specified tags to all imported bookmarks
-     */
-    public function testSetDefaultTagsWithCustomSeparator()
-    {
-        $separator = '@';
-        $this->conf->set('general.tags_separator', $separator);
-        $post = [
-            'privacy' => 'public',
-            'default_tags' => 'tag1@tag2@tag3@multiple words tag'
-        ];
-        $files = file2array('netscape_basic.htm');
-        $this->assertStringMatchesFormat(
-            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            . ' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import($post, $files)
-        );
-        $this->assertEquals(2, $this->bookmarkService->count());
-        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-        $this->assertEquals(
-            'tag1@tag2@tag3@multiple words tag@private@secret',
-            $this->bookmarkService->get(0)->getTagsString($separator)
-        );
-        $this->assertEquals(
-            ['tag1', 'tag2', 'tag3', 'multiple words tag', 'private', 'secret'],
-            $this->bookmarkService->get(0)->getTags()
-        );
-        $this->assertEquals(
-            'tag1@tag2@tag3@multiple words tag@public@hello@world',
-            $this->bookmarkService->get(1)->getTagsString($separator)
-        );
-        $this->assertEquals(
-            ['tag1', 'tag2', 'tag3', 'multiple words tag', 'public', 'hello', 'world'],
-            $this->bookmarkService->get(1)->getTags()
-        );
-    }
-
-    /**
-     * Ensure each imported bookmark has a unique id
-     *
-     * See https://github.com/shaarli/Shaarli/issues/351
-     */
-    public function testImportSameDate()
-    {
-        $files = file2array('same_date.htm');
-        $this->assertStringMatchesFormat(
-            'File same_date.htm (453 bytes) was successfully processed in %d seconds:'
-            . ' 3 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
-            $this->netscapeBookmarkUtils->import([], $files)
-        );
-        $this->assertEquals(3, $this->bookmarkService->count());
-        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
-        $this->assertEquals(0, $this->bookmarkService->get(0)->getId());
-        $this->assertEquals(1, $this->bookmarkService->get(1)->getId());
-        $this->assertEquals(2, $this->bookmarkService->get(2)->getId());
-    }
-
-    public function testImportCreateUpdateHistory()
-    {
-        $post = [
-            'privacy' => 'public',
-            'overwrite' => 'true',
-        ];
-        $files = file2array('netscape_basic.htm');
-        $this->netscapeBookmarkUtils->import($post, $files);
-        $history = $this->history->getHistory();
-        $this->assertEquals(1, count($history));
-        $this->assertEquals(History::IMPORT, $history[0]['event']);
-        $this->assertTrue(new DateTime('-5 seconds') < $history[0]['datetime']);
-
-        // re-import as private, enable overwriting
-        $this->netscapeBookmarkUtils->import($post, $files);
-        $history = $this->history->getHistory();
-        $this->assertEquals(2, count($history));
-        $this->assertEquals(History::IMPORT, $history[0]['event']);
-        $this->assertTrue(new DateTime('-5 seconds') < $history[0]['datetime']);
-        $this->assertEquals(History::IMPORT, $history[1]['event']);
-        $this->assertTrue(new DateTime('-5 seconds') < $history[1]['datetime']);
-    }
-}
diff --git a/tests/netscape/input/empty.htm b/tests/netscape/input/empty.htm
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/netscape/input/internet_explorer_encoding.htm b/tests/netscape/input/internet_explorer_encoding.htm
deleted file mode 100644
index 18703cf6..00000000
--- a/tests/netscape/input/internet_explorer_encoding.htm
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-Bookmarks
-

Bookmarks

-

-

Hg Init a Mercurial tutorial by Joel Spolsky -

diff --git a/tests/netscape/input/lowercase_doctype.htm b/tests/netscape/input/lowercase_doctype.htm deleted file mode 100644 index 8911ad19..00000000 --- a/tests/netscape/input/lowercase_doctype.htm +++ /dev/null @@ -1,8 +0,0 @@ - -Bookmarks -

Bookmarks

-

-

Secret stuff -
Super-secret stuff you're not supposed to know about -
Public stuff -

diff --git a/tests/netscape/input/netscape_basic.htm b/tests/netscape/input/netscape_basic.htm deleted file mode 100644 index affe0cf8..00000000 --- a/tests/netscape/input/netscape_basic.htm +++ /dev/null @@ -1,11 +0,0 @@ - - -Bookmarks -

Bookmarks

-

-

Secret stuff -
Super-secret stuff you're not supposed to know about -
Public stuff -

diff --git a/tests/netscape/input/netscape_nested.htm b/tests/netscape/input/netscape_nested.htm deleted file mode 100644 index b486fe18..00000000 --- a/tests/netscape/input/netscape_nested.htm +++ /dev/null @@ -1,31 +0,0 @@ - - -Bookmarks -

Bookmarks

-

-

Nested 1 -

Folder1

-

-

Nested 1-1 -
Nested 1-2 -

-

Folder2

-
This second folder contains wonderful links! -

-

Nested 2-1 -
First link of the second section -
Nested 2-2 -
Second link of the second section -

-

Folder3

-

-

Folder3-1

-

-

Nested 3-1 -
Nested 3-2 -

-

-

Nested 2 -

diff --git a/tests/netscape/input/no_doctype.htm b/tests/netscape/input/no_doctype.htm deleted file mode 100644 index 766d398b..00000000 --- a/tests/netscape/input/no_doctype.htm +++ /dev/null @@ -1,7 +0,0 @@ -Bookmarks -

Bookmarks

-

-

Secret stuff -
Super-secret stuff you're not supposed to know about -
Public stuff -

diff --git a/tests/netscape/input/same_date.htm b/tests/netscape/input/same_date.htm deleted file mode 100644 index 9d58a582..00000000 --- a/tests/netscape/input/same_date.htm +++ /dev/null @@ -1,11 +0,0 @@ - - -Bookmarks -

Bookmarks

-

-

Today's first link -
Today's second link -
Today's third link -

diff --git a/tests/plugins/PluginAddlinkTest.php b/tests/plugins/PluginAddlinkTest.php deleted file mode 100644 index 3ae72d99..00000000 --- a/tests/plugins/PluginAddlinkTest.php +++ /dev/null @@ -1,63 +0,0 @@ - $str]; - $data['_PAGE_'] = TemplatePage::LINKLIST; - $data['_LOGGEDIN_'] = true; - $data['_BASE_PATH_'] = '/subfolder'; - - $data = hook_addlink_toolbar_render_header($data); - $this->assertEquals($str, $data[$str]); - $this->assertEquals(1, count($data['fields_toolbar'])); - - $data = [$str => $str]; - $data['_PAGE_'] = $str; - $data['_LOGGEDIN_'] = true; - $data['_BASE_PATH_'] = '/subfolder'; - - $data = hook_addlink_toolbar_render_header($data); - $this->assertEquals($str, $data[$str]); - $this->assertArrayNotHasKey('fields_toolbar', $data); - } - - /** - * Test render_header hook while logged out. - */ - public function testAddlinkHeaderLoggedOut() - { - $str = 'stuff'; - $data = [$str => $str]; - $data['_PAGE_'] = TemplatePage::LINKLIST; - $data['_LOGGEDIN_'] = false; - $data['_BASE_PATH_'] = '/subfolder'; - - $data = hook_addlink_toolbar_render_header($data); - $this->assertEquals($str, $data[$str]); - $this->assertArrayNotHasKey('fields_toolbar', $data); - } -} diff --git a/tests/plugins/PluginArchiveorgTest.php b/tests/plugins/PluginArchiveorgTest.php deleted file mode 100644 index 909be088..00000000 --- a/tests/plugins/PluginArchiveorgTest.php +++ /dev/null @@ -1,154 +0,0 @@ -savedScriptName = $_SERVER['SCRIPT_NAME'] ?? null; - $_SERVER['SCRIPT_NAME'] = '/index.php'; - } - - public function tearDown(): void - { - unset($_SERVER['SERVER_PORT']); - unset($_SERVER['SERVER_NAME']); - $_SERVER['SCRIPT_NAME'] = $this->savedScriptName; - } - - /** - * Test render_linklist hook on external bookmarks. - */ - public function testArchiveorgLinklistOnExternalLinks(): void - { - $str = 'http://randomstr.com/test'; - - $data = [ - 'title' => $str, - 'links' => [ - [ - 'url' => $str, - 'private' => 0, - 'real_url' => $str - ] - ] - ]; - - $data = hook_archiveorg_render_linklist($data); - - $link = $data['links'][0]; - // data shouldn't be altered - $this->assertEquals($str, $data['title']); - $this->assertEquals($str, $link['url']); - - // plugin data - $this->assertEquals(1, count($link['link_plugin'])); - $this->assertNotFalse(strpos($link['link_plugin'][0], $str)); - } - - /** - * Test render_linklist hook on internal bookmarks. - */ - public function testArchiveorgLinklistOnInternalLinks(): void - { - $internalLink1 = 'http://shaarli.shaarli/shaare/qvMAqg'; - $internalLinkRealURL1 = '/shaare/qvMAqg'; - - $internalLink2 = 'http://shaarli.shaarli/shaare/2_7zww'; - $internalLinkRealURL2 = '/shaare/2_7zww'; - - $internalLink3 = 'http://shaarli.shaarli/shaare/z7u-_Q'; - $internalLinkRealURL3 = '/shaare/z7u-_Q'; - - $data = [ - 'title' => $internalLink1, - 'links' => [ - [ - 'url' => $internalLink1, - 'private' => 0, - 'real_url' => $internalLinkRealURL1 - ], - [ - 'url' => $internalLink1, - 'private' => 1, - 'real_url' => $internalLinkRealURL1 - ], - [ - 'url' => $internalLink2, - 'private' => 0, - 'real_url' => $internalLinkRealURL2 - ], - [ - 'url' => $internalLink2, - 'private' => 1, - 'real_url' => $internalLinkRealURL2 - ], - [ - 'url' => $internalLink3, - 'private' => 0, - 'real_url' => $internalLinkRealURL3 - ], - [ - 'url' => $internalLink3, - 'private' => 1, - 'real_url' => $internalLinkRealURL3 - ] - ] - ]; - - $data = hook_archiveorg_render_linklist($data); - - // Case n°1: first link type, public - $link = $data['links'][0]; - - $this->assertEquals(1, count($link['link_plugin'])); - $this->assertNotFalse(strpos($link['link_plugin'][0], $internalLink1)); - - // Case n°2: first link type, private - $link = $data['links'][1]; - - $this->assertArrayNotHasKey('link_plugin', $link); - - // Case n°3: second link type, public - $link = $data['links'][2]; - - $this->assertEquals(1, count($link['link_plugin'])); - $this->assertNotFalse(strpos($link['link_plugin'][0], $internalLink2)); - - // Case n°4: second link type, private - $link = $data['links'][3]; - - $this->assertArrayNotHasKey('link_plugin', $link); - - // Case n°5: third link type, public - $link = $data['links'][4]; - - $this->assertEquals(1, count($link['link_plugin'])); - $this->assertNotFalse(strpos($link['link_plugin'][0], $internalLink3)); - - // Case n°6: third link type, private - $link = $data['links'][5]; - - $this->assertArrayNotHasKey('link_plugin', $link); - } -} diff --git a/tests/plugins/PluginDefaultColorsTest.php b/tests/plugins/PluginDefaultColorsTest.php deleted file mode 100644 index 54e97612..00000000 --- a/tests/plugins/PluginDefaultColorsTest.php +++ /dev/null @@ -1,219 +0,0 @@ -set('plugins.DEFAULT_COLORS_BACKGROUND', 'value'); - $errors = default_colors_init($conf); - $this->assertEmpty($errors); - - $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css'); - } - - /** - * Test DefaultColors init with errors. - */ - public function testDefaultColorsInitError() - { - $conf = new ConfigManager(''); - $errors = default_colors_init($conf); - $this->assertNotEmpty($errors); - } - - /** - * Test the save plugin parameters hook with all colors specified. - */ - public function testGenerateCssFile() - { - $params = [ - 'other1' => true, - 'DEFAULT_COLORS_MAIN' => 'blue', - 'DEFAULT_COLORS_BACKGROUND' => 'pink', - 'other2' => ['yep'], - 'DEFAULT_COLORS_DARK_MAIN' => 'green', - ]; - - default_colors_generate_css_file($params); - $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css'); - $content = file_get_contents($file); - $expected = ':root { - --main-color: blue; - --background-color: pink; - --dark-main-color: green; - -} -'; - $this->assertEquals($expected, $content); - } - - /** - * Test the save plugin parameters hook with only one color specified. - */ - public function testGenerateCssFileSingle() - { - $params = [ - 'other1' => true, - 'DEFAULT_COLORS_BACKGROUND' => 'pink', - 'other2' => ['yep'], - 'DEFAULT_COLORS_DARK_MAIN' => '', - ]; - - default_colors_generate_css_file($params); - $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css'); - $content = file_get_contents($file); - $expected = ':root { - --background-color: pink; - -} -'; - $this->assertEquals($expected, $content); - } - - /** - * Test the save plugin parameters hook with no color specified. - */ - public function testGenerateCssFileNone() - { - default_colors_generate_css_file([]); - $this->assertFileNotExists($file = 'sandbox/default_colors/default_colors.css'); - } - - /** - * Make sure that the CSS is properly included by the include hook. - */ - public function testIncludeWithFile() - { - $data = [ - 'css_files' => ['file1'], - 'js_files' => ['file2'], - ]; - touch($file = 'sandbox/default_colors/default_colors.css'); - $processedData = hook_default_colors_render_includes($data); - - $this->assertCount(2, $processedData['css_files']); - $this->assertEquals($file, $processedData['css_files'][1]); - $this->assertCount(1, $processedData['js_files']); - } - - /** - * Make sure that the CSS is not included by the include hook if the CSS file does not exist. - */ - public function testIncludeWithoutFile() - { - $data = [ - 'css_files' => ['file1'], - 'js_files' => ['file2'], - ]; - $processedData = hook_default_colors_render_includes($data); - - $this->assertEquals($data, $processedData); - } - - /** - * Test helper function which generates CSS rules with valid input. - */ - public function testFormatCssRuleValid() - { - $data = [ - 'other1' => true, - 'DEFAULT_COLORS_BLIP_BLOP' => 'shinyColor', - 'other2' => ['yep'], - ]; - $result = default_colors_format_css_rule($data, 'DEFAULT_COLORS_BLIP_BLOP'); - $this->assertEquals(' --blip-blop-color: shinyColor', $result); - - $data = ['unknown-parameter' => true]; - $result = default_colors_format_css_rule($data, 'unknown-parameter'); - $this->assertEquals(' --unknown-parameter-color: 1', $result); - } - - /** - * Test helper function which generates CSS rules with invalid input. - */ - public function testFormatCssRuleInvalid() - { - $result = default_colors_format_css_rule([], 'DEFAULT_COLORS_BLIP_BLOP'); - $this->assertEmpty($result); - - $data = [ - 'other1' => true, - 'DEFAULT_COLORS_BLIP_BLOP' => 'shinyColor', - 'other2' => ['yep'], - ]; - $result = default_colors_format_css_rule($data, ''); - $this->assertEmpty($result); - } - - /** - * Make sure that a new CSS file is generated when save_plugin_parameters hook is triggered. - */ - public function testHookSavePluginParameters(): void - { - $params = [ - 'other1' => true, - 'DEFAULT_COLORS_BACKGROUND' => 'pink', - 'other2' => ['yep'], - 'DEFAULT_COLORS_DARK_MAIN' => '', - ]; - - hook_default_colors_save_plugin_parameters($params); - $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css'); - $content = file_get_contents($file); - $expected = ':root { - --background-color: pink; - -} -'; - $this->assertEquals($expected, $content); - } -} diff --git a/tests/plugins/PluginIssoTest.php b/tests/plugins/PluginIssoTest.php deleted file mode 100644 index 119a459e..00000000 --- a/tests/plugins/PluginIssoTest.php +++ /dev/null @@ -1,172 +0,0 @@ -set('plugins.ISSO_SERVER', 'value'); - $errors = isso_init($conf); - $this->assertEmpty($errors); - } - - /** - * Test Isso init with errors. - */ - public function testIssoInitError(): void - { - $conf = new ConfigManager(''); - $errors = isso_init($conf); - $this->assertNotEmpty($errors); - } - - /** - * Test render_linklist hook with valid settings to display the comment form. - */ - public function testIssoDisplayed(): void - { - $conf = new ConfigManager(''); - $conf->set('plugins.ISSO_SERVER', 'value'); - - $str = 'http://randomstr.com/test'; - $date = '20161118_100001'; - $data = [ - 'title' => $str, - 'links' => [ - [ - 'id' => 12, - 'url' => $str, - 'created' => DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, $date), - ] - ] - ]; - - $data = hook_isso_render_linklist($data, $conf); - - // data shouldn't be altered - $this->assertEquals($str, $data['title']); - $this->assertEquals($str, $data['links'][0]['url']); - - // plugin data - $this->assertEquals(1, count($data['plugin_end_zone'])); - $this->assertNotFalse(strpos( - $data['plugin_end_zone'][0], - 'data-isso-id="' . $data['links'][0]['id'] . '"' - )); - $this->assertNotFalse(strpos( - $data['plugin_end_zone'][0], - 'data-title="' . $data['links'][0]['id'] . '"' - )); - $this->assertNotFalse(strpos($data['plugin_end_zone'][0], 'embed.min.js')); - } - - /** - * Test isso plugin when multiple bookmarks are displayed (shouldn't be displayed). - */ - public function testIssoMultipleLinks(): void - { - $conf = new ConfigManager(''); - $conf->set('plugins.ISSO_SERVER', 'value'); - - $str = 'http://randomstr.com/test'; - $date1 = '20161118_100001'; - $date2 = '20161118_100002'; - $data = [ - 'title' => $str, - 'links' => [ - [ - 'id' => 12, - 'url' => $str, - 'shorturl' => $short1 = 'abcd', - 'created' => DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, $date1), - ], - [ - 'id' => 13, - 'url' => $str . '2', - 'shorturl' => $short2 = 'efgh', - 'created' => DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, $date2), - ], - ] - ]; - - $processed = hook_isso_render_linklist($data, $conf); - // link_plugin should be added for the icon - $this->assertContainsPolyfill( - '', - $processed['links'][0]['link_plugin'][0] - ); - $this->assertContainsPolyfill( - '', - $processed['links'][1]['link_plugin'][0] - ); - } - - /** - * Test isso plugin when using search (shouldn't be displayed). - */ - public function testIssoNotDisplayedWhenSearch(): void - { - $conf = new ConfigManager(''); - $conf->set('plugins.ISSO_SERVER', 'value'); - - $str = 'http://randomstr.com/test'; - $date = '20161118_100001'; - $data = [ - 'title' => $str, - 'links' => [ - [ - 'id' => 12, - 'url' => $str, - 'shorturl' => $short1 = 'abcd', - 'created' => DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, $date), - ] - ], - 'search_term' => $str - ]; - - $processed = hook_isso_render_linklist($data, $conf); - - // link_plugin should be added for the icon - $this->assertContainsPolyfill( - '', - $processed['links'][0]['link_plugin'][0] - ); - } - - /** - * Test isso plugin without server configuration (shouldn't be displayed). - */ - public function testIssoWithoutConf(): void - { - $data = 'abc'; - $conf = new ConfigManager(''); - $processed = hook_isso_render_linklist($data, $conf); - $this->assertEquals($data, $processed); - } -} diff --git a/tests/plugins/PluginPlayvideosTest.php b/tests/plugins/PluginPlayvideosTest.php deleted file mode 100644 index 0f267462..00000000 --- a/tests/plugins/PluginPlayvideosTest.php +++ /dev/null @@ -1,61 +0,0 @@ - $str]; - $data['_PAGE_'] = TemplatePage::LINKLIST; - - $data = hook_playvideos_render_header($data); - $this->assertEquals($str, $data[$str]); - $this->assertEquals(1, count($data['buttons_toolbar'])); - - $data = [$str => $str]; - $data['_PAGE_'] = $str; - $this->assertEquals($str, $data[$str]); - $this->assertArrayNotHasKey('buttons_toolbar', $data); - } - - /** - * Test render_footer hook. - */ - public function testPlayvideosFooter() - { - $str = 'stuff'; - $data = [$str => $str]; - $data['_PAGE_'] = TemplatePage::LINKLIST; - - $data = hook_playvideos_render_footer($data); - $this->assertEquals($str, $data[$str]); - $this->assertEquals(2, count($data['js_files'])); - - $data = [$str => $str]; - $data['_PAGE_'] = $str; - $this->assertEquals($str, $data[$str]); - $this->assertArrayNotHasKey('js_files', $data); - } -} diff --git a/tests/plugins/PluginPubsubhubbubTest.php b/tests/plugins/PluginPubsubhubbubTest.php deleted file mode 100644 index 9fa944c1..00000000 --- a/tests/plugins/PluginPubsubhubbubTest.php +++ /dev/null @@ -1,59 +0,0 @@ -set('plugins.PUBSUBHUB_URL', $hub); - $data['_PAGE_'] = TemplatePage::FEED_RSS; - - $data = hook_pubsubhubbub_render_feed($data, $conf); - $expected = ''; - $this->assertEquals($expected, $data['feed_plugins_header'][0]); - } - - /** - * Test render_feed hook with an ATOM feed. - */ - public function testPubSubAtomRenderFeed() - { - $hub = 'http://domain.hub'; - $conf = new ConfigManager(self::$configFile); - $conf->set('plugins.PUBSUBHUB_URL', $hub); - $data['_PAGE_'] = TemplatePage::FEED_ATOM; - - $data = hook_pubsubhubbub_render_feed($data, $conf); - $expected = ''; - $this->assertEquals($expected, $data['feed_plugins_header'][0]); - } -} diff --git a/tests/plugins/PluginQrcodeTest.php b/tests/plugins/PluginQrcodeTest.php deleted file mode 100644 index ff4b9182..00000000 --- a/tests/plugins/PluginQrcodeTest.php +++ /dev/null @@ -1,68 +0,0 @@ - $str, - 'links' => [ - [ - 'url' => $str, - ] - ] - ]; - - $data = hook_qrcode_render_linklist($data); - $link = $data['links'][0]; - // data shouldn't be altered - $this->assertEquals($str, $data['title']); - $this->assertEquals($str, $link['url']); - - // plugin data - $this->assertEquals(1, count($link['link_plugin'])); - $this->assertNotFalse(strpos($link['link_plugin'][0], $str)); - } - - /** - * Test render_footer hook. - */ - public function testQrcodeFooter() - { - $str = 'stuff'; - $data = [$str => $str]; - $data['_PAGE_'] = TemplatePage::LINKLIST; - - $data = hook_qrcode_render_footer($data); - $this->assertEquals($str, $data[$str]); - $this->assertEquals(1, count($data['js_files'])); - - $data = [$str => $str]; - $data['_PAGE_'] = $str; - $this->assertEquals($str, $data[$str]); - $this->assertArrayNotHasKey('js_files', $data); - } -} diff --git a/tests/plugins/PluginReadItLaterTest.php b/tests/plugins/PluginReadItLaterTest.php deleted file mode 100644 index 32225628..00000000 --- a/tests/plugins/PluginReadItLaterTest.php +++ /dev/null @@ -1,419 +0,0 @@ -confDefaultTheme = $this->createMock(ConfigManager::class); - $this->confDefaultTheme->method('get')->willReturnCallback(function (string $parameter, $default) { - if ($parameter === 'resource.theme') { - return 'default'; - } - - return $default; - }); - - $this->confOtherTheme = $this->createMock(ConfigManager::class); - $this->confDefaultTheme->method('get')->willReturnCallback(function (string $parameter, $default) { - if ($parameter === 'resource.theme') { - return 'other'; - } - - return $default; - }); - } - - /** - * Test hook_readitlater_render_linklist while logged in. - */ - public function testReadItLaterLinklistLoggedInDefaultTheme(): void - { - $url = 'http://randomstr.com/test'; - $data = [ - '_LOGGEDIN_' => true, - 'links' => [ - [ - 'id' => 1, - 'url' => $url . '1', - ], - [ - 'id' => 2, - 'url' => $url . '2', - 'additional_content' => [ - 'readitlater' => false, - ], - ], - [ - 'id' => 3, - 'url' => $url . '3', - 'additional_content' => [ - 'readitlater' => true, - ], - ], - ], - ]; - - $data = hook_readitlater_render_linklist($data, $this->confDefaultTheme); - - $link = $data['links'][0]; - static::assertEquals($url . '1', $link['url']); - static::assertNotEmpty($link['link_plugin']); - static::assertContainsPolyfill('Read it later', $link['link_plugin'][0]); - - $link = $data['links'][1]; - static::assertEquals($url . '2', $link['url']); - static::assertNotEmpty($link['link_plugin']); - static::assertContainsPolyfill('Read it later', $link['link_plugin'][0]); - - $link = $data['links'][2]; - static::assertEquals($url . '3', $link['url']); - static::assertNotEmpty($link['link_plugin']); - static::assertContainsPolyfill('Mark as Read', $link['link_plugin'][0]); - - static::assertNotEmpty($data['action_plugin']); - static::assertContainsPolyfill('readitlater/toggle-filter', $data['action_plugin'][0]['attr']['href']); - } - - /** - * Test hook_readitlater_render_linklist while logged in. - */ - public function testReadItLaterLinklistLoggedInOtherTheme(): void - { - $url = 'http://randomstr.com/test'; - $data = [ - '_LOGGEDIN_' => true, - 'links' => [ - [ - 'id' => 1, - 'url' => $url . '1', - ], - [ - 'id' => 2, - 'url' => $url . '2', - 'additional_content' => [ - 'readitlater' => false, - ], - ], - [ - 'id' => 3, - 'url' => $url . '3', - 'additional_content' => [ - 'readitlater' => true, - ], - ], - ], - ]; - - $data = hook_readitlater_render_linklist($data, $this->confOtherTheme); - - $link = $data['links'][0]; - static::assertEquals($url . '1', $link['url']); - static::assertNotEmpty($link['link_plugin']); - static::assertContainsPolyfill('Read it later', $link['link_plugin'][0]); - - $link = $data['links'][1]; - static::assertEquals($url . '2', $link['url']); - static::assertNotEmpty($link['link_plugin']); - static::assertContainsPolyfill('Read it later', $link['link_plugin'][0]); - - $link = $data['links'][2]; - static::assertEquals($url . '3', $link['url']); - static::assertNotEmpty($link['link_plugin']); - static::assertContainsPolyfill('Mark as Read', $link['link_plugin'][0]); - - static::assertNotEmpty($data['action_plugin']); - static::assertContainsPolyfill('readitlater/toggle-filter', $data['action_plugin'][0]['attr']['href']); - } - - /** - * Test hook_readitlater_render_linklist while logged out: nothing should happen. - */ - public function testReadItLaterLinklistLoggedOut(): void - { - $url = 'http://randomstr.com/test'; - $originalData = [ - '_LOGGEDIN_' => false, - 'links' => [ - [ - 'id' => 1, - 'url' => $url . '1', - ], - [ - 'id' => 2, - 'url' => $url . '2', - 'additional_content' => [ - 'readitlater' => false, - ], - ], - [ - 'id' => 3, - 'url' => $url . '3', - 'additional_content' => [ - 'readitlater' => true, - ], - ], - ], - ]; - - $data = hook_readitlater_render_linklist($originalData, $this->confDefaultTheme); - - static::assertSame($originalData, $data); - - unset($originalData['_LOGGEDIN_']); - - $data = hook_readitlater_render_linklist($originalData, $this->confDefaultTheme); - - static::assertSame($originalData, $data); - } - - /** - * Test readitlater_register_routes - */ - public function testReadItLaterRoutesRegister(): void - { - $routes = readitlater_register_routes(); - - static::assertCount(2, $routes); - foreach ($routes as $route) { - static::assertSame('GET', $route['method']); - static::assertContainsPolyfill('ReadItLaterController', $route['callable']); - } - } - - /** - * Test hook_readitlater_render_includes while logged in - */ - public function testReadItLaterRenderIncludesLoggedInDefaultTheme(): void - { - $data = hook_readitlater_render_includes(['_LOGGEDIN_' => true], $this->confDefaultTheme); - - static::assertSame('plugins/readitlater/readitlater.default.css', $data['css_files'][0]); - } - - /** - * Test hook_readitlater_render_includes while logged in - */ - public function testReadItLaterRenderIncludesLoggedInOtherTheme(): void - { - $data = hook_readitlater_render_includes($originalData = ['_LOGGEDIN_' => true], $this->confOtherTheme); - - static::assertSame($originalData, $data); - } - - /** - * Test hook_readitlater_render_includes while logged out - */ - public function testReadItLaterRenderIncludesLoggedOut(): void - { - $data = hook_readitlater_render_includes([], $this->confDefaultTheme); - - static::assertSame([], $data); - - $data = hook_readitlater_render_includes($originalData = ['_LOGGEDIN_' => false], $this->confDefaultTheme); - - static::assertSame($originalData, $data); - } - - /** - * Test hook_readitlater_render_footer while logged in - */ - public function testReadItLaterRenderFooterLoggedInDefaultTheme(): void - { - $data = hook_readitlater_render_footer(['_LOGGEDIN_' => true], $this->confDefaultTheme); - - static::assertSame('plugins/readitlater/readitlater.default.js', $data['js_files'][0]); - } - - /** - * Test hook_readitlater_render_footer while logged in - */ - public function testReadItLaterRenderFooterLoggedInOtherTheme(): void - { - $data = hook_readitlater_render_footer($originalData = ['_LOGGEDIN_' => true], $this->confOtherTheme); - - static::assertSame($originalData, $data); - } - - /** - * Test hook_readitlater_render_footer while logged out - */ - public function testReadItLaterRenderFooterLoggedOut(): void - { - $data = hook_readitlater_render_footer([], $this->confDefaultTheme); - - static::assertSame([], $data); - } - - /** - * Test hook_readitlater_render_editlink with a new link: checkbox added (unchecked) - */ - public function testReadItLaterRenderEditLinkDefaultOff(): void - { - $originalData = [ - 'link_is_new' => true, - 'link' => [], - ]; - $data = hook_readitlater_render_editlink($originalData, $this->confDefaultTheme); - - static::assertRegExp( - '##', - $data['edit_link_plugin'][0] - ); - - $this->confDefaultTheme = $this->createMock(ConfigManager::class); - $this->confDefaultTheme->method('get')->with('plugins.READITLATER_DEFAULT_CHECK')->willReturn('0'); - - $data = hook_readitlater_render_editlink($originalData, $this->confDefaultTheme); - - static::assertRegExp( - '##', - $data['edit_link_plugin'][0] - ); - } - - /** - * Test hook_readitlater_render_editlink with a new link: checkbox added (checked) - */ - public function testReadItLaterRenderEditLinkDefaultOn(): void - { - $this->confDefaultTheme = $this->createMock(ConfigManager::class); - $this->confDefaultTheme->method('get')->with('plugins.READITLATER_DEFAULT_CHECK')->willReturn('1'); - $originalData = [ - 'link_is_new' => true, - 'link' => [], - ]; - $data = hook_readitlater_render_editlink($originalData, $this->confDefaultTheme); - - static::assertRegExp( - '##', - $data['edit_link_plugin'][0] - ); - } - - /** - * Test hook_readitlater_render_editlink with an existing link: we don't do anything - */ - public function testReadItLaterRenderEditLinkNotNew(): void - { - $originalData = [ - 'link_is_new' => false, - 'link' => [], - ]; - $data = hook_readitlater_render_editlink($originalData, $this->confDefaultTheme); - - static::assertSame($originalData, $data); - } - - /** - * Test hook_readitlater_save_link with readitlater not already set and multiple values (defaults to false). - */ - public function testReadItLaterSaveLinkNewSetting(): void - { - $_POST['readitlater'] = true; - $data = hook_readitlater_save_link([]); - - static::assertTrue($data['additional_content']['readitlater']); - - $_POST['readitlater'] = 'on'; - $data = hook_readitlater_save_link([]); - - static::assertTrue($data['additional_content']['readitlater']); - - $_POST['readitlater'] = false; - $data = hook_readitlater_save_link([]); - - static::assertFalse($data['additional_content']['readitlater']); - - unset($_POST['readitlater']); - $data = hook_readitlater_save_link([]); - - static::assertFalse($data['additional_content']['readitlater']); - } - - /** - * Test hook_readitlater_save_link with readitlater setting already set. - */ - public function testReadItLaterSaveLinkExistingSetting(): void - { - $data = hook_readitlater_save_link(['additional_content' => ['readitlater' => true]]); - static::assertTrue($data['additional_content']['readitlater']); - - $data = hook_readitlater_save_link(['additional_content' => ['readitlater' => false]]); - static::assertFalse($data['additional_content']['readitlater']); - } - - /** - * Test hook_readitlater_filter_search_entry - */ - public function testReadItLaterFilterSearchEntry(): void - { - $_SESSION['readitlater-only'] = true; - - $bookmark = new Bookmark(); - static::assertFalse(hook_readitlater_filter_search_entry($bookmark, [])); - - $bookmark = new Bookmark(); - $bookmark->setAdditionalContentEntry('readitlater', false); - static::assertFalse(hook_readitlater_filter_search_entry($bookmark, [])); - - $bookmark = new Bookmark(); - $bookmark->setAdditionalContentEntry('readitlater', true); - static::assertTrue(hook_readitlater_filter_search_entry($bookmark, [])); - - $_SESSION['readitlater-only'] = false; - - $bookmark = new Bookmark(); - static::assertTrue(hook_readitlater_filter_search_entry($bookmark, [])); - - $bookmark = new Bookmark(); - $bookmark->setAdditionalContentEntry('readitlater', false); - static::assertTrue(hook_readitlater_filter_search_entry($bookmark, [])); - - $bookmark = new Bookmark(); - $bookmark->setAdditionalContentEntry('readitlater', true); - static::assertTrue(hook_readitlater_filter_search_entry($bookmark, [])); - - unset($_SESSION['readitlater-only']); - } - - public function testReadItLaterGetIconDefaultTheme(): void - { - $result = readitlater_get_icon($this->confDefaultTheme, true); - static::assertSame('', $result); - - $result = readitlater_get_icon($this->confDefaultTheme, false); - static::assertSame('', $result); - } - - public function testReadItLaterGetIconOtherTheme(): void - { - $result = readitlater_get_icon($this->confOtherTheme, true); - static::assertSame('Mark as Read', $result); - - $result = readitlater_get_icon($this->confOtherTheme, false); - static::assertSame('Read it later', $result); - } -} diff --git a/tests/plugins/PluginWallabagTest.php b/tests/plugins/PluginWallabagTest.php deleted file mode 100644 index 0be8d59a..00000000 --- a/tests/plugins/PluginWallabagTest.php +++ /dev/null @@ -1,96 +0,0 @@ -set('plugins.WALLABAG_URL', 'value'); - $errors = wallabag_init($conf); - $this->assertEmpty($errors); - } - - /** - * Test wallabag init with errors. - */ - public function testWallabagInitError() - { - $conf = new ConfigManager(''); - $errors = wallabag_init($conf); - $this->assertNotEmpty($errors); - } - - /** - * Test render_linklist hook. - */ - public function testWallabagLinklist() - { - $conf = new ConfigManager(''); - $conf->set('plugins.WALLABAG_URL', 'value'); - $str = 'http://randomstr.com/test'; - $data = [ - 'title' => $str, - 'links' => [ - [ - 'url' => $str, - ] - ], - '_LOGGEDIN_' => true, - ]; - - $data = hook_wallabag_render_linklist($data, $conf); - $link = $data['links'][0]; - // data shouldn't be altered - $this->assertEquals($str, $data['title']); - $this->assertEquals($str, $link['url']); - - // plugin data - $this->assertEquals(1, count($link['link_plugin'])); - $this->assertNotFalse(strpos($link['link_plugin'][0], urlencode($str))); - $this->assertNotFalse(strpos($link['link_plugin'][0], $conf->get('plugins.WALLABAG_URL'))); - } - - /** - * Test render_linklist hook while logged out: no change. - */ - public function testWallabagLinklistLoggedOut(): void - { - $conf = new ConfigManager(''); - $str = 'http://randomstr.com/test'; - $data = [ - 'title' => $str, - 'links' => [ - [ - 'url' => $str, - ] - ], - '_LOGGEDIN_' => false, - ]; - - $result = hook_wallabag_render_linklist($data, $conf); - - static::assertSame($data, $result); - } -} diff --git a/tests/plugins/WallabagInstanceTest.php b/tests/plugins/WallabagInstanceTest.php deleted file mode 100644 index 665a0562..00000000 --- a/tests/plugins/WallabagInstanceTest.php +++ /dev/null @@ -1,60 +0,0 @@ -instance = 'http://some.url'; - } - - /** - * Test WallabagInstance with API V1. - */ - public function testWallabagInstanceV1() - { - $instance = new WallabagInstance($this->instance, 1); - $expected = $this->instance . '/?plainurl='; - $result = $instance->getWallabagUrl(); - $this->assertEquals($expected, $result); - } - - /** - * Test WallabagInstance with API V2. - */ - public function testWallabagInstanceV2() - { - $instance = new WallabagInstance($this->instance, 2); - $expected = $this->instance . '/bookmarklet?url='; - $result = $instance->getWallabagUrl(); - $this->assertEquals($expected, $result); - } - - /** - * Test WallabagInstance with an invalid API version. - */ - public function testWallabagInstanceInvalidVersion() - { - $instance = new WallabagInstance($this->instance, false); - $expected = $this->instance . '/?plainurl='; - $result = $instance->getWallabagUrl(); - $this->assertEquals($expected, $result); - - $instance = new WallabagInstance($this->instance, 3); - $expected = $this->instance . '/?plainurl='; - $result = $instance->getWallabagUrl(); - $this->assertEquals($expected, $result); - } -} diff --git a/tests/plugins/test/test.meta b/tests/plugins/test/test.meta deleted file mode 100644 index 26f243f0..00000000 --- a/tests/plugins/test/test.meta +++ /dev/null @@ -1,4 +0,0 @@ -description="test plugin" -parameters="pop;hip" -parameter.pop="pop description" -parameter.hip= \ No newline at end of file diff --git a/tests/plugins/test/test.php b/tests/plugins/test/test.php deleted file mode 100644 index 8dbb3f94..00000000 --- a/tests/plugins/test/test.php +++ /dev/null @@ -1,52 +0,0 @@ - 'GET', - 'route' => '/test', - 'callable' => 'getFunction', - ], - [ - 'method' => 'POST', - 'route' => '/custom', - 'callable' => 'postFunction', - ], - ]; -} - -function hook_test_filter_search_entry(Bookmark $bookmark, array $context): bool -{ - return $context['_result']; -} diff --git a/tests/plugins/test_route_invalid/test_route_invalid.php b/tests/plugins/test_route_invalid/test_route_invalid.php deleted file mode 100644 index cdec9933..00000000 --- a/tests/plugins/test_route_invalid/test_route_invalid.php +++ /dev/null @@ -1,12 +0,0 @@ - 'I_INVENT_MY_HTTP_METHODS', - 'route' => '/hello', - 'callable' => 'getFunction', - ], - ]; -} diff --git a/tests/render/PageCacheManagerTest.php b/tests/render/PageCacheManagerTest.php deleted file mode 100644 index 212f5734..00000000 --- a/tests/render/PageCacheManagerTest.php +++ /dev/null @@ -1,85 +0,0 @@ -cacheManager = new PageCacheManager(static::$testCacheDir, true); - - if (!is_dir(self::$testCacheDir)) { - mkdir(self::$testCacheDir); - } else { - array_map('unlink', glob(self::$testCacheDir . '/*')); - } - - foreach (self::$pages as $page) { - file_put_contents(self::$testCacheDir . '/' . $page . '.cache', $page); - } - file_put_contents(self::$testCacheDir . '/intru.der', 'ShouldNotBeThere'); - } - - /** - * Remove dummycache folder after each tests. - */ - protected function tearDown(): void - { - array_map('unlink', glob(self::$testCacheDir . '/*')); - rmdir(self::$testCacheDir); - } - - /** - * Purge cached pages - */ - public function testPurgeCachedPages() - { - $this->cacheManager->purgeCachedPages(); - foreach (self::$pages as $page) { - $this->assertFileNotExists(self::$testCacheDir . '/' . $page . '.cache'); - } - - $this->assertFileExists(self::$testCacheDir . '/intru.der'); - } - - /** - * Purge cached pages - missing directory - */ - public function testPurgeCachedPagesMissingDir() - { - $this->cacheManager = new PageCacheManager(self::$testCacheDir . '_missing', true); - - $oldlog = ini_get('error_log'); - ini_set('error_log', '/dev/null'); - $this->assertEquals( - 'Cannot purge sandbox/dummycache_missing: no directory', - $this->cacheManager->purgeCachedPages() - ); - ini_set('error_log', $oldlog); - } -} diff --git a/tests/render/ThemeUtilsTest.php b/tests/render/ThemeUtilsTest.php deleted file mode 100644 index f59fcf04..00000000 --- a/tests/render/ThemeUtilsTest.php +++ /dev/null @@ -1,55 +0,0 @@ -assertTrue(in_array($theme, $themes)); - } - $this->assertFalse(in_array('supertheme', $res)); - - foreach ($themes as $theme) { - rmdir('sandbox/tpl/' . $theme); - } - unlink('sandbox/tpl/supertheme'); - rmdir('sandbox/tpl'); - } - - /** - * Test getThemes() without any theme dir. - */ - public function testGetThemesEmpty() - { - mkdir('sandbox/tpl/', 0755, true); - $this->assertEquals([], ThemeUtils::getThemes('sandbox/tpl/')); - rmdir('sandbox/tpl/'); - } - - /** - * Test getThemes() with an invalid path. - */ - public function testGetThemesInvalid() - { - $this->assertEquals([], ThemeUtils::getThemes('nope')); - } -} diff --git a/tests/security/BanManagerTest.php b/tests/security/BanManagerTest.php deleted file mode 100644 index 31b8cdd5..00000000 --- a/tests/security/BanManagerTest.php +++ /dev/null @@ -1,393 +0,0 @@ -banFile)) { - unlink($this->banFile); - } - - $this->banManager = $this->getNewBanManagerInstance(); - $this->server['REMOTE_ADDR'] = $this->ipAddr; - } - - /** - * Test constructor with initial file. - */ - public function testInstantiateFromFile() - { - $time = time() + 10; - FileUtils::writeFlatDB( - $this->banFile, - [ - 'failures' => [ - $this->ipAddr => 2, - $ip = '1.2.3.4' => 1, - ], - 'bans' => [ - $ip2 = '8.8.8.8' => $time, - $ip3 = '1.1.1.1' => $time + 1, - ], - ] - ); - $this->banManager = $this->getNewBanManagerInstance(); - - $this->assertCount(2, $this->banManager->getFailures()); - $this->assertEquals(2, $this->banManager->getFailures()[$this->ipAddr]); - $this->assertEquals(1, $this->banManager->getFailures()[$ip]); - $this->assertCount(2, $this->banManager->getBans()); - $this->assertEquals($time, $this->banManager->getBans()[$ip2]); - $this->assertEquals($time + 1, $this->banManager->getBans()[$ip3]); - } - - /** - * Test constructor with initial file with invalid values - */ - public function testInstantiateFromCrappyFile() - { - FileUtils::writeFlatDB($this->banFile, 'plop'); - $this->banManager = $this->getNewBanManagerInstance(); - - $this->assertEquals([], $this->banManager->getFailures()); - $this->assertEquals([], $this->banManager->getBans()); - } - - /** - * Test failed attempt with a direct IP. - */ - public function testHandleFailedAttempt() - { - $this->assertCount(0, $this->banManager->getFailures()); - - $this->banManager->handleFailedAttempt($this->server); - $this->assertCount(1, $this->banManager->getFailures()); - $this->assertEquals(1, $this->banManager->getFailures()[$this->ipAddr]); - - $this->banManager->handleFailedAttempt($this->server); - $this->assertCount(1, $this->banManager->getFailures()); - $this->assertEquals(2, $this->banManager->getFailures()[$this->ipAddr]); - } - - /** - * Test failed attempt behind a trusted proxy IP (with proper IP forwarding). - */ - public function testHandleFailedAttemptBehingProxy() - { - $server = [ - 'REMOTE_ADDR' => $this->trustedProxy, - 'HTTP_X_FORWARDED_FOR' => $this->ipAddr, - ]; - $this->assertCount(0, $this->banManager->getFailures()); - - $this->banManager->handleFailedAttempt($server); - $this->assertCount(1, $this->banManager->getFailures()); - $this->assertEquals(1, $this->banManager->getFailures()[$this->ipAddr]); - - $this->banManager->handleFailedAttempt($server); - $this->assertCount(1, $this->banManager->getFailures()); - $this->assertEquals(2, $this->banManager->getFailures()[$this->ipAddr]); - } - - /** - * Test failed attempt behind a trusted proxy IP but without IP forwarding. - */ - public function testHandleFailedAttemptBehindNotConfiguredProxy() - { - $server = [ - 'REMOTE_ADDR' => $this->trustedProxy, - ]; - $this->assertCount(0, $this->banManager->getFailures()); - - $this->banManager->handleFailedAttempt($server); - $this->assertCount(0, $this->banManager->getFailures()); - - $this->banManager->handleFailedAttempt($server); - $this->assertCount(0, $this->banManager->getFailures()); - } - - /** - * Test failed attempts with multiple direct IP. - */ - public function testHandleFailedAttemptMultipleIp() - { - $this->assertCount(0, $this->banManager->getFailures()); - $this->banManager->handleFailedAttempt($this->server); - $this->server['REMOTE_ADDR'] = '1.2.3.4'; - $this->banManager->handleFailedAttempt($this->server); - $this->banManager->handleFailedAttempt($this->server); - $this->assertCount(2, $this->banManager->getFailures()); - $this->assertEquals(1, $this->banManager->getFailures()[$this->ipAddr]); - $this->assertEquals(2, $this->banManager->getFailures()[$this->server['REMOTE_ADDR']]); - } - - /** - * Test clear failure for provided IP without any additional data. - */ - public function testClearFailuresEmpty() - { - $this->assertCount(0, $this->banManager->getFailures()); - $this->banManager->clearFailures($this->server); - $this->assertCount(0, $this->banManager->getFailures()); - } - - /** - * Test clear failure for provided IP with failed attempts. - */ - public function testClearFailuresFromFile() - { - FileUtils::writeFlatDB( - $this->banFile, - [ - 'failures' => [ - $this->ipAddr => 2, - $ip = '1.2.3.4' => 1, - ] - ] - ); - $this->banManager = $this->getNewBanManagerInstance(); - - $this->assertCount(2, $this->banManager->getFailures()); - $this->banManager->clearFailures($this->server); - $this->assertCount(1, $this->banManager->getFailures()); - $this->assertEquals(1, $this->banManager->getFailures()[$ip]); - } - - /** - * Test clear failure for provided IP with failed attempts, behind a reverse proxy. - */ - public function testClearFailuresFromFileBehindProxy() - { - $server = [ - 'REMOTE_ADDR' => $this->trustedProxy, - 'HTTP_X_FORWARDED_FOR' => $this->ipAddr, - ]; - - FileUtils::writeFlatDB( - $this->banFile, - [ - 'failures' => [ - $this->ipAddr => 2, - $ip = '1.2.3.4' => 1, - ] - ] - ); - $this->banManager = $this->getNewBanManagerInstance(); - - $this->assertCount(2, $this->banManager->getFailures()); - $this->banManager->clearFailures($server); - $this->assertCount(1, $this->banManager->getFailures()); - $this->assertEquals(1, $this->banManager->getFailures()[$ip]); - } - - /** - * Test clear failure for provided IP with failed attempts, - * behind a reverse proxy without forwarding. - */ - public function testClearFailuresFromFileBehindNotConfiguredProxy() - { - $server = [ - 'REMOTE_ADDR' => $this->trustedProxy, - ]; - - FileUtils::writeFlatDB( - $this->banFile, - [ - 'failures' => [ - $this->ipAddr => 2, - $ip = '1.2.3.4' => 1, - ] - ] - ); - $this->banManager = $this->getNewBanManagerInstance(); - - $this->assertCount(2, $this->banManager->getFailures()); - $this->banManager->clearFailures($server); - $this->assertCount(2, $this->banManager->getFailures()); - } - - /** - * Test isBanned without any data - */ - public function testIsBannedEmpty() - { - $this->assertFalse($this->banManager->isBanned($this->server)); - } - - /** - * Test isBanned with banned IP from file data - */ - public function testBannedFromFile() - { - FileUtils::writeFlatDB( - $this->banFile, - [ - 'bans' => [ - $this->ipAddr => time() + 10, - ] - ] - ); - $this->banManager = $this->getNewBanManagerInstance(); - - $this->assertCount(1, $this->banManager->getBans()); - $this->assertTrue($this->banManager->isBanned($this->server)); - } - - /** - * Test isBanned with banned IP from file data behind a reverse proxy - */ - public function testBannedFromFileBehindProxy() - { - $server = [ - 'REMOTE_ADDR' => $this->trustedProxy, - 'HTTP_X_FORWARDED_FOR' => $this->ipAddr, - ]; - FileUtils::writeFlatDB( - $this->banFile, - [ - 'bans' => [ - $this->ipAddr => time() + 10, - ] - ] - ); - $this->banManager = $this->getNewBanManagerInstance(); - - $this->assertCount(1, $this->banManager->getBans()); - $this->assertTrue($this->banManager->isBanned($server)); - } - - /** - * Test isBanned with banned IP from file data behind a reverse proxy, - * without IP forwarding - */ - public function testBannedFromFileBehindNotConfiguredProxy() - { - $server = [ - 'REMOTE_ADDR' => $this->trustedProxy, - ]; - FileUtils::writeFlatDB( - $this->banFile, - [ - 'bans' => [ - $this->ipAddr => time() + 10, - ] - ] - ); - $this->banManager = $this->getNewBanManagerInstance(); - - $this->assertCount(1, $this->banManager->getBans()); - $this->assertFalse($this->banManager->isBanned($server)); - } - - /** - * Test isBanned with an expired ban - */ - public function testLiftBan() - { - FileUtils::writeFlatDB( - $this->banFile, - [ - 'bans' => [ - $this->ipAddr => time() - 10, - ] - ] - ); - $this->banManager = $this->getNewBanManagerInstance(); - - $this->assertCount(1, $this->banManager->getBans()); - $this->assertFalse($this->banManager->isBanned($this->server)); - } - - /** - * Test isBanned with an expired ban behind a reverse proxy - */ - public function testLiftBanBehindProxy() - { - $server = [ - 'REMOTE_ADDR' => $this->trustedProxy, - 'HTTP_X_FORWARDED_FOR' => $this->ipAddr, - ]; - - FileUtils::writeFlatDB( - $this->banFile, - [ - 'bans' => [ - $this->ipAddr => time() - 10, - ] - ] - ); - $this->banManager = $this->getNewBanManagerInstance(); - - $this->assertCount(1, $this->banManager->getBans()); - $this->assertFalse($this->banManager->isBanned($server)); - } - - /** - * Test isBanned with an expired ban behind a reverse proxy - */ - public function testLiftBanBehindNotConfiguredProxy() - { - $server = [ - 'REMOTE_ADDR' => $this->trustedProxy, - ]; - - FileUtils::writeFlatDB( - $this->banFile, - [ - 'bans' => [ - $this->ipAddr => time() - 10, - ] - ] - ); - $this->banManager = $this->getNewBanManagerInstance(); - - $this->assertCount(1, $this->banManager->getBans()); - $this->assertFalse($this->banManager->isBanned($server)); - } - - /** - * Build a new instance of BanManager, which will reread the ban file. - * - * @return BanManager instance - */ - protected function getNewBanManagerInstance() - { - return new BanManager( - [$this->trustedProxy], - 3, - 1800, - $this->banFile, - $this->createMock(LoggerInterface::class) - ); - } -} diff --git a/tests/security/LoginManagerTest.php b/tests/security/LoginManagerTest.php deleted file mode 100644 index 18326d2c..00000000 --- a/tests/security/LoginManagerTest.php +++ /dev/null @@ -1,374 +0,0 @@ -banFile)) { - unlink($this->banFile); - } - - $this->passwordHash = sha1($this->password . $this->login . $this->salt); - - $this->configManager = new FakeConfigManager([ - 'credentials.login' => $this->login, - 'credentials.hash' => $this->passwordHash, - 'credentials.salt' => $this->salt, - 'resource.ban_file' => $this->banFile, - 'resource.log' => $this->logFile, - 'security.ban_after' => 2, - 'security.ban_duration' => 3600, - 'security.trusted_proxies' => [$this->trustedProxy], - 'ldap.host' => '', - ]); - - $this->cookie = []; - $this->session = []; - - $this->cookieManager = $this->createMock(CookieManager::class); - $this->cookieManager->method('getCookieParameter')->willReturnCallback(function (string $key) { - return $this->cookie[$key] ?? null; - }); - $this->sessionManager = new SessionManager($this->session, $this->configManager, 'session_path'); - $this->banManager = $this->createMock(BanManager::class); - $this->loginManager = new LoginManager( - $this->configManager, - $this->sessionManager, - $this->cookieManager, - $this->banManager, - $this->createMock(LoggerInterface::class) - ); - $this->server['REMOTE_ADDR'] = $this->ipAddr; - } - - /** - * Record a failed login attempt - */ - public function testHandleFailedLogin(): void - { - $this->banManager->expects(static::exactly(2))->method('handleFailedAttempt'); - $this->banManager->method('isBanned')->willReturn(true); - - $this->loginManager->handleFailedLogin($this->server); - $this->loginManager->handleFailedLogin($this->server); - - static::assertFalse($this->loginManager->canLogin($this->server)); - } - - /** - * Record a failed login attempt - IP behind a trusted proxy - */ - public function testHandleFailedLoginBehindTrustedProxy() - { - $server = [ - 'REMOTE_ADDR' => $this->trustedProxy, - 'HTTP_X_FORWARDED_FOR' => $this->ipAddr, - ]; - - $this->banManager->expects(static::exactly(2))->method('handleFailedAttempt'); - $this->banManager->method('isBanned')->willReturn(true); - - $this->loginManager->handleFailedLogin($server); - $this->loginManager->handleFailedLogin($server); - - $this->assertFalse($this->loginManager->canLogin($server)); - } - - /** - * Record a failed login attempt - IP behind a trusted proxy but not forwarded - */ - public function testHandleFailedLoginBehindTrustedProxyNoIp() - { - $server = [ - 'REMOTE_ADDR' => $this->trustedProxy, - ]; - $this->loginManager->handleFailedLogin($server); - $this->loginManager->handleFailedLogin($server); - $this->assertTrue($this->loginManager->canLogin($server)); - } - - /** - * Nothing to do - */ - public function testHandleSuccessfulLogin() - { - $this->assertTrue($this->loginManager->canLogin($this->server)); - - $this->loginManager->handleSuccessfulLogin($this->server); - $this->assertTrue($this->loginManager->canLogin($this->server)); - } - - /** - * Erase failure records after successfully logging in from this IP - */ - public function testHandleSuccessfulLoginAfterFailure() - { - $this->loginManager->handleFailedLogin($this->server); - $this->assertTrue($this->loginManager->canLogin($this->server)); - - $this->loginManager->handleSuccessfulLogin($this->server); - $this->loginManager->handleFailedLogin($this->server); - $this->assertTrue($this->loginManager->canLogin($this->server)); - } - - /** - * The IP is not banned - */ - public function testCanLoginIpNotBanned() - { - $this->assertTrue($this->loginManager->canLogin($this->server)); - } - - /** - * Generate a token depending on the user credentials and client IP - */ - public function testGenerateStaySignedInToken() - { - $this->loginManager->generateStaySignedInToken($this->clientIpAddress); - - $this->assertEquals( - sha1($this->passwordHash . $this->clientIpAddress . $this->salt), - $this->loginManager->getStaySignedInToken() - ); - } - - /** - * Generate a token depending on the user credentials with session protected disabled - */ - public function testGenerateStaySignedInTokenSessionProtectionDisabled() - { - $this->configManager->set('security.session_protection_disabled', true); - $this->loginManager->generateStaySignedInToken($this->clientIpAddress); - - $this->assertEquals( - sha1($this->passwordHash . $this->salt), - $this->loginManager->getStaySignedInToken() - ); - } - - /** - * Check user login - Shaarli has not yet been configured - */ - public function testCheckLoginStateNotConfigured() - { - $configManager = new FakeConfigManager([ - 'resource.ban_file' => $this->banFile, - ]); - $loginManager = new LoginManager( - $configManager, - $this->sessionManager, - $this->cookieManager, - $this->banManager, - $this->createMock(LoggerInterface::class) - ); - $loginManager->checkLoginState(''); - - $this->assertFalse($loginManager->isLoggedIn()); - } - - /** - * Check user login - the client cookie does not match the server token - */ - public function testCheckLoginStateStaySignedInWithInvalidToken() - { - // simulate a previous login - $this->session = [ - 'ip' => $this->clientIpAddress, - 'expires_on' => time() + 100, - ]; - $this->loginManager->generateStaySignedInToken($this->clientIpAddress); - $this->cookie[CookieManager::STAY_SIGNED_IN] = 'nope'; - - $this->loginManager->checkLoginState($this->clientIpAddress); - - $this->assertTrue($this->loginManager->isLoggedIn()); - $this->assertTrue(empty($this->session['username'])); - } - - /** - * Check user login - the client cookie matches the server token - */ - public function testCheckLoginStateStaySignedInWithValidToken() - { - $this->loginManager->generateStaySignedInToken($this->clientIpAddress); - $this->cookie[CookieManager::STAY_SIGNED_IN] = $this->loginManager->getStaySignedInToken(); - - $this->loginManager->checkLoginState($this->clientIpAddress); - - $this->assertTrue($this->loginManager->isLoggedIn()); - $this->assertEquals($this->login, $this->session['username']); - $this->assertEquals($this->clientIpAddress, $this->session['ip']); - } - - /** - * Check user login - the session has expired - */ - public function testCheckLoginStateSessionExpired() - { - $this->loginManager->generateStaySignedInToken($this->clientIpAddress); - $this->session['expires_on'] = time() - 100; - - $this->loginManager->checkLoginState($this->clientIpAddress); - - $this->assertFalse($this->loginManager->isLoggedIn()); - } - - /** - * Check user login - the remote client IP has changed - */ - public function testCheckLoginStateClientIpChanged() - { - $this->loginManager->generateStaySignedInToken($this->clientIpAddress); - - $this->loginManager->checkLoginState('10.7.157.98'); - - $this->assertFalse($this->loginManager->isLoggedIn()); - } - - /** - * Check user credentials - wrong login supplied - */ - public function testCheckCredentialsWrongLogin() - { - $this->assertFalse( - $this->loginManager->checkCredentials('', 'b4dl0g1n', $this->password) - ); - } - - /** - * Check user credentials - wrong password supplied - */ - public function testCheckCredentialsWrongPassword() - { - $this->assertFalse( - $this->loginManager->checkCredentials('', $this->login, 'b4dp455wd') - ); - } - - /** - * Check user credentials - wrong login and password supplied - */ - public function testCheckCredentialsWrongLoginAndPassword() - { - $this->assertFalse( - $this->loginManager->checkCredentials('', 'b4dl0g1n', 'b4dp455wd') - ); - } - - /** - * Check user credentials - correct login and password supplied - */ - public function testCheckCredentialsGoodLoginAndPassword() - { - $this->assertTrue( - $this->loginManager->checkCredentials('', $this->login, $this->password) - ); - } - - /** - * Check user credentials through LDAP - server unreachable - */ - public function testCheckCredentialsFromUnreachableLdap() - { - $this->configManager->set('ldap.host', 'dummy'); - $this->assertFalse( - $this->loginManager->checkCredentials('', $this->login, $this->password) - ); - } - - /** - * Check user credentials through LDAP - wrong login and password supplied - */ - public function testCheckCredentialsFromLdapWrongLoginAndPassword() - { - $this->configManager->set('ldap.host', 'dummy'); - $this->assertFalse( - $this->loginManager->checkCredentialsFromLdap($this->login, $this->password, function () { - return null; - }, function () { - return false; - }) - ); - } - - /** - * Check user credentials through LDAP - correct login and password supplied - */ - public function testCheckCredentialsFromLdapGoodLoginAndPassword() - { - $this->configManager->set('ldap.host', 'dummy'); - $this->assertTrue( - $this->loginManager->checkCredentialsFromLdap($this->login, $this->password, function () { - return null; - }, function () { - return true; - }) - ); - } -} diff --git a/tests/security/SessionManagerTest.php b/tests/security/SessionManagerTest.php deleted file mode 100644 index 7cc6e1a4..00000000 --- a/tests/security/SessionManagerTest.php +++ /dev/null @@ -1,328 +0,0 @@ -conf = new FakeConfigManager([ - 'credentials.login' => 'johndoe', - 'credentials.salt' => 'salt', - 'security.session_protection_disabled' => false, - ]); - $this->session = []; - $this->sessionManager = new SessionManager($this->session, $this->conf, 'session_path'); - } - - /** - * Generate a session token - */ - public function testGenerateToken() - { - $token = $this->sessionManager->generateToken(); - - $this->assertEquals(1, $this->session['tokens'][$token]); - $this->assertEquals(40, strlen($token)); - } - - /** - * Check a session token - */ - public function testCheckToken() - { - $token = '4dccc3a45ad9d03e5542b90c37d8db6d10f2b38b'; - $session = [ - 'tokens' => [ - $token => 1, - ], - ]; - $sessionManager = new SessionManager($session, $this->conf, 'session_path'); - - // check and destroy the token - $this->assertTrue($sessionManager->checkToken($token)); - $this->assertFalse(isset($session['tokens'][$token])); - - // ensure the token has been destroyed - $this->assertFalse($sessionManager->checkToken($token)); - } - - /** - * Generate and check a session token - */ - public function testGenerateAndCheckToken() - { - $token = $this->sessionManager->generateToken(); - - // ensure a token has been generated - $this->assertEquals(1, $this->session['tokens'][$token]); - $this->assertEquals(40, strlen($token)); - - // check and destroy the token - $this->assertTrue($this->sessionManager->checkToken($token)); - $this->assertFalse(isset($this->session['tokens'][$token])); - - // ensure the token has been destroyed - $this->assertFalse($this->sessionManager->checkToken($token)); - } - - /** - * Check an invalid session token - */ - public function testCheckInvalidToken() - { - $this->assertFalse($this->sessionManager->checkToken('4dccc3a45ad9d03e5542b90c37d8db6d10f2b38b')); - } - - /** - * Test SessionManager::checkId with a valid ID - TEST ALL THE HASHES! - * - * This tests extensively covers all hash algorithms / bit representations - */ - public function testIsAnyHashSessionIdValid() - { - foreach (self::$sidHashes as $algo => $bpcs) { - foreach ($bpcs as $bpc => $hash) { - $this->assertTrue(SessionManager::checkId($hash)); - } - } - } - - /** - * Test checkId with a valid ID - SHA-1 hashes - */ - public function testIsSha1SessionIdValid() - { - $this->assertTrue(SessionManager::checkId(sha1('shaarli'))); - } - - /** - * Test checkId with a valid ID - SHA-256 hashes - */ - public function testIsSha256SessionIdValid() - { - $this->assertTrue(SessionManager::checkId(hash('sha256', 'shaarli'))); - } - - /** - * Test checkId with a valid ID - SHA-512 hashes - */ - public function testIsSha512SessionIdValid() - { - $this->assertTrue(SessionManager::checkId(hash('sha512', 'shaarli'))); - } - - /** - * Test checkId with invalid IDs. - */ - public function testIsSessionIdInvalid() - { - $this->assertFalse(SessionManager::checkId('')); - $this->assertFalse(SessionManager::checkId([])); - $this->assertFalse( - SessionManager::checkId('c0ZqcWF3VFE2NmJBdm1HMVQ0ZHJ3UmZPbTFsNGhkNHI=') - ); - } - - /** - * Store login information after a successful login - */ - public function testStoreLoginInfo() - { - $this->sessionManager->storeLoginInfo('ip_id'); - - $this->assertGreaterThan(time(), $this->session['expires_on']); - $this->assertEquals('ip_id', $this->session['ip']); - $this->assertEquals('johndoe', $this->session['username']); - } - - /** - * Extend a server-side session by SessionManager::$SHORT_TIMEOUT - */ - public function testExtendSession() - { - $this->sessionManager->extendSession(); - - $this->assertGreaterThan(time(), $this->session['expires_on']); - $this->assertLessThanOrEqual( - time() + SessionManager::$SHORT_TIMEOUT, - $this->session['expires_on'] - ); - } - - /** - * Extend a server-side session by SessionManager::$LONG_TIMEOUT - */ - public function testExtendSessionStaySignedIn() - { - $this->sessionManager->setStaySignedIn(true); - $this->sessionManager->extendSession(); - - $this->assertGreaterThan(time(), $this->session['expires_on']); - $this->assertGreaterThan( - time() + SessionManager::$LONG_TIMEOUT - 10, - $this->session['expires_on'] - ); - $this->assertLessThanOrEqual( - time() + SessionManager::$LONG_TIMEOUT, - $this->session['expires_on'] - ); - } - - /** - * Unset session variables after logging out - */ - public function testLogout() - { - $this->session = [ - 'ip' => 'ip_id', - 'expires_on' => time() + 1000, - 'username' => 'johndoe', - 'visibility' => 'public', - 'untaggedonly' => true, - ]; - $this->sessionManager->logout(); - - $this->assertArrayNotHasKey('ip', $this->session); - $this->assertArrayNotHasKey('expires_on', $this->session); - $this->assertArrayNotHasKey('username', $this->session); - $this->assertArrayNotHasKey('visibility', $this->session); - $this->assertArrayHasKey('untaggedonly', $this->session); - $this->assertTrue($this->session['untaggedonly']); - } - - /** - * The session is active and expiration time has been reached - */ - public function testHasExpiredTimeElapsed() - { - $this->session['expires_on'] = time() - 10; - - $this->assertTrue($this->sessionManager->hasSessionExpired()); - } - - /** - * The session is active and expiration time has not been reached - */ - public function testHasNotExpired() - { - $this->session['expires_on'] = time() + 1000; - - $this->assertFalse($this->sessionManager->hasSessionExpired()); - } - - /** - * Session hijacking protection is disabled, we assume the IP has not changed - */ - public function testHasClientIpChangedNoSessionProtection() - { - $this->conf->set('security.session_protection_disabled', true); - - $this->assertFalse($this->sessionManager->hasClientIpChanged('')); - } - - /** - * The client IP identifier has not changed - */ - public function testHasClientIpChangedNope() - { - $this->session['ip'] = 'ip_id'; - $this->assertFalse($this->sessionManager->hasClientIpChanged('ip_id')); - } - - /** - * The client IP identifier has changed - */ - public function testHasClientIpChanged() - { - $this->session['ip'] = 'ip_id_one'; - $this->assertTrue($this->sessionManager->hasClientIpChanged('ip_id_two')); - } - - /** - * Test creating an entry in the session array - */ - public function testSetSessionParameterCreate(): void - { - $this->sessionManager->setSessionParameter('abc', 'def'); - - static::assertSame('def', $this->session['abc']); - } - - /** - * Test updating an entry in the session array - */ - public function testSetSessionParameterUpdate(): void - { - $this->session['abc'] = 'ghi'; - - $this->sessionManager->setSessionParameter('abc', 'def'); - - static::assertSame('def', $this->session['abc']); - } - - /** - * Test updating an entry in the session array with null value - */ - public function testSetSessionParameterUpdateNull(): void - { - $this->session['abc'] = 'ghi'; - - $this->sessionManager->setSessionParameter('abc', null); - - static::assertArrayHasKey('abc', $this->session); - static::assertNull($this->session['abc']); - } - - /** - * Test deleting an existing entry in the session array - */ - public function testDeleteSessionParameter(): void - { - $this->session['abc'] = 'def'; - - $this->sessionManager->deleteSessionParameter('abc'); - - static::assertArrayNotHasKey('abc', $this->session); - } - - /** - * Test deleting a non existent entry in the session array - */ - public function testDeleteSessionParameterNotExisting(): void - { - $this->sessionManager->deleteSessionParameter('abc'); - - static::assertArrayNotHasKey('abc', $this->session); - } -} diff --git a/tests/updater/DummyUpdater.php b/tests/updater/DummyUpdater.php deleted file mode 100644 index 93e63c92..00000000 --- a/tests/updater/DummyUpdater.php +++ /dev/null @@ -1,76 +0,0 @@ -methods = $class->getMethods(ReflectionMethod::IS_FINAL); - } - - /** - * Update method 1. - * - * @return bool true. - */ - final protected function updateMethodDummy1() - { - return true; - } - - /** - * Update method 2. - * - * @return bool true. - */ - final protected function updateMethodDummy2() - { - return true; - } - - /** - * Update method 3. - * - * @return bool true. - */ - final protected function updateMethodDummy3() - { - return true; - } - - /** - * Update method 4, raise an exception. - * - * @throws Exception error. - */ - final protected function updateMethodException() - { - throw new Exception('whatever'); - } -} diff --git a/tests/updater/UpdaterTest.php b/tests/updater/UpdaterTest.php deleted file mode 100644 index 9672a31b..00000000 --- a/tests/updater/UpdaterTest.php +++ /dev/null @@ -1,232 +0,0 @@ -refDB = new ReferenceLinkDB(); - $this->refDB->write(self::$testDatastore); - - copy('tests/utils/config/configJson.json.php', self::$configFile . '.json.php'); - $this->conf = new ConfigManager(self::$configFile); - $this->bookmarkService = new BookmarkFileService( - $this->conf, - $this->createMock(PluginManager::class), - $this->createMock(History::class), - $mutex, - true - ); - $this->updater = new Updater([], $this->bookmarkService, $this->conf, true); - } - - /** - * Test UpdaterUtils::read_updates_file with an empty/missing file. - */ - public function testReadEmptyUpdatesFile() - { - $this->assertEquals([], UpdaterUtils::readUpdatesFile('')); - $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt'; - touch($updatesFile); - $this->assertEquals([], UpdaterUtils::readUpdatesFile($updatesFile)); - unlink($updatesFile); - } - - /** - * Test read/write updates file. - */ - public function testReadWriteUpdatesFile() - { - $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt'; - $updatesMethods = ['m1', 'm2', 'm3']; - - UpdaterUtils::writeUpdatesFile($updatesFile, $updatesMethods); - $readMethods = UpdaterUtils::readUpdatesFile($updatesFile); - $this->assertEquals($readMethods, $updatesMethods); - - // Update - $updatesMethods[] = 'm4'; - UpdaterUtils::writeUpdatesFile($updatesFile, $updatesMethods); - $readMethods = UpdaterUtils::readUpdatesFile($updatesFile); - $this->assertEquals($readMethods, $updatesMethods); - unlink($updatesFile); - } - - /** - * Test errors in UpdaterUtils::write_updates_file(): empty updates file. - */ - public function testWriteEmptyUpdatesFile() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessageRegExp('/Updates file path is not set(.*)/'); - - UpdaterUtils::writeUpdatesFile('', ['test']); - } - - /** - * Test errors in UpdaterUtils::write_updates_file(): not writable updates file. - */ - public function testWriteUpdatesFileNotWritable() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessageRegExp('/Unable to write(.*)/'); - - $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt'; - touch($updatesFile); - chmod($updatesFile, 0444); - try { - @UpdaterUtils::writeUpdatesFile($updatesFile, ['test']); - } catch (Exception $e) { - unlink($updatesFile); - throw $e; - } - } - - /** - * Test the update() method, with no update to run. - * 1. Everything already run. - * 2. User is logged out. - */ - public function testNoUpdates() - { - $updates = [ - 'updateMethodDummy1', - 'updateMethodDummy2', - 'updateMethodDummy3', - 'updateMethodException', - ]; - $updater = new DummyUpdater($updates, [], $this->conf, true); - $this->assertEquals([], $updater->update()); - - $updater = new DummyUpdater([], [], $this->conf, false); - $this->assertEquals([], $updater->update()); - } - - /** - * Test the update() method, with all updates to run (except the failing one). - */ - public function testUpdatesFirstTime() - { - $updates = ['updateMethodException',]; - $expectedUpdates = [ - 'updateMethodDummy1', - 'updateMethodDummy2', - 'updateMethodDummy3', - ]; - $updater = new DummyUpdater($updates, [], $this->conf, true); - $this->assertEquals($expectedUpdates, $updater->update()); - } - - /** - * Test the update() method, only one update to run. - */ - public function testOneUpdate() - { - $updates = [ - 'updateMethodDummy1', - 'updateMethodDummy3', - 'updateMethodException', - ]; - $expectedUpdate = ['updateMethodDummy2']; - - $updater = new DummyUpdater($updates, [], $this->conf, true); - $this->assertEquals($expectedUpdate, $updater->update()); - } - - /** - * Test Update failed. - */ - public function testUpdateFailed() - { - $this->expectException(\Exception::class); - - $updates = [ - 'updateMethodDummy1', - 'updateMethodDummy2', - 'updateMethodDummy3', - ]; - - $updater = new DummyUpdater($updates, [], $this->conf, true); - $updater->update(); - } - - public function testUpdateMethodRelativeHomeLinkRename(): void - { - $this->updater->setBasePath('/subfolder'); - $this->conf->set('general.header_link', '?'); - - $this->updater->updateMethodRelativeHomeLink(); - - static::assertSame('/subfolder/', $this->conf->get('general.header_link')); - } - - public function testUpdateMethodRelativeHomeLinkDoNotRename(): void - { - $this->conf->set('general.header_link', '~/my-blog'); - - $this->updater->updateMethodRelativeHomeLink(); - - static::assertSame('~/my-blog', $this->conf->get('general.header_link')); - } - - public function testUpdateMethodMigrateExistingNotesUrl(): void - { - $this->updater->updateMethodMigrateExistingNotesUrl(); - - static::assertSame($this->refDB->getLinks()[0]->getUrl(), $this->bookmarkService->get(0)->getUrl()); - static::assertSame($this->refDB->getLinks()[1]->getUrl(), $this->bookmarkService->get(1)->getUrl()); - static::assertSame($this->refDB->getLinks()[4]->getUrl(), $this->bookmarkService->get(4)->getUrl()); - static::assertSame($this->refDB->getLinks()[6]->getUrl(), $this->bookmarkService->get(6)->getUrl()); - static::assertSame($this->refDB->getLinks()[7]->getUrl(), $this->bookmarkService->get(7)->getUrl()); - static::assertSame($this->refDB->getLinks()[8]->getUrl(), $this->bookmarkService->get(8)->getUrl()); - static::assertSame($this->refDB->getLinks()[9]->getUrl(), $this->bookmarkService->get(9)->getUrl()); - static::assertSame('/shaare/WDWyig', $this->bookmarkService->get(42)->getUrl()); - static::assertSame('/shaare/WDWyig', $this->bookmarkService->get(41)->getUrl()); - static::assertSame('/shaare/0gCTjQ', $this->bookmarkService->get(10)->getUrl()); - static::assertSame('/shaare/PCRizQ', $this->bookmarkService->get(11)->getUrl()); - } -} diff --git a/tests/utils/CurlUtils.php b/tests/utils/CurlUtils.php deleted file mode 100644 index 0c9c1fb2..00000000 --- a/tests/utils/CurlUtils.php +++ /dev/null @@ -1,95 +0,0 @@ -bookmarks; - } -} diff --git a/tests/utils/FakeConfigManager.php b/tests/utils/FakeConfigManager.php deleted file mode 100644 index 014c2af0..00000000 --- a/tests/utils/FakeConfigManager.php +++ /dev/null @@ -1,61 +0,0 @@ -values = $values; - } - - /** - * Set a given value - * - * @param string $key Key of the value to set - * @param mixed $value Value to set - */ - public function set($key, $value, $write = false, $isLoggedIn = false) - { - $this->values[$key] = $value; - } - - /** - * Get a given configuration value - * - * @param string $key Index of the value to retrieve - * - * @return mixed The value if set, else the name of the key - */ - public function get($key, $default = '') - { - if (isset($this->values[$key])) { - return $this->values[$key]; - } - return $key; - } - - /** - * Check if a setting exists - * - * @param string $setting Asked setting, keys separated with dots - * - * @return bool true if the setting exists, false otherwise - */ - public function exists($setting) - { - return array_key_exists($setting, $this->values); - } -} diff --git a/tests/utils/RainTPL.php b/tests/utils/RainTPL.php deleted file mode 100644 index 65046ce8..00000000 --- a/tests/utils/RainTPL.php +++ /dev/null @@ -1,6 +0,0 @@ -addEntry( - History::DELETED, - DateTime::createFromFormat('Ymd_His', '20170303_121216'), - 124 - ); - - $this->addEntry( - History::SETTINGS, - DateTime::createFromFormat('Ymd_His', '20170302_121215') - ); - - $this->addEntry( - History::UPDATED, - DateTime::createFromFormat('Ymd_His', '20170301_121214'), - 123 - ); - - $this->addEntry( - History::CREATED, - DateTime::createFromFormat('Ymd_His', '20170201_121214'), - 124 - ); - - $this->addEntry( - History::CREATED, - DateTime::createFromFormat('Ymd_His', '20170101_121212'), - 123 - ); - } - - /** - * Adds a new history entry - * - * @param string $event Event identifier - * @param DateTime $datetime creation date - * @param int $id optional: related link ID - */ - protected function addEntry($event, $datetime, $id = null) - { - $link = [ - 'event' => $event, - 'datetime' => $datetime, - 'id' => $id, - ]; - $this->history[] = $link; - $this->count++; - } - - /** - * Writes data to the datastore - * - * @param string $filename write history content to. - */ - public function write($filename) - { - FileUtils::writeFlatDB($filename, $this->history); - } - - /** - * Returns the number of bookmarks in the reference data - */ - public function count() - { - return $this->count; - } -} diff --git a/tests/utils/ReferenceLinkDB.php b/tests/utils/ReferenceLinkDB.php deleted file mode 100644 index e85dfdea..00000000 --- a/tests/utils/ReferenceLinkDB.php +++ /dev/null @@ -1,293 +0,0 @@ -isLegacy = $isLegacy; - if (! $this->isLegacy) { - $this->bookmarks = new BookmarkArray(); - } - $this->addLink( - 11, - 'Pined older', - '/shaare/PCRizQ', - 'This is an older pinned link', - 0, - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20100309_101010'), - '', - null, - 'PCRizQ', - true - ); - - $this->addLink( - 10, - 'Pined', - '/shaare/0gCTjQ', - 'This is a pinned link', - 0, - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121207_152312'), - '', - null, - '0gCTjQ', - true - ); - - $this->addLink( - 41, - 'Link title: @website', - '/shaare/WDWyig', - 'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag', - 0, - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), - 'sTuff', - null, - 'WDWyig' - ); - - $this->addLink( - 42, - 'Note: I have a big ID but an old date', - '/shaare/WDWyig', - 'Used to test bookmarks reordering.', - 0, - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20100310_101010'), - 'ut' - ); - - $this->addLink( - 9, - 'PSR-2: Coding Style Guide', - 'http://www.php-fig.org/psr/psr-2/', - 'This guide extends and expands on PSR-1, the basic coding standard.', - 0, - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_152312'), - 'coding-style standards quality assurance' - ); - - $this->addLink( - 8, - 'Free as in Freedom 2.0 @website', - 'https://static.fsf.org/nosvn/faif-2.0.pdf', - 'Richard Stallman and the Free Software Revolution. Read this. #hashtag', - 0, - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114633'), - 'free gnu software stallman -exclude stuff hashtag', - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160803_093033') - ); - - $this->addLink( - 7, - 'MediaGoblin', - 'http://mediagoblin.org/', - 'A free software media publishing platform #hashtagOther', - 0, - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130614_184135'), - 'gnu media web .hidden hashtag', - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130615_184230'), - 'IuWvgA' - ); - - $this->addLink( - 6, - 'w3c-markup-validator', - 'https://dvcs.w3.org/hg/markup-validator/summary', - 'Mercurial repository for the W3C Validator #private', - 1, - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20141125_084734'), - 'css html w3c web Mercurial' - ); - - $this->addLink( - 4, - 'UserFriendly - Web Designer', - 'http://ars.userfriendly.org/cartoons/?id=20121206', - 'Naming conventions... #private', - 0, - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_142300'), - 'dev cartoon web' - ); - - $this->addLink( - 1, - 'UserFriendly - Samba', - 'http://ars.userfriendly.org/cartoons/?id=20010306', - 'Tropical printing', - 0, - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_172539'), - 'samba cartoon web' - ); - - $this->addLink( - 0, - 'Geek and Poke', - 'http://geek-and-poke.com/', - '', - 1, - DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_182539'), - 'dev cartoon tag1 tag2 tag3 tag4 ' - ); - } - - /** - * Adds a new link - */ - protected function addLink( - $id, - $title, - $url, - $description, - $private, - $date, - $tags, - $updated = '', - $shorturl = '', - $pinned = false - ) { - $link = [ - 'id' => $id, - 'title' => $title, - 'url' => $url, - 'description' => $description, - 'private' => $private, - 'tags' => $tags, - 'created' => $date, - 'updated' => $updated, - 'shorturl' => $shorturl ? $shorturl : smallHash($date->format(Bookmark::LINK_DATE_FORMAT) . $id), - 'sticky' => $pinned - ]; - if (! $this->isLegacy) { - $bookmark = new Bookmark(); - $this->bookmarks[$id] = $bookmark->fromArray($link); - } else { - $this->bookmarks[$id] = $link; - } - - if ($private) { - $this->privateCount++; - return; - } - $this->publicCount++; - } - - /** - * Writes data to the datastore - */ - public function write($filename) - { - $this->reorder(); - file_put_contents( - $filename, - 'bookmarks))) . ' */ ?>' - ); - } - - /** - * Reorder links by creation date (newest first). - * - * @param string $order ASC|DESC - */ - public function reorder($order = 'DESC') - { - if (! $this->isLegacy) { - $this->bookmarks->reorder($order); - } else { - $order = $order === 'ASC' ? -1 : 1; - // backward compatibility: ignore reorder if the the `created` field doesn't exist - if (! isset(array_values($this->bookmarks)[0]['created'])) { - return; - } - - usort($this->bookmarks, function ($a, $b) use ($order) { - if (isset($a['sticky']) && isset($b['sticky']) && $a['sticky'] !== $b['sticky']) { - return $a['sticky'] ? -1 : 1; - } - - return $a['created'] < $b['created'] ? 1 * $order : -1 * $order; - }); - } - } - - /** - * Returns the number of bookmarks in the reference data - */ - public function countLinks() - { - return $this->publicCount + $this->privateCount; - } - - /** - * Returns the number of public bookmarks in the reference data - */ - public function countPublicLinks() - { - return $this->publicCount; - } - - /** - * Returns the number of private bookmarks in the reference data - */ - public function countPrivateLinks() - { - return $this->privateCount; - } - - /** - * Returns the number of bookmarks without tag - */ - public function countUntaggedLinks() - { - $cpt = 0; - foreach ($this->bookmarks as $link) { - if (! $this->isLegacy) { - if (empty($link->getTags())) { - ++$cpt; - } - } else { - if (empty($link['tags'])) { - ++$cpt; - } - } - } - return $cpt; - } - - public function getLinks() - { - $this->reorder(); - return $this->bookmarks; - } - - /** - * Setter to override link creation. - * - * @param array $links List of bookmarks. - */ - public function setLinks($links) - { - $this->bookmarks = $links; - } -} diff --git a/tests/utils/ReferenceSessionIdHashes.php b/tests/utils/ReferenceSessionIdHashes.php deleted file mode 100644 index d0b2ddf7..00000000 --- a/tests/utils/ReferenceSessionIdHashes.php +++ /dev/null @@ -1,58 +0,0 @@ - diff --git a/tests/utils/config/configJson.json.php b/tests/utils/config/configJson.json.php deleted file mode 100644 index b04dc303..00000000 --- a/tests/utils/config/configJson.json.php +++ /dev/null @@ -1,84 +0,0 @@ - diff --git a/tests/utils/config/configPhp.php b/tests/utils/config/configPhp.php deleted file mode 100644 index 3dcd2d96..00000000 --- a/tests/utils/config/configPhp.php +++ /dev/null @@ -1,15 +0,0 @@ -nA9<++2G%9DgxL}w^!7z>E1vUYkTVjVKMHS;ZL`^B0E8jplkEvDK70MecAsi?@U8*TzN@_0QcBYgq^1rr(iGAvg z!eM{dU5AY-BJ3E=S$W#Wf@673ezGW3u3M1Jly&6WcWDnBYhU^5ge-P-X2!XmN@zUi z{KsNxRe?QY=T3rN{2pC9Zo#T-A85FM=&%(&;1y}ItKf;Daqc0mh)O*SGV>bz05WrP A_y7O^ diff --git a/tests/utils/customtpl/dummy/language/fr/LC_MESSAGES/dummy.po b/tests/utils/customtpl/dummy/language/fr/LC_MESSAGES/dummy.po deleted file mode 100644 index 90d1abb0..00000000 --- a/tests/utils/customtpl/dummy/language/fr/LC_MESSAGES/dummy.po +++ /dev/null @@ -1,16 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Theme extension test\n" -"POT-Creation-Date: 2017-05-20 13:54+0200\n" -"PO-Revision-Date: 2018-03-26 19:09+0200\n" -"Last-Translator: \n" -"Language-Team: Shaarli\n" -"Language: fr_FR\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Poedit 2.0.6\n" - -msgid "rooster" -msgstr "coq" diff --git a/tests/utils/languages/fr/LC_MESSAGES/test.mo b/tests/utils/languages/fr/LC_MESSAGES/test.mo deleted file mode 100644 index 416c783105300fb5ecdfdb2636b060c338b65e55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 456 zcmZvX&rSj{5XS5OWRIRb^r$h#?g~mu@Pe!%LL{&nZ#pgmTeG&L+a>rQzKchn#Ak69 zj9i@bm-#yR+J5~$-2GrwJH!!DC-#UwQPnfCPXJMQGU9fNv7Gt=@kzSsU({)>s`73B zYBol2X~t4;Z0PJOre5?W;sITutx>$Y^k^!{Jr+I~-X)^r5Ijx9HF#7!lHsM04G~Em zo~uUvR7O&gQH*e*tCRou>MFcg`}$CLkvK3#4&FM&gFcv92{RN4!kgmb48Z03Z>;## zJ;kG7&>M6&F~gb+I@VBDy6t^Vu{j>PPCaR z-h9#Y$Gmiqi`criLUA)+ZHso);%nJHHvEc*C#~R7+@L|X4nDLv(Vj;&SqlRe+fwH_ LvW3;YbjW@H<$QQq diff --git a/tests/utils/languages/fr/LC_MESSAGES/test.po b/tests/utils/languages/fr/LC_MESSAGES/test.po deleted file mode 100644 index 89a4fd9b..00000000 --- a/tests/utils/languages/fr/LC_MESSAGES/test.po +++ /dev/null @@ -1,19 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Extension test\n" -"POT-Creation-Date: 2017-05-20 13:54+0200\n" -"PO-Revision-Date: 2017-05-20 14:16+0200\n" -"Last-Translator: \n" -"Language-Team: Shaarli\n" -"Language: fr_FR\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Poedit 2.0.1\n" - -msgid "car" -msgstr "voiture" - -msgid "Search" -msgstr "Fouille"