1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-26 23:38:08 +02:00

Compare commits

...

24 Commits

Author SHA1 Message Date
Laurent Cozic
84a793270d move back to test 2021-05-21 15:15:48 +02:00
Laurent Cozic
5f8b73350d other tdsts 2021-05-21 15:09:08 +02:00
Laurent Cozic
a733cb0394 services 2021-05-21 13:44:57 +02:00
Laurent Cozic
7da4cb0d80 sync tests 2021-05-21 13:22:50 +02:00
Laurent Cozic
3233082b4c move more 2021-05-21 13:14:45 +02:00
Laurent Cozic
c782f5e981 move 2021-05-21 13:01:43 +02:00
Laurent Cozic
db51e2dd4b cli setup 2021-05-21 12:42:19 +02:00
Laurent Cozic
f00f3d7f04 test move 2021-05-21 12:33:33 +02:00
Laurent Cozic
6ff560f22f Desktop: Fixes #4591: Prevent cursor from jumping to top of page when pasting image 2021-05-20 19:13:35 +02:00
Laurent Cozic
2226b79c46 Desktop: Fixed pasting HTML in Rich Text editor, and improved pasting plain text 2021-05-20 18:08:59 +02:00
Laurent Cozic
9e9bf63d70 Desktop: Regression: Pasting plain text in Rich Text editor was broken 2021-05-20 17:42:04 +02:00
Breno Baptista
5d9419be5d Doc: Improved Linux notifications instruction (#4985) 2021-05-19 22:27:43 +01:00
Roman Musin
9f37aa96c6 Android: Fixes #4912, Fixes #4086, Fixes #4908: Fixed and improved alarm notifications (#4984) 2021-05-19 22:26:42 +01:00
JackGruber
eceb14ff9e Desktop: Resolves #4433: Import linked local files when importing Markdown files (#4966) 2021-05-19 22:22:03 +01:00
Laurent Cozic
85211e8d5c Mobile: Fixes #4494: Fixed opening URLs that contain non-alphabetical characters 2021-05-19 16:48:55 +02:00
Laurent Cozic
bd08041f53 Desktop: Pass custom CSS property to all export handlers and renderers 2021-05-19 15:00:16 +02:00
Laurent Cozic
77b284f01f Server: Added API end points to manage users 2021-05-18 17:11:50 +02:00
Laurent Cozic
daaaa133ab Server: Allow enabling or disabling the sharing feature per user 2021-05-18 15:53:56 +02:00
Laurent Cozic
e6c4eb7cdf Server: Renamed users.item_max_size => users.max_item_size 2021-05-18 15:11:03 +02:00
tatsu
dc2cdb7d3a Doc: Fixed json of s3 bucket policy (#4981) 2021-05-17 15:15:23 -04:00
Andre Jilderda
f3e03d48bb Desktop: Add classnames to DOM elements for theming purposes (#4933) 2021-05-17 19:33:44 +01:00
Laurent Cozic
6577f4f35d Desktop: Regression: It was no longer possible to add list items in an empty note 2021-05-17 20:30:48 +02:00
Laurent Cozic
d29624c816 Desktop: Handle too large items for Joplin Server 2021-05-17 18:55:39 +02:00
Laurent Cozic
6afde54bda Server: Add support for item size limit 2021-05-17 18:35:01 +02:00
150 changed files with 1936 additions and 1376 deletions

View File

@@ -74,69 +74,18 @@ packages/app-cli/app/command-settingschema.js.map
packages/app-cli/app/services/plugins/PluginRunner.d.ts
packages/app-cli/app/services/plugins/PluginRunner.js
packages/app-cli/app/services/plugins/PluginRunner.js.map
packages/app-cli/tests/EnexToMd.d.ts
packages/app-cli/tests/EnexToMd.js
packages/app-cli/tests/EnexToMd.js.map
packages/app-cli/tests/HtmlToMd.d.ts
packages/app-cli/tests/HtmlToMd.js
packages/app-cli/tests/HtmlToMd.js.map
packages/app-cli/tests/InMemoryCache.d.ts
packages/app-cli/tests/InMemoryCache.js
packages/app-cli/tests/InMemoryCache.js.map
packages/app-cli/tests/MdToHtml.d.ts
packages/app-cli/tests/MdToHtml.js
packages/app-cli/tests/MdToHtml.js.map
packages/app-cli/tests/Synchronizer.basics.d.ts
packages/app-cli/tests/Synchronizer.basics.js
packages/app-cli/tests/Synchronizer.basics.js.map
packages/app-cli/tests/Synchronizer.conflicts.d.ts
packages/app-cli/tests/Synchronizer.conflicts.js
packages/app-cli/tests/Synchronizer.conflicts.js.map
packages/app-cli/tests/Synchronizer.e2ee.d.ts
packages/app-cli/tests/Synchronizer.e2ee.js
packages/app-cli/tests/Synchronizer.e2ee.js.map
packages/app-cli/tests/Synchronizer.resources.d.ts
packages/app-cli/tests/Synchronizer.resources.js
packages/app-cli/tests/Synchronizer.resources.js.map
packages/app-cli/tests/Synchronizer.revisions.d.ts
packages/app-cli/tests/Synchronizer.revisions.js
packages/app-cli/tests/Synchronizer.revisions.js.map
packages/app-cli/tests/Synchronizer.sharing.d.ts
packages/app-cli/tests/Synchronizer.sharing.js
packages/app-cli/tests/Synchronizer.sharing.js.map
packages/app-cli/tests/Synchronizer.tags.d.ts
packages/app-cli/tests/Synchronizer.tags.js
packages/app-cli/tests/Synchronizer.tags.js.map
packages/app-cli/tests/Synchronizer.tools.d.ts
packages/app-cli/tests/Synchronizer.tools.js
packages/app-cli/tests/Synchronizer.tools.js.map
packages/app-cli/tests/dateTimeFormats.d.ts
packages/app-cli/tests/dateTimeFormats.js
packages/app-cli/tests/dateTimeFormats.js.map
packages/app-cli/tests/file-api-driver.d.ts
packages/app-cli/tests/file-api-driver.js
packages/app-cli/tests/file-api-driver.js.map
packages/app-cli/tests/fsDriver.d.ts
packages/app-cli/tests/fsDriver.js
packages/app-cli/tests/fsDriver.js.map
packages/app-cli/tests/htmlUtils.d.ts
packages/app-cli/tests/htmlUtils.js
packages/app-cli/tests/htmlUtils.js.map
packages/app-cli/tests/models_Folder.d.ts
packages/app-cli/tests/models_Folder.js
packages/app-cli/tests/models_Folder.js.map
packages/app-cli/tests/models_Folder.sharing.d.ts
packages/app-cli/tests/models_Folder.sharing.js
packages/app-cli/tests/models_Folder.sharing.js.map
packages/app-cli/tests/models_Note.d.ts
packages/app-cli/tests/models_Note.js
packages/app-cli/tests/models_Note.js.map
packages/app-cli/tests/models_Setting.d.ts
packages/app-cli/tests/models_Setting.js
packages/app-cli/tests/models_Setting.js.map
packages/app-cli/tests/registry.d.ts
packages/app-cli/tests/registry.js
packages/app-cli/tests/registry.js.map
packages/app-cli/tests/MdToMd.d.ts
packages/app-cli/tests/MdToMd.js
packages/app-cli/tests/MdToMd.js.map
packages/app-cli/tests/services/plugins/PluginService.d.ts
packages/app-cli/tests/services/plugins/PluginService.js
packages/app-cli/tests/services/plugins/PluginService.js.map
packages/app-cli/tests/services/plugins/RepositoryApi.d.ts
packages/app-cli/tests/services/plugins/RepositoryApi.js
packages/app-cli/tests/services/plugins/RepositoryApi.js.map
@@ -152,42 +101,6 @@ packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js.map
packages/app-cli/tests/services/plugins/sandboxProxy.d.ts
packages/app-cli/tests/services/plugins/sandboxProxy.js
packages/app-cli/tests/services/plugins/sandboxProxy.js.map
packages/app-cli/tests/services_CommandService.d.ts
packages/app-cli/tests/services_CommandService.js
packages/app-cli/tests/services_CommandService.js.map
packages/app-cli/tests/services_InteropService.d.ts
packages/app-cli/tests/services_InteropService.js
packages/app-cli/tests/services_InteropService.js.map
packages/app-cli/tests/services_InteropService_Exporter_Html.d.ts
packages/app-cli/tests/services_InteropService_Exporter_Html.js
packages/app-cli/tests/services_InteropService_Exporter_Html.js.map
packages/app-cli/tests/services_PluginService.d.ts
packages/app-cli/tests/services_PluginService.js
packages/app-cli/tests/services_PluginService.js.map
packages/app-cli/tests/services_ResourceService.d.ts
packages/app-cli/tests/services_ResourceService.js
packages/app-cli/tests/services_ResourceService.js.map
packages/app-cli/tests/services_SearchEngineUtils.d.ts
packages/app-cli/tests/services_SearchEngineUtils.js
packages/app-cli/tests/services_SearchEngineUtils.js.map
packages/app-cli/tests/services_keychainService.d.ts
packages/app-cli/tests/services_keychainService.js
packages/app-cli/tests/services_keychainService.js.map
packages/app-cli/tests/services_rest_Api.d.ts
packages/app-cli/tests/services_rest_Api.js
packages/app-cli/tests/services_rest_Api.js.map
packages/app-cli/tests/synchronizer_LockHandler.d.ts
packages/app-cli/tests/synchronizer_LockHandler.js
packages/app-cli/tests/synchronizer_LockHandler.js.map
packages/app-cli/tests/synchronizer_MigrationHandler.d.ts
packages/app-cli/tests/synchronizer_MigrationHandler.js
packages/app-cli/tests/synchronizer_MigrationHandler.js.map
packages/app-cli/tests/test-utils-synchronizer.d.ts
packages/app-cli/tests/test-utils-synchronizer.js
packages/app-cli/tests/test-utils-synchronizer.js.map
packages/app-cli/tests/test-utils.d.ts
packages/app-cli/tests/test-utils.js
packages/app-cli/tests/test-utils.js.map
packages/app-desktop/ElectronAppWrapper.d.ts
packages/app-desktop/ElectronAppWrapper.js
packages/app-desktop/ElectronAppWrapper.js.map
@@ -788,9 +701,9 @@ packages/app-mobile/services/AlarmServiceDriver.android.js.map
packages/app-mobile/services/AlarmServiceDriver.ios.d.ts
packages/app-mobile/services/AlarmServiceDriver.ios.js
packages/app-mobile/services/AlarmServiceDriver.ios.js.map
packages/app-mobile/setUpQuickActions.d.ts
packages/app-mobile/setUpQuickActions.js
packages/app-mobile/setUpQuickActions.js.map
packages/app-mobile/setupQuickActions.d.ts
packages/app-mobile/setupQuickActions.js
packages/app-mobile/setupQuickActions.js.map
packages/app-mobile/utils/ShareExtension.d.ts
packages/app-mobile/utils/ShareExtension.js
packages/app-mobile/utils/ShareExtension.js.map
@@ -806,6 +719,9 @@ packages/app-mobile/utils/checkPermissions.js.map
packages/app-mobile/utils/fs-driver-rn.d.ts
packages/app-mobile/utils/fs-driver-rn.js
packages/app-mobile/utils/fs-driver-rn.js.map
packages/app-mobile/utils/setupNotifications.d.ts
packages/app-mobile/utils/setupNotifications.js
packages/app-mobile/utils/setupNotifications.js.map
packages/app-mobile/utils/shareHandler.d.ts
packages/app-mobile/utils/shareHandler.js
packages/app-mobile/utils/shareHandler.js.map
@@ -881,6 +797,9 @@ packages/lib/HtmlToMd.js.map
packages/lib/InMemoryCache.d.ts
packages/lib/InMemoryCache.js
packages/lib/InMemoryCache.js.map
packages/lib/InMemoryCache.test.d.ts
packages/lib/InMemoryCache.test.js
packages/lib/InMemoryCache.test.js.map
packages/lib/JoplinDatabase.d.ts
packages/lib/JoplinDatabase.js
packages/lib/JoplinDatabase.js.map
@@ -932,6 +851,9 @@ packages/lib/eventManager.js.map
packages/lib/file-api-driver-joplinServer.d.ts
packages/lib/file-api-driver-joplinServer.js
packages/lib/file-api-driver-joplinServer.js.map
packages/lib/file-api-driver.test.d.ts
packages/lib/file-api-driver.test.js
packages/lib/file-api-driver.test.js.map
packages/lib/file-api.d.ts
packages/lib/file-api.js
packages/lib/file-api.js.map
@@ -941,12 +863,24 @@ packages/lib/fs-driver-base.js.map
packages/lib/fs-driver-node.d.ts
packages/lib/fs-driver-node.js
packages/lib/fs-driver-node.js.map
packages/lib/fsDriver.test.d.ts
packages/lib/fsDriver.test.js
packages/lib/fsDriver.test.js.map
packages/lib/htmlUtils.d.ts
packages/lib/htmlUtils.js
packages/lib/htmlUtils.js.map
packages/lib/htmlUtils.test.d.ts
packages/lib/htmlUtils.test.js
packages/lib/htmlUtils.test.js.map
packages/lib/htmlUtils2.test.d.ts
packages/lib/htmlUtils2.test.js
packages/lib/htmlUtils2.test.js.map
packages/lib/import-enex-md-gen.d.ts
packages/lib/import-enex-md-gen.js
packages/lib/import-enex-md-gen.js.map
packages/lib/import-enex-md-gen.test.d.ts
packages/lib/import-enex-md-gen.test.js
packages/lib/import-enex-md-gen.test.js.map
packages/lib/locale.d.ts
packages/lib/locale.js
packages/lib/locale.js.map
@@ -956,6 +890,9 @@ packages/lib/markdownUtils.js.map
packages/lib/markdownUtils.test.d.ts
packages/lib/markdownUtils.test.js
packages/lib/markdownUtils.test.js.map
packages/lib/markdownUtils2.test.d.ts
packages/lib/markdownUtils2.test.js
packages/lib/markdownUtils2.test.js.map
packages/lib/markupLanguageUtils.d.ts
packages/lib/markupLanguageUtils.js
packages/lib/markupLanguageUtils.js.map
@@ -968,6 +905,12 @@ packages/lib/models/BaseItem.js.map
packages/lib/models/Folder.d.ts
packages/lib/models/Folder.js
packages/lib/models/Folder.js.map
packages/lib/models/Folder.sharing.test.d.ts
packages/lib/models/Folder.sharing.test.js
packages/lib/models/Folder.sharing.test.js.map
packages/lib/models/Folder.test.d.ts
packages/lib/models/Folder.test.js
packages/lib/models/Folder.test.js.map
packages/lib/models/ItemChange.d.ts
packages/lib/models/ItemChange.js
packages/lib/models/ItemChange.js.map
@@ -980,6 +923,9 @@ packages/lib/models/Migration.js.map
packages/lib/models/Note.d.ts
packages/lib/models/Note.js
packages/lib/models/Note.js.map
packages/lib/models/Note.test.d.ts
packages/lib/models/Note.test.js
packages/lib/models/Note.test.js.map
packages/lib/models/NoteResource.d.ts
packages/lib/models/NoteResource.js
packages/lib/models/NoteResource.js.map
@@ -1001,12 +947,18 @@ packages/lib/models/Search.js.map
packages/lib/models/Setting.d.ts
packages/lib/models/Setting.js
packages/lib/models/Setting.js.map
packages/lib/models/Setting.test.d.ts
packages/lib/models/Setting.test.js
packages/lib/models/Setting.test.js.map
packages/lib/models/SmartFilter.d.ts
packages/lib/models/SmartFilter.js
packages/lib/models/SmartFilter.js.map
packages/lib/models/Tag.d.ts
packages/lib/models/Tag.js
packages/lib/models/Tag.js.map
packages/lib/models/dateTimeFormats.test.d.ts
packages/lib/models/dateTimeFormats.test.js
packages/lib/models/dateTimeFormats.test.js.map
packages/lib/models/settings/FileHandler.d.ts
packages/lib/models/settings/FileHandler.js
packages/lib/models/settings/FileHandler.js.map
@@ -1034,6 +986,9 @@ packages/lib/reducer.js.map
packages/lib/registry.d.ts
packages/lib/registry.js
packages/lib/registry.js.map
packages/lib/registry.test.d.ts
packages/lib/registry.test.js
packages/lib/registry.test.js.map
packages/lib/services/AlarmService.d.ts
packages/lib/services/AlarmService.js
packages/lib/services/AlarmService.js.map
@@ -1046,6 +1001,9 @@ packages/lib/services/BaseService.js.map
packages/lib/services/CommandService.d.ts
packages/lib/services/CommandService.js
packages/lib/services/CommandService.js.map
packages/lib/services/CommandService.test.d.ts
packages/lib/services/CommandService.test.js
packages/lib/services/CommandService.test.js.map
packages/lib/services/DecryptionWorker.d.ts
packages/lib/services/DecryptionWorker.js
packages/lib/services/DecryptionWorker.js.map
@@ -1094,6 +1052,9 @@ packages/lib/services/ResourceFetcher.js.map
packages/lib/services/ResourceService.d.ts
packages/lib/services/ResourceService.js
packages/lib/services/ResourceService.js.map
packages/lib/services/ResourceService.test.d.ts
packages/lib/services/ResourceService.test.js
packages/lib/services/ResourceService.test.js.map
packages/lib/services/RevisionService.d.ts
packages/lib/services/RevisionService.js
packages/lib/services/RevisionService.js.map
@@ -1142,6 +1103,9 @@ packages/lib/services/debug/populateDatabase.js.map
packages/lib/services/interop/InteropService.d.ts
packages/lib/services/interop/InteropService.js
packages/lib/services/interop/InteropService.js.map
packages/lib/services/interop/InteropService.test.d.ts
packages/lib/services/interop/InteropService.test.js
packages/lib/services/interop/InteropService.test.js.map
packages/lib/services/interop/InteropService_Exporter_Base.d.ts
packages/lib/services/interop/InteropService_Exporter_Base.js
packages/lib/services/interop/InteropService_Exporter_Base.js.map
@@ -1151,6 +1115,9 @@ packages/lib/services/interop/InteropService_Exporter_Custom.js.map
packages/lib/services/interop/InteropService_Exporter_Html.d.ts
packages/lib/services/interop/InteropService_Exporter_Html.js
packages/lib/services/interop/InteropService_Exporter_Html.js.map
packages/lib/services/interop/InteropService_Exporter_Html.test.d.ts
packages/lib/services/interop/InteropService_Exporter_Html.test.js
packages/lib/services/interop/InteropService_Exporter_Html.test.js.map
packages/lib/services/interop/InteropService_Exporter_Jex.d.ts
packages/lib/services/interop/InteropService_Exporter_Jex.js
packages/lib/services/interop/InteropService_Exporter_Jex.js.map
@@ -1199,6 +1166,9 @@ packages/lib/services/keychain/KeychainServiceDriver.node.js.map
packages/lib/services/keychain/KeychainServiceDriverBase.d.ts
packages/lib/services/keychain/KeychainServiceDriverBase.js
packages/lib/services/keychain/KeychainServiceDriverBase.js.map
packages/lib/services/keychain/keychainService.test.d.ts
packages/lib/services/keychain/keychainService.test.js
packages/lib/services/keychain/keychainService.test.js.map
packages/lib/services/plugins/BasePluginRunner.d.ts
packages/lib/services/plugins/BasePluginRunner.js
packages/lib/services/plugins/BasePluginRunner.js.map
@@ -1313,6 +1283,9 @@ packages/lib/services/plugins/utils/validatePluginId.test.js.map
packages/lib/services/rest/Api.d.ts
packages/lib/services/rest/Api.js
packages/lib/services/rest/Api.js.map
packages/lib/services/rest/Api.test.d.ts
packages/lib/services/rest/Api.test.js
packages/lib/services/rest/Api.test.js.map
packages/lib/services/rest/ApiResponse.d.ts
packages/lib/services/rest/ApiResponse.js
packages/lib/services/rest/ApiResponse.js.map
@@ -1373,6 +1346,9 @@ packages/lib/services/searchengine/SearchEngine.js.map
packages/lib/services/searchengine/SearchEngineUtils.d.ts
packages/lib/services/searchengine/SearchEngineUtils.js
packages/lib/services/searchengine/SearchEngineUtils.js.map
packages/lib/services/searchengine/SearchEngineUtils.test.d.ts
packages/lib/services/searchengine/SearchEngineUtils.test.js
packages/lib/services/searchengine/SearchEngineUtils.test.js.map
packages/lib/services/searchengine/filterParser.d.ts
packages/lib/services/searchengine/filterParser.js
packages/lib/services/searchengine/filterParser.js.map
@@ -1397,6 +1373,30 @@ packages/lib/services/synchronizer/LockHandler.js.map
packages/lib/services/synchronizer/MigrationHandler.d.ts
packages/lib/services/synchronizer/MigrationHandler.js
packages/lib/services/synchronizer/MigrationHandler.js.map
packages/lib/services/synchronizer/Synchronizer.basics.test.d.ts
packages/lib/services/synchronizer/Synchronizer.basics.test.js
packages/lib/services/synchronizer/Synchronizer.basics.test.js.map
packages/lib/services/synchronizer/Synchronizer.conflicts.test.d.ts
packages/lib/services/synchronizer/Synchronizer.conflicts.test.js
packages/lib/services/synchronizer/Synchronizer.conflicts.test.js.map
packages/lib/services/synchronizer/Synchronizer.e2ee.test.d.ts
packages/lib/services/synchronizer/Synchronizer.e2ee.test.js
packages/lib/services/synchronizer/Synchronizer.e2ee.test.js.map
packages/lib/services/synchronizer/Synchronizer.resources.test.d.ts
packages/lib/services/synchronizer/Synchronizer.resources.test.js
packages/lib/services/synchronizer/Synchronizer.resources.test.js.map
packages/lib/services/synchronizer/Synchronizer.revisions.test.d.ts
packages/lib/services/synchronizer/Synchronizer.revisions.test.js
packages/lib/services/synchronizer/Synchronizer.revisions.test.js.map
packages/lib/services/synchronizer/Synchronizer.sharing.test.d.ts
packages/lib/services/synchronizer/Synchronizer.sharing.test.js
packages/lib/services/synchronizer/Synchronizer.sharing.test.js.map
packages/lib/services/synchronizer/Synchronizer.tags.test.d.ts
packages/lib/services/synchronizer/Synchronizer.tags.test.js
packages/lib/services/synchronizer/Synchronizer.tags.test.js.map
packages/lib/services/synchronizer/Synchronizer.tools.test.d.ts
packages/lib/services/synchronizer/Synchronizer.tools.test.js
packages/lib/services/synchronizer/Synchronizer.tools.test.js.map
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.d.ts
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js.map
@@ -1406,6 +1406,12 @@ packages/lib/services/synchronizer/migrations/1.js.map
packages/lib/services/synchronizer/migrations/2.d.ts
packages/lib/services/synchronizer/migrations/2.js
packages/lib/services/synchronizer/migrations/2.js.map
packages/lib/services/synchronizer/synchronizer_LockHandler.test.d.ts
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js.map
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.d.ts
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js.map
packages/lib/services/synchronizer/tools.d.ts
packages/lib/services/synchronizer/tools.js
packages/lib/services/synchronizer/tools.js.map
@@ -1418,6 +1424,12 @@ packages/lib/services/synchronizer/utils/types.js.map
packages/lib/shim.d.ts
packages/lib/shim.js
packages/lib/shim.js.map
packages/lib/testing/test-utils-synchronizer.d.ts
packages/lib/testing/test-utils-synchronizer.js
packages/lib/testing/test-utils-synchronizer.js.map
packages/lib/testing/test-utils.d.ts
packages/lib/testing/test-utils.js
packages/lib/testing/test-utils.js.map
packages/lib/theme.d.ts
packages/lib/theme.js
packages/lib/theme.js.map
@@ -1451,6 +1463,9 @@ packages/lib/themes/type.js.map
packages/lib/time.d.ts
packages/lib/time.js
packages/lib/time.js.map
packages/lib/utils/credentialFiles.d.ts
packages/lib/utils/credentialFiles.js
packages/lib/utils/credentialFiles.js.map
packages/lib/uuid.d.ts
packages/lib/uuid.js
packages/lib/uuid.js.map

207
.gitignore vendored
View File

@@ -60,69 +60,18 @@ packages/app-cli/app/command-settingschema.js.map
packages/app-cli/app/services/plugins/PluginRunner.d.ts
packages/app-cli/app/services/plugins/PluginRunner.js
packages/app-cli/app/services/plugins/PluginRunner.js.map
packages/app-cli/tests/EnexToMd.d.ts
packages/app-cli/tests/EnexToMd.js
packages/app-cli/tests/EnexToMd.js.map
packages/app-cli/tests/HtmlToMd.d.ts
packages/app-cli/tests/HtmlToMd.js
packages/app-cli/tests/HtmlToMd.js.map
packages/app-cli/tests/InMemoryCache.d.ts
packages/app-cli/tests/InMemoryCache.js
packages/app-cli/tests/InMemoryCache.js.map
packages/app-cli/tests/MdToHtml.d.ts
packages/app-cli/tests/MdToHtml.js
packages/app-cli/tests/MdToHtml.js.map
packages/app-cli/tests/Synchronizer.basics.d.ts
packages/app-cli/tests/Synchronizer.basics.js
packages/app-cli/tests/Synchronizer.basics.js.map
packages/app-cli/tests/Synchronizer.conflicts.d.ts
packages/app-cli/tests/Synchronizer.conflicts.js
packages/app-cli/tests/Synchronizer.conflicts.js.map
packages/app-cli/tests/Synchronizer.e2ee.d.ts
packages/app-cli/tests/Synchronizer.e2ee.js
packages/app-cli/tests/Synchronizer.e2ee.js.map
packages/app-cli/tests/Synchronizer.resources.d.ts
packages/app-cli/tests/Synchronizer.resources.js
packages/app-cli/tests/Synchronizer.resources.js.map
packages/app-cli/tests/Synchronizer.revisions.d.ts
packages/app-cli/tests/Synchronizer.revisions.js
packages/app-cli/tests/Synchronizer.revisions.js.map
packages/app-cli/tests/Synchronizer.sharing.d.ts
packages/app-cli/tests/Synchronizer.sharing.js
packages/app-cli/tests/Synchronizer.sharing.js.map
packages/app-cli/tests/Synchronizer.tags.d.ts
packages/app-cli/tests/Synchronizer.tags.js
packages/app-cli/tests/Synchronizer.tags.js.map
packages/app-cli/tests/Synchronizer.tools.d.ts
packages/app-cli/tests/Synchronizer.tools.js
packages/app-cli/tests/Synchronizer.tools.js.map
packages/app-cli/tests/dateTimeFormats.d.ts
packages/app-cli/tests/dateTimeFormats.js
packages/app-cli/tests/dateTimeFormats.js.map
packages/app-cli/tests/file-api-driver.d.ts
packages/app-cli/tests/file-api-driver.js
packages/app-cli/tests/file-api-driver.js.map
packages/app-cli/tests/fsDriver.d.ts
packages/app-cli/tests/fsDriver.js
packages/app-cli/tests/fsDriver.js.map
packages/app-cli/tests/htmlUtils.d.ts
packages/app-cli/tests/htmlUtils.js
packages/app-cli/tests/htmlUtils.js.map
packages/app-cli/tests/models_Folder.d.ts
packages/app-cli/tests/models_Folder.js
packages/app-cli/tests/models_Folder.js.map
packages/app-cli/tests/models_Folder.sharing.d.ts
packages/app-cli/tests/models_Folder.sharing.js
packages/app-cli/tests/models_Folder.sharing.js.map
packages/app-cli/tests/models_Note.d.ts
packages/app-cli/tests/models_Note.js
packages/app-cli/tests/models_Note.js.map
packages/app-cli/tests/models_Setting.d.ts
packages/app-cli/tests/models_Setting.js
packages/app-cli/tests/models_Setting.js.map
packages/app-cli/tests/registry.d.ts
packages/app-cli/tests/registry.js
packages/app-cli/tests/registry.js.map
packages/app-cli/tests/MdToMd.d.ts
packages/app-cli/tests/MdToMd.js
packages/app-cli/tests/MdToMd.js.map
packages/app-cli/tests/services/plugins/PluginService.d.ts
packages/app-cli/tests/services/plugins/PluginService.js
packages/app-cli/tests/services/plugins/PluginService.js.map
packages/app-cli/tests/services/plugins/RepositoryApi.d.ts
packages/app-cli/tests/services/plugins/RepositoryApi.js
packages/app-cli/tests/services/plugins/RepositoryApi.js.map
@@ -138,42 +87,6 @@ packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js.map
packages/app-cli/tests/services/plugins/sandboxProxy.d.ts
packages/app-cli/tests/services/plugins/sandboxProxy.js
packages/app-cli/tests/services/plugins/sandboxProxy.js.map
packages/app-cli/tests/services_CommandService.d.ts
packages/app-cli/tests/services_CommandService.js
packages/app-cli/tests/services_CommandService.js.map
packages/app-cli/tests/services_InteropService.d.ts
packages/app-cli/tests/services_InteropService.js
packages/app-cli/tests/services_InteropService.js.map
packages/app-cli/tests/services_InteropService_Exporter_Html.d.ts
packages/app-cli/tests/services_InteropService_Exporter_Html.js
packages/app-cli/tests/services_InteropService_Exporter_Html.js.map
packages/app-cli/tests/services_PluginService.d.ts
packages/app-cli/tests/services_PluginService.js
packages/app-cli/tests/services_PluginService.js.map
packages/app-cli/tests/services_ResourceService.d.ts
packages/app-cli/tests/services_ResourceService.js
packages/app-cli/tests/services_ResourceService.js.map
packages/app-cli/tests/services_SearchEngineUtils.d.ts
packages/app-cli/tests/services_SearchEngineUtils.js
packages/app-cli/tests/services_SearchEngineUtils.js.map
packages/app-cli/tests/services_keychainService.d.ts
packages/app-cli/tests/services_keychainService.js
packages/app-cli/tests/services_keychainService.js.map
packages/app-cli/tests/services_rest_Api.d.ts
packages/app-cli/tests/services_rest_Api.js
packages/app-cli/tests/services_rest_Api.js.map
packages/app-cli/tests/synchronizer_LockHandler.d.ts
packages/app-cli/tests/synchronizer_LockHandler.js
packages/app-cli/tests/synchronizer_LockHandler.js.map
packages/app-cli/tests/synchronizer_MigrationHandler.d.ts
packages/app-cli/tests/synchronizer_MigrationHandler.js
packages/app-cli/tests/synchronizer_MigrationHandler.js.map
packages/app-cli/tests/test-utils-synchronizer.d.ts
packages/app-cli/tests/test-utils-synchronizer.js
packages/app-cli/tests/test-utils-synchronizer.js.map
packages/app-cli/tests/test-utils.d.ts
packages/app-cli/tests/test-utils.js
packages/app-cli/tests/test-utils.js.map
packages/app-desktop/ElectronAppWrapper.d.ts
packages/app-desktop/ElectronAppWrapper.js
packages/app-desktop/ElectronAppWrapper.js.map
@@ -774,9 +687,9 @@ packages/app-mobile/services/AlarmServiceDriver.android.js.map
packages/app-mobile/services/AlarmServiceDriver.ios.d.ts
packages/app-mobile/services/AlarmServiceDriver.ios.js
packages/app-mobile/services/AlarmServiceDriver.ios.js.map
packages/app-mobile/setUpQuickActions.d.ts
packages/app-mobile/setUpQuickActions.js
packages/app-mobile/setUpQuickActions.js.map
packages/app-mobile/setupQuickActions.d.ts
packages/app-mobile/setupQuickActions.js
packages/app-mobile/setupQuickActions.js.map
packages/app-mobile/utils/ShareExtension.d.ts
packages/app-mobile/utils/ShareExtension.js
packages/app-mobile/utils/ShareExtension.js.map
@@ -792,6 +705,9 @@ packages/app-mobile/utils/checkPermissions.js.map
packages/app-mobile/utils/fs-driver-rn.d.ts
packages/app-mobile/utils/fs-driver-rn.js
packages/app-mobile/utils/fs-driver-rn.js.map
packages/app-mobile/utils/setupNotifications.d.ts
packages/app-mobile/utils/setupNotifications.js
packages/app-mobile/utils/setupNotifications.js.map
packages/app-mobile/utils/shareHandler.d.ts
packages/app-mobile/utils/shareHandler.js
packages/app-mobile/utils/shareHandler.js.map
@@ -867,6 +783,9 @@ packages/lib/HtmlToMd.js.map
packages/lib/InMemoryCache.d.ts
packages/lib/InMemoryCache.js
packages/lib/InMemoryCache.js.map
packages/lib/InMemoryCache.test.d.ts
packages/lib/InMemoryCache.test.js
packages/lib/InMemoryCache.test.js.map
packages/lib/JoplinDatabase.d.ts
packages/lib/JoplinDatabase.js
packages/lib/JoplinDatabase.js.map
@@ -918,6 +837,9 @@ packages/lib/eventManager.js.map
packages/lib/file-api-driver-joplinServer.d.ts
packages/lib/file-api-driver-joplinServer.js
packages/lib/file-api-driver-joplinServer.js.map
packages/lib/file-api-driver.test.d.ts
packages/lib/file-api-driver.test.js
packages/lib/file-api-driver.test.js.map
packages/lib/file-api.d.ts
packages/lib/file-api.js
packages/lib/file-api.js.map
@@ -927,12 +849,24 @@ packages/lib/fs-driver-base.js.map
packages/lib/fs-driver-node.d.ts
packages/lib/fs-driver-node.js
packages/lib/fs-driver-node.js.map
packages/lib/fsDriver.test.d.ts
packages/lib/fsDriver.test.js
packages/lib/fsDriver.test.js.map
packages/lib/htmlUtils.d.ts
packages/lib/htmlUtils.js
packages/lib/htmlUtils.js.map
packages/lib/htmlUtils.test.d.ts
packages/lib/htmlUtils.test.js
packages/lib/htmlUtils.test.js.map
packages/lib/htmlUtils2.test.d.ts
packages/lib/htmlUtils2.test.js
packages/lib/htmlUtils2.test.js.map
packages/lib/import-enex-md-gen.d.ts
packages/lib/import-enex-md-gen.js
packages/lib/import-enex-md-gen.js.map
packages/lib/import-enex-md-gen.test.d.ts
packages/lib/import-enex-md-gen.test.js
packages/lib/import-enex-md-gen.test.js.map
packages/lib/locale.d.ts
packages/lib/locale.js
packages/lib/locale.js.map
@@ -942,6 +876,9 @@ packages/lib/markdownUtils.js.map
packages/lib/markdownUtils.test.d.ts
packages/lib/markdownUtils.test.js
packages/lib/markdownUtils.test.js.map
packages/lib/markdownUtils2.test.d.ts
packages/lib/markdownUtils2.test.js
packages/lib/markdownUtils2.test.js.map
packages/lib/markupLanguageUtils.d.ts
packages/lib/markupLanguageUtils.js
packages/lib/markupLanguageUtils.js.map
@@ -954,6 +891,12 @@ packages/lib/models/BaseItem.js.map
packages/lib/models/Folder.d.ts
packages/lib/models/Folder.js
packages/lib/models/Folder.js.map
packages/lib/models/Folder.sharing.test.d.ts
packages/lib/models/Folder.sharing.test.js
packages/lib/models/Folder.sharing.test.js.map
packages/lib/models/Folder.test.d.ts
packages/lib/models/Folder.test.js
packages/lib/models/Folder.test.js.map
packages/lib/models/ItemChange.d.ts
packages/lib/models/ItemChange.js
packages/lib/models/ItemChange.js.map
@@ -966,6 +909,9 @@ packages/lib/models/Migration.js.map
packages/lib/models/Note.d.ts
packages/lib/models/Note.js
packages/lib/models/Note.js.map
packages/lib/models/Note.test.d.ts
packages/lib/models/Note.test.js
packages/lib/models/Note.test.js.map
packages/lib/models/NoteResource.d.ts
packages/lib/models/NoteResource.js
packages/lib/models/NoteResource.js.map
@@ -987,12 +933,18 @@ packages/lib/models/Search.js.map
packages/lib/models/Setting.d.ts
packages/lib/models/Setting.js
packages/lib/models/Setting.js.map
packages/lib/models/Setting.test.d.ts
packages/lib/models/Setting.test.js
packages/lib/models/Setting.test.js.map
packages/lib/models/SmartFilter.d.ts
packages/lib/models/SmartFilter.js
packages/lib/models/SmartFilter.js.map
packages/lib/models/Tag.d.ts
packages/lib/models/Tag.js
packages/lib/models/Tag.js.map
packages/lib/models/dateTimeFormats.test.d.ts
packages/lib/models/dateTimeFormats.test.js
packages/lib/models/dateTimeFormats.test.js.map
packages/lib/models/settings/FileHandler.d.ts
packages/lib/models/settings/FileHandler.js
packages/lib/models/settings/FileHandler.js.map
@@ -1020,6 +972,9 @@ packages/lib/reducer.js.map
packages/lib/registry.d.ts
packages/lib/registry.js
packages/lib/registry.js.map
packages/lib/registry.test.d.ts
packages/lib/registry.test.js
packages/lib/registry.test.js.map
packages/lib/services/AlarmService.d.ts
packages/lib/services/AlarmService.js
packages/lib/services/AlarmService.js.map
@@ -1032,6 +987,9 @@ packages/lib/services/BaseService.js.map
packages/lib/services/CommandService.d.ts
packages/lib/services/CommandService.js
packages/lib/services/CommandService.js.map
packages/lib/services/CommandService.test.d.ts
packages/lib/services/CommandService.test.js
packages/lib/services/CommandService.test.js.map
packages/lib/services/DecryptionWorker.d.ts
packages/lib/services/DecryptionWorker.js
packages/lib/services/DecryptionWorker.js.map
@@ -1080,6 +1038,9 @@ packages/lib/services/ResourceFetcher.js.map
packages/lib/services/ResourceService.d.ts
packages/lib/services/ResourceService.js
packages/lib/services/ResourceService.js.map
packages/lib/services/ResourceService.test.d.ts
packages/lib/services/ResourceService.test.js
packages/lib/services/ResourceService.test.js.map
packages/lib/services/RevisionService.d.ts
packages/lib/services/RevisionService.js
packages/lib/services/RevisionService.js.map
@@ -1128,6 +1089,9 @@ packages/lib/services/debug/populateDatabase.js.map
packages/lib/services/interop/InteropService.d.ts
packages/lib/services/interop/InteropService.js
packages/lib/services/interop/InteropService.js.map
packages/lib/services/interop/InteropService.test.d.ts
packages/lib/services/interop/InteropService.test.js
packages/lib/services/interop/InteropService.test.js.map
packages/lib/services/interop/InteropService_Exporter_Base.d.ts
packages/lib/services/interop/InteropService_Exporter_Base.js
packages/lib/services/interop/InteropService_Exporter_Base.js.map
@@ -1137,6 +1101,9 @@ packages/lib/services/interop/InteropService_Exporter_Custom.js.map
packages/lib/services/interop/InteropService_Exporter_Html.d.ts
packages/lib/services/interop/InteropService_Exporter_Html.js
packages/lib/services/interop/InteropService_Exporter_Html.js.map
packages/lib/services/interop/InteropService_Exporter_Html.test.d.ts
packages/lib/services/interop/InteropService_Exporter_Html.test.js
packages/lib/services/interop/InteropService_Exporter_Html.test.js.map
packages/lib/services/interop/InteropService_Exporter_Jex.d.ts
packages/lib/services/interop/InteropService_Exporter_Jex.js
packages/lib/services/interop/InteropService_Exporter_Jex.js.map
@@ -1185,6 +1152,9 @@ packages/lib/services/keychain/KeychainServiceDriver.node.js.map
packages/lib/services/keychain/KeychainServiceDriverBase.d.ts
packages/lib/services/keychain/KeychainServiceDriverBase.js
packages/lib/services/keychain/KeychainServiceDriverBase.js.map
packages/lib/services/keychain/keychainService.test.d.ts
packages/lib/services/keychain/keychainService.test.js
packages/lib/services/keychain/keychainService.test.js.map
packages/lib/services/plugins/BasePluginRunner.d.ts
packages/lib/services/plugins/BasePluginRunner.js
packages/lib/services/plugins/BasePluginRunner.js.map
@@ -1299,6 +1269,9 @@ packages/lib/services/plugins/utils/validatePluginId.test.js.map
packages/lib/services/rest/Api.d.ts
packages/lib/services/rest/Api.js
packages/lib/services/rest/Api.js.map
packages/lib/services/rest/Api.test.d.ts
packages/lib/services/rest/Api.test.js
packages/lib/services/rest/Api.test.js.map
packages/lib/services/rest/ApiResponse.d.ts
packages/lib/services/rest/ApiResponse.js
packages/lib/services/rest/ApiResponse.js.map
@@ -1359,6 +1332,9 @@ packages/lib/services/searchengine/SearchEngine.js.map
packages/lib/services/searchengine/SearchEngineUtils.d.ts
packages/lib/services/searchengine/SearchEngineUtils.js
packages/lib/services/searchengine/SearchEngineUtils.js.map
packages/lib/services/searchengine/SearchEngineUtils.test.d.ts
packages/lib/services/searchengine/SearchEngineUtils.test.js
packages/lib/services/searchengine/SearchEngineUtils.test.js.map
packages/lib/services/searchengine/filterParser.d.ts
packages/lib/services/searchengine/filterParser.js
packages/lib/services/searchengine/filterParser.js.map
@@ -1383,6 +1359,30 @@ packages/lib/services/synchronizer/LockHandler.js.map
packages/lib/services/synchronizer/MigrationHandler.d.ts
packages/lib/services/synchronizer/MigrationHandler.js
packages/lib/services/synchronizer/MigrationHandler.js.map
packages/lib/services/synchronizer/Synchronizer.basics.test.d.ts
packages/lib/services/synchronizer/Synchronizer.basics.test.js
packages/lib/services/synchronizer/Synchronizer.basics.test.js.map
packages/lib/services/synchronizer/Synchronizer.conflicts.test.d.ts
packages/lib/services/synchronizer/Synchronizer.conflicts.test.js
packages/lib/services/synchronizer/Synchronizer.conflicts.test.js.map
packages/lib/services/synchronizer/Synchronizer.e2ee.test.d.ts
packages/lib/services/synchronizer/Synchronizer.e2ee.test.js
packages/lib/services/synchronizer/Synchronizer.e2ee.test.js.map
packages/lib/services/synchronizer/Synchronizer.resources.test.d.ts
packages/lib/services/synchronizer/Synchronizer.resources.test.js
packages/lib/services/synchronizer/Synchronizer.resources.test.js.map
packages/lib/services/synchronizer/Synchronizer.revisions.test.d.ts
packages/lib/services/synchronizer/Synchronizer.revisions.test.js
packages/lib/services/synchronizer/Synchronizer.revisions.test.js.map
packages/lib/services/synchronizer/Synchronizer.sharing.test.d.ts
packages/lib/services/synchronizer/Synchronizer.sharing.test.js
packages/lib/services/synchronizer/Synchronizer.sharing.test.js.map
packages/lib/services/synchronizer/Synchronizer.tags.test.d.ts
packages/lib/services/synchronizer/Synchronizer.tags.test.js
packages/lib/services/synchronizer/Synchronizer.tags.test.js.map
packages/lib/services/synchronizer/Synchronizer.tools.test.d.ts
packages/lib/services/synchronizer/Synchronizer.tools.test.js
packages/lib/services/synchronizer/Synchronizer.tools.test.js.map
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.d.ts
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js.map
@@ -1392,6 +1392,12 @@ packages/lib/services/synchronizer/migrations/1.js.map
packages/lib/services/synchronizer/migrations/2.d.ts
packages/lib/services/synchronizer/migrations/2.js
packages/lib/services/synchronizer/migrations/2.js.map
packages/lib/services/synchronizer/synchronizer_LockHandler.test.d.ts
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js.map
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.d.ts
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js.map
packages/lib/services/synchronizer/tools.d.ts
packages/lib/services/synchronizer/tools.js
packages/lib/services/synchronizer/tools.js.map
@@ -1404,6 +1410,12 @@ packages/lib/services/synchronizer/utils/types.js.map
packages/lib/shim.d.ts
packages/lib/shim.js
packages/lib/shim.js.map
packages/lib/testing/test-utils-synchronizer.d.ts
packages/lib/testing/test-utils-synchronizer.js
packages/lib/testing/test-utils-synchronizer.js.map
packages/lib/testing/test-utils.d.ts
packages/lib/testing/test-utils.js
packages/lib/testing/test-utils.js.map
packages/lib/theme.d.ts
packages/lib/theme.js
packages/lib/theme.js.map
@@ -1437,6 +1449,9 @@ packages/lib/themes/type.js.map
packages/lib/time.d.ts
packages/lib/time.js
packages/lib/time.js.map
packages/lib/utils/credentialFiles.d.ts
packages/lib/utils/credentialFiles.js
packages/lib/utils/credentialFiles.js.map
packages/lib/uuid.d.ts
packages/lib/uuid.js
packages/lib/uuid.js.map

View File

@@ -292,7 +292,7 @@ To add a **Bucket Policy** from the AWS S3 Web Console, navigate to the **Permis
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:PutObject"
]
],
"Resource": [
"arn:aws:s3:::joplin-bucket",
"arn:aws:s3:::joplin-bucket/*"
@@ -342,7 +342,7 @@ In the desktop and mobile apps, an alarm can be associated with any to-do. It wi
- **Windows**: >= 8. Make sure the Action Center is enabled on Windows. Task bar balloon for Windows < 8. Growl as fallback. Growl takes precedence over Windows balloons.
- **macOS**: >= 10.8 or Growl if earlier.
- **Linux**: `notify-osd` or `libnotify-bin` installed (Ubuntu should have this by default). Growl otherwise
- **Linux**: `notify-send` tool, delivered through packages `notify-osd`, `libnotify-bin` or `libnotify-tools`. GNOME should have this by default, but install `libnotify-tools` if using KDE Plasma.
See [documentation and flow chart for reporter choice](https://github.com/mikaelbr/node-notifier/blob/master/DECISION_FLOW.md)

View File

@@ -1,4 +1,17 @@
const { afterEachCleanUp } = require('./tests/test-utils.js');
const { afterEachCleanUp } = require('@joplin/lib/testing/test-utils.js');
const { shimInit } = require('@joplin/lib/shim-init-node.js');
const shim = require('@joplin/lib/shim').default;
const sharp = require('sharp');
let keytar;
try {
keytar = shim.platformSupportsKeyChain() ? require('keytar') : null;
} catch (error) {
console.error('Cannot load keytar - keychain support will be disabled', error);
keytar = null;
}
shimInit(sharp, keytar);
global.afterEach(async () => {
await afterEachCleanUp();

View File

@@ -43,7 +43,6 @@
"@joplin/renderer": "1.8",
"aws-sdk": "^2.588.0",
"chalk": "^4.1.0",
"clean-html": "^1.5.0",
"compare-version": "^0.1.2",
"fs-extra": "^5.0.0",
"html-entities": "^1.2.1",

View File

@@ -1,116 +0,0 @@
const { setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
const shim = require('@joplin/lib/shim').default;
const { enexXmlToHtml } = require('@joplin/lib/import-enex-html-gen.js');
const cleanHtml = require('clean-html');
const fileWithPath = (filename) =>
`${__dirname}/enex_to_html/${filename}`;
const audioResource = {
filename: 'audio test',
id: '9168ee833d03c5ea7c730ac6673978c1',
mime: 'audio/x-m4a',
size: 82011,
title: 'audio test',
};
// All the test HTML files are beautified ones, so we need to run
// this before the comparison. Before, beautifying was done by `enexXmlToHtml`
// but that was removed due to problems with the clean-html package.
const beautifyHtml = (html) => {
return new Promise((resolve) => {
try {
cleanHtml.clean(html, { wrap: 0 }, (...cleanedHtml) => resolve(cleanedHtml.join('')));
} catch (error) {
console.warn(`Could not clean HTML - the "unclean" version will be used: ${error.message}: ${html.trim().substr(0, 512).replace(/[\n\r]/g, ' ')}...`);
resolve([html].join(''));
}
});
};
/**
* Tests the importer for a single note, checking that the result of
* processing the given `.enex` input file matches the contents of the given
* `.html` file.
*
* Note that this does not test the importing of an entire exported `.enex`
* archive, but rather a single node of such a file. Thus, the test data files
* (e.g. `./enex_to_html/code1.enex`) correspond to the contents of a single
* `<note>...</note>` node in an `.enex` file already extracted from
* `<content><![CDATA[...]]</content>`.
*/
const compareOutputToExpected = (options) => {
const inputFile = fileWithPath(`${options.testName}.enex`);
const outputFile = fileWithPath(`${options.testName}.html`);
const testTitle = `should convert from Enex to Html: ${options.testName}`;
it(testTitle, (async () => {
const enexInput = await shim.fsDriver().readFile(inputFile);
const expectedOutput = await shim.fsDriver().readFile(outputFile);
const actualOutput = await beautifyHtml(await enexXmlToHtml(enexInput, options.resources));
expect(actualOutput).toEqual(expectedOutput);
}));
};
describe('EnexToHtml', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
compareOutputToExpected({
testName: 'checklist-list',
resources: [],
});
compareOutputToExpected({
testName: 'svg',
resources: [],
});
compareOutputToExpected({
testName: 'en-media--image',
resources: [{
filename: '',
id: '89ce7da62c6b2832929a6964237e98e9', // Mock id
mime: 'image/jpeg',
size: 50347,
title: '',
}],
});
compareOutputToExpected({
testName: 'en-media--audio',
resources: [audioResource],
});
compareOutputToExpected({
testName: 'attachment',
resources: [{
filename: 'attachment-1',
id: '21ca2b948f222a38802940ec7e2e5de3',
mime: 'application/pdf', // Any non-image/non-audio mime type will do
size: 1000,
}],
});
// it('fails when not given a matching resource', (async () => {
// // To test the promise-unexpectedly-resolved case, add `audioResource` to the array.
// const resources = [];
// const inputFile = fileWithPath('en-media--image.enex');
// const enexInput = await shim.fsDriver().readFile(inputFile);
// const promisedOutput = enexXmlToHtml(enexInput, resources);
// promisedOutput.then(() => {
// // Promise should not be resolved
// expect(false).toEqual(true);
// }, (reason) => {
// expect(reason)
// .toBe('Hash with no associated resource: 89ce7da62c6b2832929a6964237e98e9');
// });
// }));
});

View File

@@ -4,7 +4,7 @@
const os = require('os');
const time = require('@joplin/lib/time').default;
const { filename } = require('@joplin/lib/path-utils');
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('@joplin/lib/testing/test-utils.js');
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
const BaseModel = require('@joplin/lib/BaseModel').default;

View File

@@ -1,7 +1,7 @@
import MdToHtml from '@joplin/renderer/MdToHtml';
const os = require('os');
const { filename } = require('@joplin/lib/path-utils');
const { setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient } = require('@joplin/lib/testing/test-utils.js');
import shim from '@joplin/lib/shim';
const { themeStyle } = require('@joplin/lib/theme');
@@ -137,6 +137,14 @@ describe('MdToHtml', function() {
}
}));
it('should render an empty string', (async () => {
const mdToHtml = newTestMdToHtml();
const result = await mdToHtml.render('', null, { splitted: true });
// The TinyMCE component checks for this exact string to apply a hack,
// so make sure it doesn't change from version to version.
expect(result.html).toBe('<div id="rendered-md"></div>');
}));
it('should split HTML and CSS', (async () => {
const mdToHtml = newTestMdToHtml();

View File

@@ -1,6 +1,6 @@
const mdImporterService = require('@joplin/lib/services/interop/InteropService_Importer_Md').default;
const Note = require('@joplin/lib/models/Note').default;
const { setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient } = require('@joplin/lib/testing/test-utils.js');
const importer = new mdImporterService();
@@ -43,4 +43,9 @@ describe('InteropService_Importer_Md: importLocalImages', function() {
const items = await Note.linkedItems(note.body);
expect(items.length).toBe(1);
});
it('should import resources for files', async function() {
const note = await importer.importFile(`${__dirname}/md_to_md/sample-files.md`, 'notebook');
const items = await Note.linkedItems(note.body);
expect(items.length).toBe(4);
});
});

View File

@@ -1,290 +0,0 @@
'use strict';
const __awaiter = (this && this.__awaiter) || function(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function(resolve) { resolve(value); }); }
return new (P || (P = Promise))(function(resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator['throw'](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, '__esModule', { value: true });
const Setting_1 = require('@joplin/lib/models/Setting');
const test_utils_synchronizer_1 = require('./test-utils-synchronizer');
const { syncTargetName, afterAllCleanUp, synchronizerStart, setupDatabaseAndSynchronizer, synchronizer, sleep, switchClient, syncTargetId, fileApi } = require('./test-utils.js');
const Folder_1 = require('@joplin/lib/models/Folder');
const Note_1 = require('@joplin/lib/models/Note');
const BaseItem_1 = require('@joplin/lib/models/BaseItem');
const WelcomeUtils = require('@joplin/lib/WelcomeUtils');
describe('Synchronizer.basics', function() {
beforeEach((done) => __awaiter(this, void 0, void 0, function* () {
yield setupDatabaseAndSynchronizer(1);
yield setupDatabaseAndSynchronizer(2);
yield switchClient(1);
done();
}));
afterAll(() => __awaiter(this, void 0, void 0, function* () {
yield afterAllCleanUp();
}));
it('should create remote items', (() => __awaiter(this, void 0, void 0, function* () {
const folder = yield Folder_1.default.save({ title: 'folder1' });
yield Note_1.default.save({ title: 'un', parent_id: folder.id });
const all = yield test_utils_synchronizer_1.allNotesFolders();
yield synchronizerStart();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
})));
it('should update remote items', (() => __awaiter(this, void 0, void 0, function* () {
const folder = yield Folder_1.default.save({ title: 'folder1' });
const note = yield Note_1.default.save({ title: 'un', parent_id: folder.id });
yield synchronizerStart();
yield Note_1.default.save({ title: 'un UPDATE', id: note.id });
const all = yield test_utils_synchronizer_1.allNotesFolders();
yield synchronizerStart();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
})));
it('should create local items', (() => __awaiter(this, void 0, void 0, function* () {
const folder = yield Folder_1.default.save({ title: 'folder1' });
yield Note_1.default.save({ title: 'un', parent_id: folder.id });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
const all = yield test_utils_synchronizer_1.allNotesFolders();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
})));
it('should update local items', (() => __awaiter(this, void 0, void 0, function* () {
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
const note1 = yield Note_1.default.save({ title: 'un', parent_id: folder1.id });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield sleep(0.1);
let note2 = yield Note_1.default.load(note1.id);
note2.title = 'Updated on client 2';
yield Note_1.default.save(note2);
note2 = yield Note_1.default.load(note2.id);
yield synchronizerStart();
yield switchClient(1);
yield synchronizerStart();
const all = yield test_utils_synchronizer_1.allNotesFolders();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
})));
it('should delete remote notes', (() => __awaiter(this, void 0, void 0, function* () {
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
const note1 = yield Note_1.default.save({ title: 'un', parent_id: folder1.id });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield sleep(0.1);
yield Note_1.default.delete(note1.id);
yield synchronizerStart();
const remotes = yield test_utils_synchronizer_1.remoteNotesAndFolders();
expect(remotes.length).toBe(1);
expect(remotes[0].id).toBe(folder1.id);
const deletedItems = yield BaseItem_1.default.deletedItems(syncTargetId());
expect(deletedItems.length).toBe(0);
})));
it('should not created deleted_items entries for items deleted via sync', (() => __awaiter(this, void 0, void 0, function* () {
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
yield Note_1.default.save({ title: 'un', parent_id: folder1.id });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield Folder_1.default.delete(folder1.id);
yield synchronizerStart();
yield switchClient(1);
yield synchronizerStart();
const deletedItems = yield BaseItem_1.default.deletedItems(syncTargetId());
expect(deletedItems.length).toBe(0);
})));
it('should delete local notes', (() => __awaiter(this, void 0, void 0, function* () {
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
const note1 = yield Note_1.default.save({ title: 'un', parent_id: folder1.id });
const note2 = yield Note_1.default.save({ title: 'deux', parent_id: folder1.id });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield Note_1.default.delete(note1.id);
yield synchronizerStart();
yield switchClient(1);
yield synchronizerStart();
const items = yield test_utils_synchronizer_1.allNotesFolders();
expect(items.length).toBe(2);
const deletedItems = yield BaseItem_1.default.deletedItems(syncTargetId());
expect(deletedItems.length).toBe(0);
yield Note_1.default.delete(note2.id);
yield synchronizerStart();
})));
it('should delete remote folder', (() => __awaiter(this, void 0, void 0, function* () {
yield Folder_1.default.save({ title: 'folder1' });
const folder2 = yield Folder_1.default.save({ title: 'folder2' });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield sleep(0.1);
yield Folder_1.default.delete(folder2.id);
yield synchronizerStart();
const all = yield test_utils_synchronizer_1.allNotesFolders();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
})));
it('should delete local folder', (() => __awaiter(this, void 0, void 0, function* () {
yield Folder_1.default.save({ title: 'folder1' });
const folder2 = yield Folder_1.default.save({ title: 'folder2' });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield Folder_1.default.delete(folder2.id);
yield synchronizerStart();
yield switchClient(1);
yield synchronizerStart();
const items = yield test_utils_synchronizer_1.allNotesFolders();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(items, expect);
})));
it('should cross delete all folders', (() => __awaiter(this, void 0, void 0, function* () {
// If client1 and 2 have two folders, client 1 deletes item 1 and client
// 2 deletes item 2, they should both end up with no items after sync.
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
const folder2 = yield Folder_1.default.save({ title: 'folder2' });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield sleep(0.1);
yield Folder_1.default.delete(folder1.id);
yield switchClient(1);
yield Folder_1.default.delete(folder2.id);
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
const items2 = yield test_utils_synchronizer_1.allNotesFolders();
yield switchClient(1);
yield synchronizerStart();
const items1 = yield test_utils_synchronizer_1.allNotesFolders();
expect(items1.length).toBe(0);
expect(items1.length).toBe(items2.length);
})));
it('items should be downloaded again when user cancels in the middle of delta operation', (() => __awaiter(this, void 0, void 0, function* () {
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
yield Note_1.default.save({ title: 'un', is_todo: 1, parent_id: folder1.id });
yield synchronizerStart();
yield switchClient(2);
synchronizer().testingHooks_ = ['cancelDeltaLoop2'];
yield synchronizerStart();
let notes = yield Note_1.default.all();
expect(notes.length).toBe(0);
synchronizer().testingHooks_ = [];
yield synchronizerStart();
notes = yield Note_1.default.all();
expect(notes.length).toBe(1);
})));
it('should skip items that cannot be synced', (() => __awaiter(this, void 0, void 0, function* () {
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
const note1 = yield Note_1.default.save({ title: 'un', is_todo: 1, parent_id: folder1.id });
const noteId = note1.id;
yield synchronizerStart();
let disabledItems = yield BaseItem_1.default.syncDisabledItems(syncTargetId());
expect(disabledItems.length).toBe(0);
yield Note_1.default.save({ id: noteId, title: 'un mod' });
synchronizer().testingHooks_ = ['notesRejectedByTarget'];
yield synchronizerStart();
synchronizer().testingHooks_ = [];
yield synchronizerStart(); // Another sync to check that this item is now excluded from sync
yield switchClient(2);
yield synchronizerStart();
const notes = yield Note_1.default.all();
expect(notes.length).toBe(1);
expect(notes[0].title).toBe('un');
yield switchClient(1);
disabledItems = yield BaseItem_1.default.syncDisabledItems(syncTargetId());
expect(disabledItems.length).toBe(1);
})));
it('should allow duplicate folder titles', (() => __awaiter(this, void 0, void 0, function* () {
yield Folder_1.default.save({ title: 'folder' });
yield switchClient(2);
let remoteF2 = yield Folder_1.default.save({ title: 'folder' });
yield synchronizerStart();
yield switchClient(1);
yield sleep(0.1);
yield synchronizerStart();
const localF2 = yield Folder_1.default.load(remoteF2.id);
expect(localF2.title == remoteF2.title).toBe(true);
// Then that folder that has been renamed locally should be set in such a way
// that synchronizing it applies the title change remotely, and that new title
// should be retrieved by client 2.
yield synchronizerStart();
yield switchClient(2);
yield sleep(0.1);
yield synchronizerStart();
remoteF2 = yield Folder_1.default.load(remoteF2.id);
expect(remoteF2.title == localF2.title).toBe(true);
})));
it('should create remote items with UTF-8 content', (() => __awaiter(this, void 0, void 0, function* () {
const folder = yield Folder_1.default.save({ title: 'Fahrräder' });
yield Note_1.default.save({ title: 'Fahrräder', body: 'Fahrräder', parent_id: folder.id });
const all = yield test_utils_synchronizer_1.allNotesFolders();
yield synchronizerStart();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
})));
it('should update remote items but not pull remote changes', (() => __awaiter(this, void 0, void 0, function* () {
const folder = yield Folder_1.default.save({ title: 'folder1' });
const note = yield Note_1.default.save({ title: 'un', parent_id: folder.id });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield Note_1.default.save({ title: 'deux', parent_id: folder.id });
yield synchronizerStart();
yield switchClient(1);
yield Note_1.default.save({ title: 'un UPDATE', id: note.id });
yield synchronizerStart(null, { syncSteps: ['update_remote'] });
const all = yield test_utils_synchronizer_1.allNotesFolders();
expect(all.length).toBe(2);
yield switchClient(2);
yield synchronizerStart();
const note2 = yield Note_1.default.load(note.id);
expect(note2.title).toBe('un UPDATE');
})));
it('should create a new Welcome notebook on each client', (() => __awaiter(this, void 0, void 0, function* () {
// Create the Welcome items on two separate clients
yield WelcomeUtils.createWelcomeItems();
yield synchronizerStart();
yield switchClient(2);
yield WelcomeUtils.createWelcomeItems();
const beforeFolderCount = (yield Folder_1.default.all()).length;
const beforeNoteCount = (yield Note_1.default.all()).length;
expect(beforeFolderCount === 1).toBe(true);
expect(beforeNoteCount > 1).toBe(true);
yield synchronizerStart();
const afterFolderCount = (yield Folder_1.default.all()).length;
const afterNoteCount = (yield Note_1.default.all()).length;
expect(afterFolderCount).toBe(beforeFolderCount * 2);
expect(afterNoteCount).toBe(beforeNoteCount * 2);
// Changes to the Welcome items should be synced to all clients
const f1 = (yield Folder_1.default.all())[0];
yield Folder_1.default.save({ id: f1.id, title: 'Welcome MOD' });
yield synchronizerStart();
yield switchClient(1);
yield synchronizerStart();
const f1_1 = yield Folder_1.default.load(f1.id);
expect(f1_1.title).toBe('Welcome MOD');
})));
it('should not wipe out user data when syncing with an empty target', (() => __awaiter(this, void 0, void 0, function* () {
// Only these targets support the wipeOutFailSafe flag (in other words, the targets that use basicDelta)
if (!['nextcloud', 'memory', 'filesystem', 'amazon_s3'].includes(syncTargetName())) { return; }
for (let i = 0; i < 10; i++) { yield Note_1.default.save({ title: 'note' }); }
Setting_1.default.setValue('sync.wipeOutFailSafe', true);
yield synchronizerStart();
yield fileApi().clearRoot(); // oops
yield synchronizerStart();
expect((yield Note_1.default.all()).length).toBe(10); // but since the fail-safe if on, the notes have not been deleted
Setting_1.default.setValue('sync.wipeOutFailSafe', false); // Now switch it off
yield synchronizerStart();
expect((yield Note_1.default.all()).length).toBe(0); // Since the fail-safe was off, the data has been cleared
// Handle case where the sync target has been wiped out, then the user creates one note and sync.
for (let i = 0; i < 10; i++) { yield Note_1.default.save({ title: 'note' }); }
Setting_1.default.setValue('sync.wipeOutFailSafe', true);
yield synchronizerStart();
yield fileApi().clearRoot();
yield Note_1.default.save({ title: 'ma note encore' });
yield synchronizerStart();
expect((yield Note_1.default.all()).length).toBe(11);
})));
});
// # sourceMappingURL=Synchronizer.share.js.map

View File

@@ -1,31 +0,0 @@
/* eslint-disable no-unused-vars */
const time = require('@joplin/lib/time').default;
const { sortedIds, createNTestNotes, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
const Setting = require('@joplin/lib/models/Setting').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
const ArrayUtils = require('@joplin/lib/ArrayUtils.js');
const shim = require('@joplin/lib/shim').default;
describe('database', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should not modify cached field names', (async () => {
const db = BaseModel.db();
const fieldNames = db.tableFieldNames('notes');
const fieldCount = fieldNames.length;
fieldNames.push('type_');
expect(fieldCount).toBeGreaterThan(0);
expect(db.tableFieldNames('notes').length).toBe(fieldCount);
}));
});

View File

@@ -1,4 +1,4 @@
const { id, ids, createNTestFolders, sortedIds, createNTestNotes, TestApp } = require('./test-utils.js');
const { id, ids, createNTestFolders, sortedIds, createNTestNotes, TestApp } = require('@joplin/lib/testing/test-utils.js');
const BaseModel = require('@joplin/lib/BaseModel').default;
const uuid = require('@joplin/lib/uuid').default;
const Note = require('@joplin/lib/models/Note').default;

View File

@@ -1,5 +1,5 @@
/* eslint-disable no-unused-vars */
const { setupDatabaseAndSynchronizer, switchClient, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('./test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('@joplin/lib/testing/test-utils.js');
const Setting = require('@joplin/lib/models/Setting').default;
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;

View File

@@ -1,5 +1,5 @@
/* eslint-disable no-unused-vars */
const { setupDatabaseAndSynchronizer, switchClient, id, ids, sortedIds, at, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('./test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient, id, ids, sortedIds, at, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('@joplin/lib/testing/test-utils.js');
const Setting = require('@joplin/lib/models/Setting').default;
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;

View File

@@ -1,5 +1,5 @@
/* eslint-disable no-unused-vars */
const { setupDatabaseAndSynchronizer, switchClient, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('./test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('@joplin/lib/testing/test-utils.js');
const Setting = require('@joplin/lib/models/Setting').default;
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;

View File

@@ -0,0 +1,9 @@
# Markdown file test
![../support/photo.jpg](../support/photo.jpg)
[welcome.pdf](../support/welcome.pdf)
[sample.md](sample.md)
[sample2.md](./sample.md)

View File

@@ -1,4 +1,4 @@
import PluginRunner from '../app/services/plugins/PluginRunner';
import PluginRunner from '../../../app/services/plugins/PluginRunner';
import PluginService from '@joplin/lib/services/plugins/PluginService';
import { ContentScriptType } from '@joplin/lib/services/plugins/api/types';
import MdToHtml from '@joplin/renderer/MdToHtml';
@@ -7,10 +7,10 @@ import Setting from '@joplin/lib/models/Setting';
import * as fs from 'fs-extra';
import Note from '@joplin/lib/models/Note';
import Folder from '@joplin/lib/models/Folder';
import { newPluginScript } from './test-utils';
import { expectNotThrow, setupDatabaseAndSynchronizer, switchClient, expectThrow, createTempDir } from './test-utils.js';
import { newPluginScript } from '@joplin/lib/testing/test-utils';
import { expectNotThrow, setupDatabaseAndSynchronizer, switchClient, expectThrow, createTempDir, supportDir } from '@joplin/lib/testing/test-utils';
const testPluginDir = `${__dirname}/../tests/support/plugins`;
const testPluginDir = `${supportDir}/plugins`;
function newPluginService(appVersion: string = '1.4') {
const runner = new PluginRunner();
@@ -102,7 +102,7 @@ describe('services_PluginService', function() {
"homepage_url": "https://joplinapp.org"
}
*/
joplin.plugins.register({
onStart: async function() {
await joplin.data.post(['folders'], null, { title: "my plugin folder" });
@@ -141,14 +141,14 @@ describe('services_PluginService', function() {
"not_a_valid_manifest_at_all": 1
}
*/
joplin.plugins.register({
onStart: async function() {},
});
`, `
/* joplin-manifest:
*/
joplin.plugins.register({
onStart: async function() {},
});
@@ -189,7 +189,7 @@ describe('services_PluginService', function() {
"homepage_url": "https://joplinapp.org"
}
*/
joplin.plugins.register({
onStart: async function() {
await joplin.contentScripts.register('markdownItPlugin', 'justtesting', './markdownItTestPlugin.js');
@@ -236,7 +236,7 @@ describe('services_PluginService', function() {
"version": "1.0.0"
}
*/
joplin.plugins.register({
onStart: async function() { },
});
@@ -283,7 +283,7 @@ describe('services_PluginService', function() {
}));
it('should create the data directory', (async () => {
const pluginScript = newPluginScript(`
const pluginScript = newPluginScript(`
joplin.plugins.register({
onStart: async function() {
const dataDir = await joplin.plugins.dataDir();

View File

@@ -1,6 +1,6 @@
import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
import shim from '@joplin/lib/shim';
import { setupDatabaseAndSynchronizer, switchClient, supportDir, createTempDir } from '../../test-utils';
import { setupDatabaseAndSynchronizer, switchClient, supportDir, createTempDir } from '@joplin/lib/testing/test-utils';
async function newRepoApi(): Promise<RepositoryApi> {
const repo = new RepositoryApi(`${supportDir}/pluginRepo`, await createTempDir());

View File

@@ -1,6 +1,6 @@
import Setting from '@joplin/lib/models/Setting';
import PluginService from '@joplin/lib/services/plugins/PluginService';
const { waitForFolderCount, newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('../../../test-utils');
const { waitForFolderCount, newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('@joplin/lib/testing/test-utils');
import Folder from '@joplin/lib/models/Folder';
describe('JoplinSettings', () => {

View File

@@ -1,6 +1,6 @@
import KeymapService from '@joplin/lib/services/KeymapService';
import PluginService from '@joplin/lib/services/plugins/PluginService';
const { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('../../../test-utils');
const { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('@joplin/lib/testing/test-utils');
describe('JoplinViewMenuItem', () => {

View File

@@ -1,5 +1,5 @@
import Setting from '@joplin/lib/models/Setting';
import { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } from '../../../test-utils';
import { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } from '@joplin/lib/testing/test-utils';
import Note from '@joplin/lib/models/Note';
import Folder from '@joplin/lib/models/Folder';
import ItemChange from '@joplin/lib/models/ItemChange';

View File

@@ -1,6 +1,6 @@
import sandboxProxy, { Target } from '@joplin/lib/services/plugins/sandboxProxy';
const { setupDatabaseAndSynchronizer, switchClient } = require('../../test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient } = require('@joplin/lib/testing/test-utils.js');
describe('services_plugins_sandboxProxy', function() {

View File

@@ -1,4 +1,4 @@
const {main} = require('./syncTargetUtils');
const {main} = require('@joplin/lib/testing/syncTargetUtils');
const syncTargetType = process.argv.length <= 2 ? 'normal' : process.argv[2];

View File

@@ -166,6 +166,7 @@ export default class InteropServiceHelper {
exportOptions.format = module.format;
// exportOptions.modulePath = module.path;
if (options.plugins) exportOptions.plugins = options.plugins;
exportOptions.customCss = options.customCss;
exportOptions.target = module.target;
exportOptions.includeConflicts = !!options.includeConflicts;
if (options.sourceFolderIds) exportOptions.sourceFolderIds = options.sourceFolderIds;

View File

@@ -256,7 +256,7 @@ class EncryptionConfigScreenComponent extends React.Component {
<div>
<div style={containerStyle}>
{
<div style={{ backgroundColor: theme.warningBackgroundColor, paddingLeft: 10, paddingRight: 10, paddingTop: 2, paddingBottom: 2 }}>
<div className="alert alert-warning" style={{ backgroundColor: theme.warningBackgroundColor, paddingLeft: 10, paddingRight: 10, paddingTop: 2, paddingBottom: 2 }}>
<p style={theme.textStyle}>
<span>{_('For more information about End-To-End Encryption (E2EE) and advice on how to enable it please check the documentation:')}</span>{' '}
<a

View File

@@ -90,6 +90,7 @@ interface Props {
['spellChecker.enabled']: boolean;
['spellChecker.language']: string;
plugins: PluginStates;
customCss: string;
}
const commandNames: string[] = menuCommandNames();
@@ -313,7 +314,10 @@ function useMenu(props: Props) {
await InteropServiceHelper.export(
(action: any) => props.dispatch(action),
module,
{ plugins: props.plugins }
{
plugins: props.plugins,
customCss: props.customCss,
}
);
},
});
@@ -860,7 +864,7 @@ function useMenu(props: Props) {
clearTimeout(timeoutId);
timeoutId = null;
};
}, [props.routeName, props.pluginMenuItems, props.pluginMenus, keymapLastChangeTime, modulesLastChangeTime, props['spellChecker.language'], props['spellChecker.enabled'], props.plugins]);
}, [props.routeName, props.pluginMenuItems, props.pluginMenus, keymapLastChangeTime, modulesLastChangeTime, props['spellChecker.language'], props['spellChecker.enabled'], props.plugins, props.customCss]);
useMenuStates(menu, props);
@@ -917,6 +921,7 @@ const mapStateToProps = (state: AppState) => {
['spellChecker.language']: state.settings['spellChecker.language'],
['spellChecker.enabled']: state.settings['spellChecker.enabled'],
plugins: state.pluginService.plugins,
customCss: state.customCss,
};
};

View File

@@ -13,6 +13,7 @@ interface MultiNoteActionsProps {
watchedNoteFiles: string[];
plugins: PluginStates;
inConflictFolder: boolean;
customCss: string;
}
function styles_(props: MultiNoteActionsProps) {
@@ -53,6 +54,7 @@ export default function MultiNoteActions(props: MultiNoteActionsProps) {
watchedNoteFiles: props.watchedNoteFiles,
plugins: props.plugins,
inConflictFolder: props.inConflictFolder,
customCss: props.customCss,
});
const itemComps = [];

View File

@@ -24,7 +24,7 @@ interface KeyToLabelMap {
let markupToHtml_: any = null;
function markupToHtml() {
if (markupToHtml_) return markupToHtml_;
markupToHtml_ = markupLanguageUtils.newMarkupToHtml({});
markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
return markupToHtml_;
}

View File

@@ -1,7 +1,7 @@
import * as React from 'react';
import { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from 'react';
import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps } from '../../utils/types';
import { resourcesStatus, commandAttachFileToBody, handlePasteEvent, processPastedHtml } from '../../utils/resourceHandling';
import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps, ResourceInfos } from '../../utils/types';
import { resourcesStatus, commandAttachFileToBody, handlePasteEvent, processPastedHtml, attachedResources } from '../../utils/resourceHandling';
import useScroll from './utils/useScroll';
import styles_ from './styles';
import CommandService from '@joplin/lib/services/CommandService';
@@ -20,6 +20,7 @@ const taboverride = require('taboverride');
import { reg } from '@joplin/lib/registry';
import BaseItem from '@joplin/lib/models/BaseItem';
import setupToolbarButtons from './utils/setupToolbarButtons';
import { plainTextToHtml } from '@joplin/lib/htmlUtils';
const { themeStyle } = require('@joplin/lib/theme');
const { clipboard } = require('electron');
const supportedLocales = require('./supportedLocales');
@@ -66,6 +67,26 @@ function newBlockSource(language: string = '', content: string = ''): any {
};
}
// In TinyMCE 5.2, when setting the body to '<div id="rendered-md"></div>',
// it would end up as '<div id="rendered-md"><br/></div>' once rendered
// (an additional <br/> was inserted).
//
// This behaviour was "fixed" later on, possibly in 5.6, which has this change:
//
// - Fixed getContent with text format returning a new line when the editor is empty #TINY-6281
//
// The problem is that the list plugin was, unknown to me, relying on this <br/>
// being present. Without it, trying to add a bullet point or checkbox on an
// empty document, does nothing. The exact reason for this is unclear
// so as a workaround we manually add this <br> for empty documents,
// which fixes the issue.
//
// Perhaps upgrading the list plugin (which is a fork of TinyMCE own list plugin)
// would help?
function awfulBrHack(html: string): string {
return html === '<div id="rendered-md"></div>' ? '<div id="rendered-md"><br/></div>' : html;
}
function findEditableContainer(node: any): any {
while (node) {
if (node.classList && node.classList.contains('joplin-editable')) return node;
@@ -127,6 +148,12 @@ const joplinCommandToTinyMceCommands: JoplinCommandToTinyMceCommands = {
'search': { name: 'SearchReplace' },
};
interface LastOnChangeEventInfo {
content: string;
resourceInfos: ResourceInfos;
contentKey: string;
}
let loadedCssFiles_: string[] = [];
let loadedJsFiles_: string[] = [];
let dispatchDidUpdateIID_: any = null;
@@ -147,7 +174,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
const markupToHtml = useRef(null);
markupToHtml.current = props.markupToHtml;
const lastOnChangeEventInfo = useRef<any>({
const lastOnChangeEventInfo = useRef<LastOnChangeEventInfo>({
content: null,
resourceInfos: null,
contentKey: null,
@@ -338,7 +365,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
element.id = script.id;
element.onload = () => {
resolve();
resolve(null);
};
document.getElementsByTagName('head')[0].appendChild(element);
@@ -807,6 +834,25 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
}
};
function resourceInfosEqual(ri1: ResourceInfos, ri2: ResourceInfos): boolean {
if (ri1 && !ri2 || !ri1 && ri2) return false;
if (!ri1 && !ri2) return true;
const keys1 = Object.keys(ri1);
const keys2 = Object.keys(ri2);
if (keys1.length !== keys2.length) return false;
// The attachedResources() call that generates the ResourceInfos object
// uses cache for the resource objects, so we can use strict equality
// for comparison.
for (const k of keys1) {
if (ri1[k] !== ri2[k]) return false;
}
return true;
}
useEffect(() => {
if (!editor) return () => {};
@@ -818,11 +864,13 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
let cancelled = false;
const loadContent = async () => {
if (lastOnChangeEventInfo.current.content !== props.content || lastOnChangeEventInfo.current.resourceInfos !== props.resourceInfos) {
const resourcesEqual = resourceInfosEqual(lastOnChangeEventInfo.current.resourceInfos, props.resourceInfos);
if (lastOnChangeEventInfo.current.content !== props.content || !resourcesEqual) {
const result = await props.markupToHtml(props.contentMarkupLanguage, props.content, markupRenderOptions({ resourceInfos: props.resourceInfos }));
if (cancelled) return;
editor.setContent(result.html);
editor.setContent(awfulBrHack(result.html));
if (lastOnChangeEventInfo.current.contentKey !== props.contentKey) {
// Need to clear UndoManager to avoid this problem:
@@ -922,6 +970,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
const contentMd = await prop_htmlToMarkdownRef.current(info.contentMarkupLanguage, info.editor.getContent(), info.contentOriginalCss);
lastOnChangeEventInfo.current.content = contentMd;
lastOnChangeEventInfo.current.resourceInfos = await attachedResources(contentMd);
props_onChangeRef.current({
changeId: info.changeId,
@@ -1017,31 +1066,37 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
}
async function onPaste(event: any) {
// We do not use the default pasting behaviour because the input has
// to be processed in various ways.
event.preventDefault();
const resourceMds = await handlePasteEvent(event);
if (resourceMds.length) {
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, resourceMds.join('\n'), markupRenderOptions({ bodyOnly: true }));
editor.insertContent(result.html);
} else {
const pastedText = event.clipboardData.getData('text');
const pastedText = event.clipboardData.getData('text/plain');
if (BaseItem.isMarkdownTag(pastedText)) { // Paste a link to a note
event.preventDefault();
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, pastedText, markupRenderOptions({ bodyOnly: true }));
editor.insertContent(result.html);
} else { // Paste regular text
// HACK: TinyMCE doesn't add an undo step when pasting, for unclear reasons
// so we manually add it here. We also can't do it immediately it seems, or
// else nothing is added to the stack, so do it on the next frame.
const pastedHtml = clipboard.readHTML();
if (pastedHtml) {
event.preventDefault();
const pastedHtml = event.clipboardData.getData('text/html');
if (pastedHtml) { // Handles HTML
const modifiedHtml = await processPastedHtml(pastedHtml);
editor.insertContent(modifiedHtml);
} else { // Handles plain text
pasteAsPlainText(pastedText);
}
window.requestAnimationFrame(() => editor.undoManager.add());
onChangeHandler();
// This code before was necessary to get undo working after
// pasting but it seems it's no longer necessary, so
// removing it for now. We also couldn't do it immediately
// it seems, or else nothing is added to the stack, so do it
// on the next frame.
//
// window.requestAnimationFrame(() =>
// editor.undoManager.add()); onChangeHandler();
}
}
}
@@ -1060,6 +1115,13 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
onChangeHandler();
}
function pasteAsPlainText(text: string = null) {
const pastedText = text === null ? clipboard.readText() : text;
if (pastedText) {
editor.insertContent(plainTextToHtml(pastedText));
}
}
function onKeyDown(event: any) {
// It seems "paste as text" is handled automatically by
// on Windows so the code below so we need to run the below
@@ -1072,8 +1134,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
// it here and we don't need to do anything special in onPaste
if (!shim.isWindows()) {
if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.code === 'KeyV') {
const pastedText = clipboard.readText();
if (pastedText) editor.insertContent(pastedText);
pasteAsPlainText();
}
}
}

View File

@@ -156,10 +156,11 @@ function NoteEditor(props: NoteEditorProps) {
const markupToHtml = markupLanguageUtils.newMarkupToHtml({}, {
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
customCss: props.customCss,
});
return markupToHtml.allAssets(markupLanguage, theme);
}, [props.themeId]);
}, [props.themeId, props.customCss]);
const handleProvisionalFlag = useCallback(() => {
if (props.isProvisional) {
@@ -458,6 +459,7 @@ function NoteEditor(props: NoteEditorProps) {
watchedNoteFiles={props.watchedNoteFiles}
plugins={props.plugins}
inConflictFolder={props.selectedFolderId === Folder.conflictFolderId()}
customCss={props.customCss}
/>;
}
@@ -549,7 +551,7 @@ function NoteEditor(props: NoteEditorProps) {
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
{renderSearchBar()}
</div>
<div style={{ paddingLeft: theme.editorPaddingLeft, display: 'flex', flexDirection: 'row', alignItems: 'center', height: 40 }}>
<div className="tag-bar" style={{ paddingLeft: theme.editorPaddingLeft, display: 'flex', flexDirection: 'row', alignItems: 'center', height: 40 }}>
{renderTagButton()}
{renderTagBar()}
</div>

View File

@@ -91,7 +91,7 @@ export default function NoteTitleBar(props: Props) {
}, []);
function renderTitleBarDate() {
return <span style={styles.titleDate}>{time.formatMsToLocal(props.noteUserUpdatedTime)}</span>;
return <span className="updated-time-label" style={styles.titleDate}>{time.formatMsToLocal(props.noteUserUpdatedTime)}</span>;
}
function renderNoteToolbar() {
@@ -104,6 +104,7 @@ export default function NoteTitleBar(props: Props) {
return (
<StyledRoot>
<input
className="title-input"
type="text"
ref={props.titleInputRef}
placeholder={props.isProvisional ? _('Creating new %s...', props.noteIsTodo ? _('to-do') : _('note')) : ''}

View File

@@ -135,6 +135,13 @@ export async function processPastedHtml(html: string) {
const allImageUrls: string[] = [];
const mappedResources: Record<string, string> = {};
// When copying text from eg. GitHub, the HTML might contain non-breaking
// spaces instead of regular spaces. If these non-breaking spaces are
// inserted into the TinyMCE editor (using insertContent), they will be
// dropped. So here we convert them to regular spaces.
// https://stackoverflow.com/a/31790544/561309
html = html.replace(/[\u202F\u00A0]/g, ' ');
htmlUtils.replaceImageUrls(html, (src: string) => {
allImageUrls.push(src);
});

View File

@@ -2,6 +2,8 @@
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
import { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import { MarkupLanguage } from '@joplin/renderer';
import { RenderResult, RenderResultPluginAsset } from '@joplin/renderer/MarkupToHtml';
export interface ToolbarButtonInfos {
[key: string]: ToolbarButtonInfo;
@@ -49,9 +51,9 @@ export interface NoteBodyEditorProps {
onWillChange(event: any): void;
onMessage(event: any): void;
onScroll(event: any): void;
markupToHtml: Function;
markupToHtml: (markupLanguage: MarkupLanguage, markup: string, options: any)=> Promise<RenderResult>;
htmlToMarkdown: Function;
allAssets: Function;
allAssets: (markupLanguage: MarkupLanguage)=> Promise<RenderResultPluginAsset[]>;
disabled: boolean;
dispatch: Function;
noteToolbar: any;

View File

@@ -24,6 +24,7 @@ export default function useMarkupToHtml(deps: HookDependencies) {
const markupToHtml = useMemo(() => {
return markupLanguageUtils.newMarkupToHtml(deps.plugins, {
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
customCss: customCss || '',
});
}, [plugins]);
@@ -49,7 +50,6 @@ export default function useMarkupToHtml(deps: HookDependencies) {
const result = await markupToHtml.render(markupLanguage, md, theme, Object.assign({}, {
codeTheme: theme.codeThemeCss,
userCss: customCss || '',
resources: resources,
postMessageSyntax: 'ipcProxySendToHost',
splitted: true,

View File

@@ -125,6 +125,7 @@ class NoteListComponent extends React.Component {
watchedNoteFiles: this.props.watchedNoteFiles,
plugins: this.props.plugins,
inConflictFolder: this.props.selectedFolderId === Folder.conflictFolderId(),
customCss: this.props.customCss,
});
menu.popup(bridge().window());
@@ -513,6 +514,7 @@ const mapStateToProps = (state: AppState) => {
noteSortOrder: state.settings['notes.sortOrder.field'],
highlightedWords: state.highlightedWords,
plugins: state.pluginService.plugins,
customCss: state.customCss,
};
};

View File

@@ -54,12 +54,14 @@ export default function NoteListControls(props: Props) {
return (
<ButtonContainer>
<StyledButton
className="new-todo-button"
tooltip={CommandService.instance().label('newTodo')}
iconName="far fa-check-square"
level={ButtonLevel.Primary}
onClick={onNewTodoButtonClick}
/>
<StyledButton
className="new-note-button"
tooltip={CommandService.instance().label('newNote')}
iconName="icon-note"
level={ButtonLevel.Primary}

View File

@@ -150,12 +150,21 @@ function NoteListItem(props: NoteListItemProps, ref: any) {
color: theme.color,
};
const watchedIcon = props.isWatched ? null : <i style={watchedIconStyle} className={'fa fa-share-square'}></i>;
const classNames = [
'list-item-container',
props.isSelected && 'selected',
item.todo_completed && 'todo-completed',
item.is_todo ? 'todo-list-item' : 'note-list-item',
(props.index + 1) % 2 === 0 ? 'even' : 'odd',
]
.filter(e => !!e)
.join(' ');
// Need to include "todo_completed" in key so that checkbox is updated when
// item is changed via sync.
return (
<StyledRoot
className="list-item-container"
className={classNames}
onDragOver={props.onNoteDragOver}
onDrop={props.onNoteDrop}
width={props.width}

View File

@@ -118,11 +118,11 @@ class NoteRevisionViewerComponent extends React.PureComponent {
const markupToHtml = markupLanguageUtils.newMarkupToHtml({}, {
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
customCss: this.props.customCss ? this.props.customCss : '',
});
const result = await markupToHtml.render(markupLanguage, noteBody, theme, {
codeTheme: theme.codeThemeCss,
userCss: this.props.customCss ? this.props.customCss : '',
resources: await shared.attachedResources(noteBody),
postMessageSyntax: 'ipcProxySendToHost',
});

View File

@@ -158,7 +158,7 @@ class NoteSearchBarComponent extends React.Component {
);
return (
<div style={this.props.style}>
<div className="note-search-bar" style={this.props.style}>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
{closeButton}
<input

View File

@@ -221,11 +221,11 @@ class PromptDialog extends React.Component {
let inputComp = null;
if (this.props.inputType === 'datetime') {
inputComp = <Datetime value={this.state.answer} inputProps={{ style: styles.input }} dateFormat={time.dateFormat()} timeFormat={time.timeFormat()} onChange={momentObject => onDateTimeChange(momentObject)} />;
inputComp = <Datetime className="datetime-picker" value={this.state.answer} inputProps={{ style: styles.input }} dateFormat={time.dateFormat()} timeFormat={time.timeFormat()} onChange={momentObject => onDateTimeChange(momentObject)} />;
} else if (this.props.inputType === 'tags') {
inputComp = <CreatableSelect styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} value={this.state.answer} placeholder="" components={makeAnimated()} isMulti={true} isClearable={false} backspaceRemovesValue={true} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
inputComp = <CreatableSelect className="tag-selector" styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} value={this.state.answer} placeholder="" components={makeAnimated()} isMulti={true} isClearable={false} backspaceRemovesValue={true} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
} else if (this.props.inputType === 'dropdown') {
inputComp = <Select styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} components={makeAnimated()} value={this.props.answer} defaultValue={this.props.defaultValue} isClearable={false} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
inputComp = <Select className="item-selector" styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} components={makeAnimated()} value={this.props.answer} defaultValue={this.props.defaultValue} isClearable={false} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
} else {
inputComp = <input style={styles.input} ref={this.answerInput_} value={this.state.answer} type="text" onChange={event => onChange(event)} onKeyDown={event => onKeyDown(event)} />;
}
@@ -254,8 +254,8 @@ class PromptDialog extends React.Component {
}
return (
<div style={styles.modalLayer}>
<div style={styles.promptDialog}>
<div className="modal-layer" style={styles.modalLayer}>
<div className="modal-dialog" style={styles.promptDialog}>
<label style={styles.label}>{this.props.label ? this.props.label : ''}</label>
<div style={{ display: 'inline-block', color: 'black', backgroundColor: theme.backgroundColor }}>
{inputComp}

View File

@@ -126,7 +126,7 @@ function SearchBar(props: Props) {
}, [props.notesParentType, onExitSearch]);
return (
<Root>
<Root className="search-bar">
<SearchInput
inputRef={props.inputRef}
value={query}

View File

@@ -77,12 +77,12 @@ function ExpandLink(props: any) {
function FolderItem(props: any) {
const { hasChildren, isExpanded, parentId, depth, selected, folderId, folderTitle, anchorRef, noteCount, onFolderDragStart_, onFolderDragOver_, onFolderDrop_, itemContextMenu, folderItem_click, onFolderToggleClick_, shareId } = props;
const noteCountComp = noteCount ? <StyledNoteCount>{noteCount}</StyledNoteCount> : null;
const noteCountComp = noteCount ? <StyledNoteCount className="note-count-label">{noteCount}</StyledNoteCount> : null;
const shareIcon = shareId && !parentId ? <StyledShareIcon className="fas fa-share-alt"></StyledShareIcon> : null;
return (
<StyledListItem depth={depth} selected={selected} className={`list-item-container list-item-depth-${depth}`} onDragStart={onFolderDragStart_} onDragOver={onFolderDragOver_} onDrop={onFolderDrop_} draggable={true} data-folder-id={folderId}>
<StyledListItem depth={depth} selected={selected} className={`list-item-container list-item-depth-${depth} ${selected ? 'selected' : ''}`} onDragStart={onFolderDragStart_} onDragOver={onFolderDragOver_} onDrop={onFolderDrop_} draggable={true} data-folder-id={folderId}>
<ExpandLink themeId={props.themeId} hasChildren={hasChildren} folderId={folderId} onClick={onFolderToggleClick_} isExpanded={isExpanded}/>
<StyledListItemAnchor
ref={anchorRef}
@@ -100,7 +100,8 @@ function FolderItem(props: any) {
}}
onDoubleClick={onFolderToggleClick_}
>
{folderTitle} {shareIcon} {noteCountComp}
<span className="title">{folderTitle}</span>
{shareIcon} {noteCountComp}
</StyledListItemAnchor>
</StyledListItem>
);
@@ -382,7 +383,7 @@ class SidebarComponent extends React.Component<Props, State> {
}
renderNoteCount(count: number) {
return count ? <StyledNoteCount>{count}</StyledNoteCount> : null;
return count ? <StyledNoteCount className="note-count-label">{count}</StyledNoteCount> : null;
}
renderExpandIcon(isExpanded: boolean, isVisible: boolean = true) {
@@ -394,7 +395,7 @@ class SidebarComponent extends React.Component<Props, State> {
renderAllNotesItem(selected: boolean) {
return (
<StyledListItem key="allNotesHeader" selected={selected} className={'list-item-container list-item-depth-0'} isSpecialItem={true}>
<StyledListItem key="allNotesHeader" selected={selected} className={'list-item-container list-item-depth-0 all-notes'} isSpecialItem={true}>
<StyledExpandLink>{this.renderExpandIcon(false, false)}</StyledExpandLink>
<StyledAllNotesIcon className="icon-notes"/>
<StyledListItemAnchor
@@ -451,7 +452,12 @@ class SidebarComponent extends React.Component<Props, State> {
const noteCount = Setting.value('showNoteCounts') ? this.renderNoteCount(tag.note_count) : '';
return (
<StyledListItem selected={selected} className={'list-item-container'} key={tag.id} onDrop={this.onTagDrop_} data-tag-id={tag.id}>
<StyledListItem selected={selected}
className={`list-item-container ${selected ? 'selected' : ''}`}
key={tag.id}
onDrop={this.onTagDrop_}
data-tag-id={tag.id}
>
<StyledExpandLink>{this.renderExpandIcon(false, false)}</StyledExpandLink>
<StyledListItemAnchor
ref={anchorRef}
@@ -465,7 +471,8 @@ class SidebarComponent extends React.Component<Props, State> {
this.tagItem_click(tag);
}}
>
{Tag.displayTitle(tag)} {noteCount}
<span className="tag-label">{Tag.displayTitle(tag)}</span>
{noteCount}
</StyledListItemAnchor>
</StyledListItem>
);
@@ -657,7 +664,11 @@ class SidebarComponent extends React.Component<Props, State> {
const folderItems = [this.renderAllNotesItem(allNotesSelected)].concat(result.items);
this.folderItemsOrder_ = result.order;
items.push(
<div className="folders" key="folder_items" style={{ display: this.state.folderHeaderIsExpanded ? 'block' : 'none', paddingBottom: 10 }}>
<div
className={`folders ${this.state.folderHeaderIsExpanded ? 'expanded' : ''}`}
key="folder_items"
style={{ display: this.state.folderHeaderIsExpanded ? 'block' : 'none', paddingBottom: 10 }}
>
{folderItems}
</div>
);

View File

@@ -17,7 +17,16 @@ export default function ToggleEditorsButton(props: Props) {
const style = styles_(props);
return (
<button style={style.button} disabled={!props.toolbarButtonInfo.enabled} aria-label={props.toolbarButtonInfo.tooltip} title={props.toolbarButtonInfo.tooltip} type="button" className="tox-tbtn" aria-pressed="false" onClick={props.toolbarButtonInfo.onClick}>
<button
style={style.button}
disabled={!props.toolbarButtonInfo.enabled}
aria-label={props.toolbarButtonInfo.tooltip}
title={props.toolbarButtonInfo.tooltip}
type="button"
className={`tox-tbtn ${props.value}-active`}
aria-pressed="false"
onClick={props.toolbarButtonInfo.onClick}
>
<div style={style.leftInnerButton}>
<i style={style.leftIcon} className="fab fa-markdown"></i>
</div>

View File

@@ -22,6 +22,7 @@ interface ContextMenuProps {
watchedNoteFiles: string[];
plugins: PluginStates;
inConflictFolder: boolean;
customCss: string;
}
export default class NoteListUtils {
@@ -158,6 +159,7 @@ export default class NoteListUtils {
sourceNoteIds: noteIds,
includeConflicts: props.inConflictFolder,
plugins: props.plugins,
customCss: props.customCss,
});
},
})

View File

@@ -246,7 +246,7 @@ class Dialog extends React.PureComponent<Props, State> {
markupToHtml() {
if (this.markupToHtml_) return this.markupToHtml_;
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml({});
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
return this.markupToHtml_;
}
@@ -432,7 +432,8 @@ class Dialog extends React.PureComponent<Props, State> {
renderItem(item: SearchResult) {
const theme = themeStyle(this.props.themeId);
const style = this.style();
const rowStyle = item.id === this.state.selectedItemId ? style.rowSelected : style.row;
const isSelected = item.id === this.state.selectedItemId;
const rowStyle = isSelected ? style.rowSelected : style.row;
const titleHtml = item.fragments
? `<span style="font-weight: bold; color: ${theme.colorBright};">${item.title}</span>`
: surroundKeywords(this.state.keywords, item.title, `<span style="font-weight: bold; color: ${theme.colorBright};">`, '</span>', { escapeHtml: true });
@@ -444,7 +445,7 @@ class Dialog extends React.PureComponent<Props, State> {
const fragmentComp = !fragmentsHtml ? null : <div style={style.rowFragments} dangerouslySetInnerHTML={{ __html: (fragmentsHtml) }}></div>;
return (
<div key={item.id} style={rowStyle} onClick={this.listItem_onClick} data-id={item.id} data-parent-id={item.parent_id} data-type={item.type}>
<div key={item.id} className={isSelected ? 'selected' : null} style={rowStyle} onClick={this.listItem_onClick} data-id={item.id} data-parent-id={item.parent_id} data-type={item.type}>
<div style={style.rowTitle} dangerouslySetInnerHTML={{ __html: titleHtml }}></div>
{fragmentComp}
{pathComp}
@@ -521,11 +522,11 @@ class Dialog extends React.PureComponent<Props, State> {
render() {
const theme = themeStyle(this.props.themeId);
const style = this.style();
const helpComp = !this.state.showHelp ? null : <div style={style.help}>{_('Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.')}</div>;
const helpComp = !this.state.showHelp ? null : <div className="help-text" style={style.help}>{_('Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.')}</div>;
return (
<div onClick={this.modalLayer_onClick} style={theme.dialogModalLayer}>
<div style={style.dialogBox}>
<div className="modal-layer" onClick={this.modalLayer_onClick} style={theme.dialogModalLayer}>
<div className="modal-dialog" style={style.dialogBox}>
{helpComp}
<div style={style.inputHelpWrapper}>
<input autoFocus type="text" style={style.input} ref={this.inputRef} value={this.state.query} onChange={this.input_onChange} onKeyDown={this.input_onKeyDown} />

View File

@@ -14,32 +14,35 @@ if [ "$1" == "" ]; then
fi
USER_NUM=$1
CMD_FILE="$SCRIPT_DIR/runForSharingCommands-$USER_NUM.txt"
rm -f "$CMD_FILE"
USER_EMAIL="user$USER_NUM@example.com"
RESET_ALL=$2
PROFILE_DIR=~/.config/joplindev-desktop-$USER_NUM
rm -rf "$PROFILE_DIR"
echo "config keychain.supported 0" >> "$CMD_FILE"
echo "config sync.target 9" >> "$CMD_FILE"
echo "config sync.9.path http://localhost:22300" >> "$CMD_FILE"
echo "config sync.9.username $USER_EMAIL" >> "$CMD_FILE"
echo "config sync.9.password 123456" >> "$CMD_FILE"
if [ "$RESET_ALL" == "1" ]; then
CMD_FILE="$SCRIPT_DIR/runForSharingCommands-$USER_NUM.txt"
rm -f "$CMD_FILE"
if [ "$1" == "1" ]; then
curl --data '{"action": "createTestUsers"}' http://localhost:22300/api/debug
USER_EMAIL="user$USER_NUM@example.com"
rm -rf "$PROFILE_DIR"
echo 'mkbook "shared"' >> "$CMD_FILE"
echo 'mkbook "other"' >> "$CMD_FILE"
echo 'use "shared"' >> "$CMD_FILE"
echo 'mknote "note 1"' >> "$CMD_FILE"
echo 'mknote "note 2"' >> "$CMD_FILE"
echo "config keychain.supported 0" >> "$CMD_FILE"
echo "config sync.target 9" >> "$CMD_FILE"
echo "config sync.9.path http://localhost:22300" >> "$CMD_FILE"
echo "config sync.9.username $USER_EMAIL" >> "$CMD_FILE"
echo "config sync.9.password 123456" >> "$CMD_FILE"
if [ "$1" == "1" ]; then
curl --data '{"action": "createTestUsers"}' http://localhost:22300/api/debug
echo 'mkbook "shared"' >> "$CMD_FILE"
echo 'mkbook "other"' >> "$CMD_FILE"
echo 'use "shared"' >> "$CMD_FILE"
echo 'mknote "note 1"' >> "$CMD_FILE"
echo 'mknote "note 2"' >> "$CMD_FILE"
fi
cd "$ROOT_DIR/packages/app-cli"
npm start -- --profile "$PROFILE_DIR" batch "$CMD_FILE"
fi
cd "$ROOT_DIR/packages/app-cli"
npm start -- --profile "$PROFILE_DIR" batch "$CMD_FILE"
cd "$ROOT_DIR/packages/app-desktop"
npm start -- --profile "$PROFILE_DIR"

View File

@@ -1,11 +1,14 @@
import { MarkupLanguageUtils as BaseMarkupLanguageUtils } from '@joplin/lib/markupLanguageUtils';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import { contentScriptsToRendererRules } from '@joplin/lib/services/plugins/utils/loadContentScripts';
import { Options } from '@joplin/renderer/MarkupToHtml';
class MarkupLanguageUtils extends BaseMarkupLanguageUtils {
public newMarkupToHtml(plugins: PluginStates, options: any = null) {
return super.newMarkupToHtml({
public newMarkupToHtml(plugins: PluginStates = null, options: Options = null) {
plugins = plugins || {};
return super.newMarkupToHtml(null, {
extraRendererRules: contentScriptsToRendererRules(plugins),
...options,
});

View File

@@ -8,12 +8,6 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- RN-NOTIFICATION -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- /RN-NOTIFICATION -->
<!-- Make these features optional to enable Chromebooks -->
<!-- https://github.com/laurent22/joplin/issues/37 -->
<uses-feature android:name="android.hardware.camera" android:required="false" />
@@ -41,35 +35,6 @@
android:resizeableActivity="true"
android:theme="@style/AppTheme">
<!-- RN-NOTIFICATION -->
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="ACTION_DISMISS" />
<action android:name="ACTION_SNOOZE" />
</intent-filter>
</receiver>
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmDismissReceiver"
android:enabled="true"
android:exported="true" />
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmBootReceiver"
android:directBootAware="true"
android:enabled="false"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- /RN-NOTIFICATION -->
<!--
2018-12-16: Changed android:launchMode from "singleInstance" to "singleTop" for Firebase notification
Previously singleInstance was necessary to prevent multiple instance of the RN app from running at the same time, but maybe no longer needed.

View File

@@ -3,8 +3,13 @@ const shared = require('@joplin/lib/components/shared/note-screen-shared');
export default function useOnMessage(onCheckboxChange: Function, noteBody: string, onMarkForDownload: Function, onJoplinLinkClick: Function, onResourceLongPress: Function) {
return useCallback((event: any) => {
// Since RN 58 (or 59) messages are now escaped twice???
const msg = unescape(unescape(event.nativeEvent.data));
// 2021-05-19: Historically this was unescaped twice as it was
// apparently needed after an upgrade to RN 58 (or 59). However this is
// no longer needed and in fact would break certain URLs so it can be
// removed. Keeping the comment here anyway in case we find some URLs
// that end up being broken after removing the double unescaping.
// https://github.com/laurent22/joplin/issues/4494
const msg = event.nativeEvent.data;
console.info('Got IPC message: ', msg);

View File

@@ -240,7 +240,7 @@ PODS:
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsinspector (0.63.3)
- react-native-alarm-notification (1.7.1):
- react-native-alarm-notification (1.0.3):
- React
- react-native-camera (3.40.0):
- React-Core
@@ -391,7 +391,7 @@ DEPENDENCIES:
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- react-native-alarm-notification (from `../node_modules/react-native-alarm-notification`)
- react-native-alarm-notification (from `../node_modules/joplin-rn-alarm-notification`)
- react-native-camera (from `../node_modules/react-native-camera`)
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
- "react-native-geolocation (from `../node_modules/@react-native-community/geolocation`)"
@@ -475,7 +475,7 @@ EXTERNAL SOURCES:
React-jsinspector:
:path: "../node_modules/react-native/ReactCommon/jsinspector"
react-native-alarm-notification:
:path: "../node_modules/react-native-alarm-notification"
:path: "../node_modules/joplin-rn-alarm-notification"
react-native-camera:
:path: "../node_modules/react-native-camera"
react-native-document-picker:
@@ -568,7 +568,7 @@ SPEC CHECKSUMS:
React-jsi: df07aa95b39c5be3e41199921509bfa929ed2b9d
React-jsiexecutor: b56c03e61c0dd5f5801255f2160a815f4a53d451
React-jsinspector: 8e68ffbfe23880d3ee9bafa8be2777f60b25cbe2
react-native-alarm-notification: 8f2adb4521bf7ca867278d4c236553e154dbbbc6
react-native-alarm-notification: 466e4ad56fbd948ecac26e657f292dca8bf483d5
react-native-camera: 5c1fbfecf63b802b8ca4a71c60d30a71550fb348
react-native-document-picker: b3e78a8f7fef98b5cb069f20fc35797d55e68e28
react-native-geolocation: cbd9d6bd06bac411eed2671810f454d4908484a8

View File

@@ -23,6 +23,7 @@
"@react-native-community/slider": "^3.0.3",
"buffer": "^5.0.8",
"events": "^3.2.0",
"joplin-rn-alarm-notification": "^1.0.3",
"jsc-android": "241213.1.0",
"md5": "^2.2.1",
"prop-types": "^15.6.0",
@@ -30,7 +31,6 @@
"react": "16.13.1",
"react-native": "0.63.3",
"react-native-action-button": "^2.8.5",
"react-native-alarm-notification": "^1.7.1",
"react-native-camera": "^3.40.0",
"react-native-dialogbox": "^0.6.10",
"react-native-document-picker": "^4.0.0",

View File

@@ -2,7 +2,7 @@ const React = require('react');
import shim from '@joplin/lib/shim';
shim.setReact(React);
import setUpQuickActions from './setUpQuickActions';
import setupQuickActions from './setupQuickActions';
import PluginAssetsLoader from './PluginAssetsLoader';
import AlarmService from '@joplin/lib/services/AlarmService';
import Alarm from '@joplin/lib/models/Alarm';
@@ -98,6 +98,7 @@ import MigrationService from '@joplin/lib/services/MigrationService';
import { clearSharedFilesCache } from './utils/ShareUtils';
import setIgnoreTlsErrors from './utils/TlsUtils';
import ShareService from '@joplin/lib/services/share/ShareService';
import setupNotifications from './utils/setupNotifications';
let storeDispatch = function(_action: any) {};
@@ -715,7 +716,9 @@ class AppComponent extends React.Component {
await this.handleShareData();
setUpQuickActions(this.props.dispatch, this.props.selectedFolderId);
setupQuickActions(this.props.dispatch, this.props.selectedFolderId);
await setupNotifications(this.props.dispatch);
}
componentWillUnmount() {

View File

@@ -1,7 +1,7 @@
import Logger from '@joplin/lib/Logger';
import { Notification } from '@joplin/lib/models/Alarm';
const ReactNativeAN = require('react-native-alarm-notification').default;
const ReactNativeAN = require('joplin-rn-alarm-notification').default;
export default class AlarmServiceDriver {
@@ -30,9 +30,11 @@ export default class AlarmServiceDriver {
// Returns -1 if could not be found
private alarmJoplinAlarmId(alarm: any): number {
if (!alarm.data) return -1;
const m = alarm.data.match(/joplinNotificationId==>(\d+)/);
return m ? Number(m[1]) : -1;
if (!alarm.data || !alarm.data.joplinNotificationId) {
return -1;
} else {
return alarm.data.joplinNotificationId;
}
}
private async alarmByJoplinNotificationId(joplinNotificationId: number) {
@@ -51,9 +53,12 @@ export default class AlarmServiceDriver {
title: notification.title,
message: notification.body ? notification.body : '-', // Required
channel: 'net.cozic.joplin.notification',
small_icon: 'ic_launcher',
color: 'white',
data: { joplinNotificationId: `${notification.id}` },
small_icon: 'ic_launcher_foreground', // Android requires the icon to be transparent
color: 'blue',
data: {
joplinNotificationId: `${notification.id}`,
noteId: notification.noteId,
},
};
// ReactNativeAN expects a string as a date and it seems this utility

View File

@@ -0,0 +1,37 @@
import { Dispatch } from 'redux';
const { NativeEventEmitter, NativeModules, Platform } = require('react-native');
interface NotificationData {
joplinNotificationId: string;
noteId: string;
}
export default async (dispatch: Dispatch) => {
if (Platform.OS === 'android') {
const RNAlarmNotification = NativeModules.RNAlarmNotification;
const RNAlarmEmitter = new NativeEventEmitter(RNAlarmNotification);
const handleNotification = async (notification: NotificationData) => {
if (notification) {
const noteId = notification.noteId;
if (noteId) {
dispatch({ type: 'NAV_BACK' });
dispatch({ type: 'SIDE_MENU_CLOSE' });
dispatch({
type: 'NAV_GO',
noteId: noteId,
routeName: 'Note',
});
}
}
};
// receive notification click events when the app is running
RNAlarmEmitter.addListener('OnNotificationOpened', handleNotification);
// retrieve notification info if the app was started after the user clicked notification
const notification = await RNAlarmNotification.getAlarmInfo();
await handleNotification(notification);
}
};

View File

@@ -1,9 +1,4 @@
/* eslint-disable no-unused-vars */
const time = require('@joplin/lib/time').default;
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const ArrayUtils = require('@joplin/lib/ArrayUtils');
const ArrayUtils = require('./ArrayUtils');
describe('ArrayUtils', function() {

View File

@@ -1,5 +1,5 @@
import InMemoryCache from '@joplin/lib/InMemoryCache';
import time from '@joplin/lib/time';
import InMemoryCache from './InMemoryCache';
import time from './time';
describe('InMemoryCache', function() {

View File

@@ -1,6 +1,6 @@
/* eslint-disable no-unused-vars */
const StringUtils = require('@joplin/lib/string-utils');
const StringUtils = require('./string-utils');
describe('StringUtils', function() {

View File

@@ -1,8 +1,5 @@
/* eslint-disable no-unused-vars */
const { fileContentEqual, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const TaskQueue = require('@joplin/lib/TaskQueue.js');
const { setupDatabaseAndSynchronizer, sleep, switchClient } = require('./testing/test-utils.js');
const TaskQueue = require('./TaskQueue.js');
describe('TaskQueue', function() {

View File

@@ -0,0 +1,22 @@
const { setupDatabaseAndSynchronizer, switchClient } = require('./testing/test-utils.js');
const BaseModel = require('./BaseModel').default;
describe('database', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should not modify cached field names', (async () => {
const db = BaseModel.db();
const fieldNames = db.tableFieldNames('notes');
const fieldCount = fieldNames.length;
fieldNames.push('type_');
expect(fieldCount).toBeGreaterThan(0);
expect(db.tableFieldNames('notes').length).toBe(fieldCount);
}));
});

View File

@@ -1,8 +1,8 @@
'use strict';
const { checkThrow } = require('./test-utils.js');
const eventManager = require('@joplin/lib/eventManager').default;
const { checkThrow } = require('./testing/test-utils.js');
const eventManager = require('./eventManager').default;
describe('eventManager', function() {

View File

@@ -1,3 +1,4 @@
import JoplinError from './JoplinError';
import JoplinServerApi from './JoplinServerApi';
import { trimSlashes } from './path-utils';
@@ -148,9 +149,17 @@ export default class FileApiDriverJoplinServer {
}
public async put(path: string, content: any, options: any = null) {
return this.api().exec('PUT', `${this.apiFilePath_(path)}/content`, options && options.shareId ? { share_id: options.shareId } : null, content, {
'Content-Type': 'application/octet-stream',
}, options);
try {
const output = await this.api().exec('PUT', `${this.apiFilePath_(path)}/content`, options && options.shareId ? { share_id: options.shareId } : null, content, {
'Content-Type': 'application/octet-stream',
}, options);
return output;
} catch (error) {
if (error.code === 413) {
throw new JoplinError(error.message, 'rejectedByTarget');
}
throw error;
}
}
public async delete(path: string) {

View File

@@ -1,4 +1,4 @@
import { afterAllCleanUp, setupDatabaseAndSynchronizer, switchClient, fileApi } from './test-utils';
import { afterAllCleanUp, setupDatabaseAndSynchronizer, switchClient, fileApi } from './testing/test-utils';
describe('file-api-driver', function() {

View File

@@ -1,6 +1,6 @@
import FsDriverNode from '@joplin/lib/fs-driver-node';
import shim from '@joplin/lib/shim';
const { expectThrow } = require('./test-utils.js');
import FsDriverNode from './fs-driver-node';
import shim from './shim';
const { expectThrow } = require('./testing/test-utils.js');
// On Windows, path.resolve is going to convert a path such as
// /tmp/file.txt to c:\tmp\file.txt

View File

@@ -0,0 +1,28 @@
import { plainTextToHtml } from './htmlUtils';
describe('htmlUtils', () => {
test('should convert a plain text string to its HTML equivalent', () => {
const testCases = [
[
'',
'',
],
[
'line 1\nline 2',
'<p>line 1</p><p>line 2</p>',
],
[
'<img onerror="http://downloadmalware.com"/>',
'&lt;img onerror=&quot;http://downloadmalware.com&quot;/&gt;',
],
];
for (const t of testCases) {
const [input, expected] = t;
const actual = plainTextToHtml(input);
expect(actual).toBe(expected);
}
});
});

View File

@@ -2,6 +2,7 @@ const urlUtils = require('./urlUtils.js');
const Entities = require('html-entities').AllHtmlEntities;
const htmlentities = new Entities().encode;
const htmlparser2 = require('@joplin/fork-htmlparser2');
const { escapeHtml } = require('./string-utils.js');
// [\s\S] instead of . for multiline matching
// https://stackoverflow.com/a/16119722/561309
@@ -153,3 +154,16 @@ class HtmlUtils {
}
export default new HtmlUtils();
export function plainTextToHtml(plainText: string): string {
const lines = plainText
.replace(/[\n\r]/g, '\n')
.split('\n');
const lineOpenTag = lines.length > 1 ? '<p>' : '';
const lineCloseTag = lines.length > 1 ? '</p>' : '';
return lines
.map(line => lineOpenTag + escapeHtml(line) + lineCloseTag)
.join('');
}

View File

@@ -1,4 +1,4 @@
import htmlUtils from '@joplin/lib/htmlUtils';
import htmlUtils from './htmlUtils';
describe('htmlUtils', function() {

View File

@@ -1,17 +1,17 @@
import { NoteEntity, ResourceEntity, TagEntity } from '@joplin/lib/services/database/types';
import shim from '@joplin/lib/shim';
import { NoteEntity, ResourceEntity, TagEntity } from './services/database/types';
import shim from './shim';
const fs = require('fs-extra');
const os = require('os');
const { filename } = require('@joplin/lib/path-utils');
const { setupDatabaseAndSynchronizer, switchClient, expectNotThrow } = require('./test-utils.js');
const { enexXmlToMd } = require('@joplin/lib/import-enex-md-gen.js');
const { importEnex } = require('@joplin/lib/import-enex');
import Note from '@joplin/lib/models/Note';
import Tag from '@joplin/lib/models/Tag';
import Resource from '@joplin/lib/models/Resource';
const { filename } = require('./path-utils');
const { setupDatabaseAndSynchronizer, switchClient, expectNotThrow, supportDir } = require('./testing/test-utils.js');
const { enexXmlToMd } = require('./import-enex-md-gen.js');
const { importEnex } = require('./import-enex');
import Note from './models/Note';
import Tag from './models/Tag';
import Resource from './models/Resource';
const enexSampleBaseDir = `${__dirname}/enex_to_md`;
const enexSampleBaseDir = `${supportDir}/../enex_to_md`;
describe('EnexToMd', function() {

View File

@@ -10,4 +10,7 @@ module.exports = {
],
testEnvironment: 'node',
setupFilesAfterEnv: [`${__dirname}/jest.setup.js`],
slowTestThreshold: 40,
};

View File

@@ -0,0 +1,18 @@
const { afterEachCleanUp } = require('./testing/test-utils.js');
const { shimInit } = require('./shim-init-node.js');
const shim = require('./shim').default;
const sharp = require('sharp');
let keytar;
try {
keytar = shim.platformSupportsKeyChain() ? require('keytar') : null;
} catch (error) {
console.error('Cannot load keytar - keychain support will be disabled', error);
keytar = null;
}
shimInit(sharp, keytar);
global.afterEach(async () => {
await afterEachCleanUp();
});

View File

@@ -61,7 +61,7 @@ const markdownUtils = {
},
// Returns the **encoded** URLs, so to be useful they should be decoded again before use.
extractImageUrls(md: string) {
extractFileUrls(md: string, onlyImage: boolean = false): Array<string> {
const markdownIt = new MarkdownIt();
markdownIt.validateLink = validateLinks; // Necessary to support file:/// links
@@ -72,11 +72,10 @@ const markdownUtils = {
const searchUrls = (tokens: any[]) => {
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (token.type === 'image') {
if ((onlyImage === true && token.type === 'image') || (onlyImage === false && (token.type === 'image' || token.type === 'link_open'))) {
for (let j = 0; j < token.attrs.length; j++) {
const a = token.attrs[j];
if (a[0] === 'src' && a.length >= 2 && a[1]) {
if ((a[0] === 'src' || a[0] === 'href') && a.length >= 2 && a[1]) {
output.push(a[1]);
}
}
@@ -93,6 +92,10 @@ const markdownUtils = {
return output;
},
extractImageUrls(md: string) {
return markdownUtils.extractFileUrls(md,true);
},
// The match results has 5 items
// Full match array is
// [Full match, whitespace, list token, ol line number, whitespace following token]

View File

@@ -1,6 +1,6 @@
/* eslint-disable no-unused-vars */
const markdownUtils = require('@joplin/lib/markdownUtils').default;
const markdownUtils = require('./markdownUtils').default;
describe('markdownUtils', function() {
@@ -33,14 +33,48 @@ describe('markdownUtils', function() {
['![something](http://test.com/img.png "Some description")', ['http://test.com/img.png']],
['![something](https://test.com/ohoh_(123).png)', ['https://test.com/ohoh_(123).png']],
['![nothing]() ![something](http://test.com/img.png)', ['http://test.com/img.png']],
['![something](img.png)', ['img.png']],
['![something](/img.png)', ['/img.png']],
['![something](../img.png)', ['../img.png']],
['![something](../upload/img.png)', ['../upload/img.png']],
['![something](./upload/img.png)', ['./upload/img.png']],
['[something](testing.html)', ['']],
['[something](img.png)', ['']],
['![something](file://img.png)', ['file://img.png']],
];
for (let i = 0; i < testCases.length; i++) {
const md = testCases[i][0];
const actual = markdownUtils.extractImageUrls(md);
const expected = testCases[i][1];
expect(actual.join(' ')).toBe((expected as string[]).join(' '));
}
}));
expect(actual.join(' ')).toBe(expected.join(' '));
it('should extract files URLs', (async () => {
const testCases = [
['[something](http://test.com/img.png)', ['http://test.com/img.png']],
['[something](http://test.com/test.txt)', ['http://test.com/test.txt']],
['[something](http://test.com/img.png) ![something2](http://test.com/img2.png)', ['http://test.com/img.png', 'http://test.com/img2.png']],
['[something](http://test.com/img.png "Some description")', ['http://test.com/img.png']],
['[something](https://test.com/ohoh_(123).png)', ['https://test.com/ohoh_(123).png']],
['[nothing]() ![something](http://test.com/img.png)', ['http://test.com/img.png']],
['[something](test.txt)', ['test.txt']],
['[something](/test.txt)', ['/test.txt']],
['[something](../test.txt)', ['../test.txt']],
['[something](../upload/test.txt)', ['../upload/test.txt']],
['[something](./upload/test.txt)', ['./upload/test.txt']],
['[something](testing.html)', ['testing.html']],
['[something](img.png)', ['img.png']],
['[something](file://img.png)', ['file://img.png']],
];
for (let i = 0; i < testCases.length; i++) {
const md = testCases[i][0];
const actual = markdownUtils.extractFileUrls(md);
const expected = testCases[i][1];
expect(actual.join(' ')).toBe((expected as string[]).join(' '));
}
}));

View File

@@ -1,10 +1,11 @@
import markdownUtils from './markdownUtils';
import Setting from './models/Setting';
import shim from './shim';
import MarkupToHtml, { MarkupLanguage } from '@joplin/renderer/MarkupToHtml';
import MarkupToHtml, { MarkupLanguage, Options } from '@joplin/renderer/MarkupToHtml';
import htmlUtils from './htmlUtils';
import Resource from './models/Resource';
import { PluginStates } from './services/plugins/reducer';
export class MarkupLanguageUtils {
@@ -20,7 +21,7 @@ export class MarkupLanguageUtils {
// Create a new MarkupToHtml instance while injecting options specific to Joplin
// desktop and mobile applications.
public newMarkupToHtml(options: any = null) {
public newMarkupToHtml(_plugins: PluginStates = null, options: Options = null) {
const subValues = Setting.subValues('markdown.plugin', Setting.toPlainObject());
const pluginOptions: any = {};
for (const n in subValues) {

View File

@@ -1,9 +1,4 @@
/* eslint-disable no-unused-vars */
const time = require('@joplin/lib/time').default;
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const mimeUtils = require('@joplin/lib/mime-utils.js').mime;
const mimeUtils = require('./mime-utils.js').mime;
describe('mimeUils', function() {

View File

@@ -1,20 +1,6 @@
/* eslint-disable no-unused-vars */
const time = require('@joplin/lib/time').default;
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
const BaseItem = require('@joplin/lib/models/BaseItem').default;
const Resource = require('@joplin/lib/models/Resource').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
const shim = require('@joplin/lib/shim').default;
async function allItems() {
const folders = await Folder.all();
const notes = await Note.all();
return folders.concat(notes);
}
const { setupDatabaseAndSynchronizer, switchClient } = require('../testing/test-utils.js');
const Folder = require('../models/Folder').default;
const Note = require('../models/Note').default;
describe('models_BaseItem', function() {

View File

@@ -1,13 +1,13 @@
import { setupDatabaseAndSynchronizer, switchClient, createFolderTree } from './test-utils';
import Folder from '@joplin/lib/models/Folder';
import { allNotesFolders } from './test-utils-synchronizer';
import Note from '@joplin/lib/models/Note';
import shim from '@joplin/lib/shim';
import Resource from '@joplin/lib/models/Resource';
import { FolderEntity, NoteEntity, ResourceEntity } from '@joplin/lib/services/database/types';
import ResourceService from '@joplin/lib/services/ResourceService';
import { setupDatabaseAndSynchronizer, switchClient, createFolderTree, supportDir } from '../testing/test-utils';
import Folder from '../models/Folder';
import { allNotesFolders } from '../testing/test-utils-synchronizer';
import Note from '../models/Note';
import shim from '../shim';
import Resource from '../models/Resource';
import { FolderEntity, NoteEntity, ResourceEntity } from '../services/database/types';
import ResourceService from '../services/ResourceService';
const testImagePath = `${__dirname}/../tests/support/photo.jpg`;
const testImagePath = `${supportDir}/photo.jpg`;
describe('models_Folder.sharing', function() {

View File

@@ -1,7 +1,7 @@
import { FolderEntity } from '@joplin/lib/services/database/types';
import { createNTestNotes, setupDatabaseAndSynchronizer, sleep, switchClient, checkThrowAsync, createFolderTree } from './test-utils';
import Folder from '@joplin/lib/models/Folder';
import Note from '@joplin/lib/models/Note';
import { FolderEntity } from '../services/database/types';
import { createNTestNotes, setupDatabaseAndSynchronizer, sleep, switchClient, checkThrowAsync, createFolderTree } from '../testing/test-utils';
import Folder from './Folder';
import Note from './Note';
async function allItems() {
const folders = await Folder.all();

View File

@@ -1,14 +1,9 @@
/* eslint-disable no-unused-vars */
const time = require('@joplin/lib/time').default;
const { fileContentEqual, revisionService, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const SearchEngine = require('@joplin/lib/services/searchengine/SearchEngine').default;
const ResourceService = require('@joplin/lib/services/ResourceService').default;
const ItemChangeUtils = require('@joplin/lib/services/ItemChangeUtils').default;
const Note = require('@joplin/lib/models/Note').default;
const Setting = require('@joplin/lib/models/Setting').default;
const ItemChange = require('@joplin/lib/models/ItemChange').default;
const { revisionService, setupDatabaseAndSynchronizer, db, switchClient } = require('../testing/test-utils.js');
const SearchEngine = require('../services/searchengine/SearchEngine').default;
const ResourceService = require('../services/ResourceService').default;
const ItemChangeUtils = require('../services/ItemChangeUtils').default;
const Note = require('../models/Note').default;
const ItemChange = require('../models/ItemChange').default;
let searchEngine = null;
@@ -23,7 +18,7 @@ describe('models_ItemChange', function() {
});
it('should delete old changes that have been processed', (async () => {
const n1 = await Note.save({ title: 'abcd efgh' }); // 3
await Note.save({ title: 'abcd efgh' });
await ItemChange.waitForAllSaved();

View File

@@ -1,12 +1,12 @@
import Setting from '@joplin/lib/models/Setting';
import BaseModel from '@joplin/lib/BaseModel';
import shim from '@joplin/lib/shim';
import markdownUtils from '@joplin/lib/markdownUtils';
const { sortedIds, createNTestNotes, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('./test-utils.js');
import Folder from '@joplin/lib/models/Folder';
import Note from '@joplin/lib/models/Note';
import Tag from '@joplin/lib/models/Tag';
const ArrayUtils = require('@joplin/lib/ArrayUtils.js');
import Setting from './Setting';
import BaseModel from '../BaseModel';
import shim from '../shim';
import markdownUtils from '../markdownUtils';
import { sortedIds, createNTestNotes, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync, supportDir } from '../testing/test-utils';
import Folder from './Folder';
import Note from './Note';
import Tag from './Tag';
const ArrayUtils = require('../ArrayUtils.js');
async function allItems() {
const folders = await Folder.all();
@@ -30,15 +30,15 @@ describe('models_Note', function() {
expect(items.length).toBe(1);
expect(items[0].id).toBe(note1.id);
await shim.attachFileToNote(note2, `${__dirname}/../tests/support/photo.jpg`);
await shim.attachFileToNote(note2, `${supportDir}/photo.jpg`);
note2 = await Note.load(note2.id);
items = await Note.linkedItems(note2.body);
expect(items.length).toBe(2);
expect(items[0].type_).toBe(BaseModel.TYPE_NOTE);
expect(items[1].type_).toBe(BaseModel.TYPE_RESOURCE);
const resource2 = await shim.createResourceFromPath(`${__dirname}/../tests/support/photo.jpg`);
const resource3 = await shim.createResourceFromPath(`${__dirname}/../tests/support/photo.jpg`);
const resource2 = await shim.createResourceFromPath(`${supportDir}/photo.jpg`);
const resource3 = await shim.createResourceFromPath(`${supportDir}/photo.jpg`);
note2.body += `<img alt="bla" src=":/${resource2.id}"/>`;
note2.body += `<img src=':/${resource3.id}' />`;
items = await Note.linkedItems(note2.body);
@@ -228,9 +228,9 @@ describe('models_Note', function() {
it('should convert resource paths from internal to external paths', (async () => {
const resourceDirName = Setting.value('resourceDirName');
const resourceDir = Setting.value('resourceDir');
const r1 = await shim.createResourceFromPath(`${__dirname}/../tests/support/photo.jpg`);
const r2 = await shim.createResourceFromPath(`${__dirname}/../tests/support/photo.jpg`);
const r3 = await shim.createResourceFromPath(`${__dirname}/../tests/support/welcome.pdf`);
const r1 = await shim.createResourceFromPath(`${supportDir}/photo.jpg`);
const r2 = await shim.createResourceFromPath(`${supportDir}/photo.jpg`);
const r3 = await shim.createResourceFromPath(`${supportDir}/welcome.pdf`);
const note1 = await Note.save({ title: 'note1' });
const t1 = r1.updated_time;
const t2 = r2.updated_time;

View File

@@ -1,20 +1,7 @@
/* eslint-disable no-unused-vars */
const time = require('@joplin/lib/time').default;
const { sortedIds, createNTestNotes, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
const Setting = require('@joplin/lib/models/Setting').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
const ArrayUtils = require('@joplin/lib/ArrayUtils.js');
const shim = require('@joplin/lib/shim').default;
async function allItems() {
const folders = await Folder.all();
const notes = await Note.all();
return folders.concat(notes);
}
const time = require('../time').default;
const { setupDatabaseAndSynchronizer, switchClient } = require('../testing/test-utils.js');
const Folder = require('../models/Folder').default;
const Note = require('../models/Note').default;
describe('models_Note_CustomSortOrder', function() {
beforeEach(async (done) => {

View File

@@ -1,15 +1,10 @@
/* eslint-disable no-unused-vars, require-atomic-updates */
const { supportDir, setupDatabaseAndSynchronizer, switchClient } = require('../testing/test-utils.js');
const Folder = require('../models/Folder').default;
const Note = require('../models/Note').default;
const Resource = require('../models/Resource').default;
const shim = require('../shim').default;
const time = require('@joplin/lib/time').default;
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
const Resource = require('@joplin/lib/models/Resource').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
const shim = require('@joplin/lib/shim').default;
const testImagePath = `${__dirname}/../tests/support/photo.jpg`;
const testImagePath = `${supportDir}/photo.jpg`;
describe('models_Resource', function() {

View File

@@ -1,15 +1,6 @@
/* eslint-disable no-unused-vars */
const time = require('@joplin/lib/time').default;
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
const NoteTag = require('@joplin/lib/models/NoteTag').default;
const Tag = require('@joplin/lib/models/Tag').default;
const Revision = require('@joplin/lib/models/Revision').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
const shim = require('@joplin/lib/shim').default;
const { setupDatabaseAndSynchronizer, switchClient } = require('../testing/test-utils.js');
const Note = require('../models/Note').default;
const Revision = require('../models/Revision').default;
describe('models_Revision', function() {

View File

@@ -1,7 +1,7 @@
import Setting, { SettingSectionSource } from '@joplin/lib/models/Setting';
import { setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow, msleep } from './test-utils';
import Setting, { SettingSectionSource } from '../models/Setting';
import { setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow, msleep } from '../testing/test-utils';
import * as fs from 'fs-extra';
import Logger from '@joplin/lib/Logger';
import Logger from '../Logger';
async function loadSettingsFromFile(): Promise<any> {
return JSON.parse(await fs.readFile(Setting.settingFilePath, 'utf8'));

View File

@@ -1,14 +1,7 @@
/* eslint-disable no-unused-vars */
const time = require('@joplin/lib/time').default;
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
const NoteTag = require('@joplin/lib/models/NoteTag').default;
const Tag = require('@joplin/lib/models/Tag').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
const shim = require('@joplin/lib/shim').default;
const { setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('../testing/test-utils.js');
const Folder = require('../models/Folder').default;
const Note = require('../models/Note').default;
const Tag = require('../models/Tag').default;
describe('models_Tag', function() {
@@ -97,7 +90,7 @@ describe('models_Tag', function() {
const taga = await Tag.save({ title: 'mytaga' });
const tagb = await Tag.save({ title: 'mytagb' });
const tagc = await Tag.save({ title: 'mytagc' });
const tagd = await Tag.save({ title: 'mytagd' });
await Tag.save({ title: 'mytagd' });
const note0 = await Note.save({ title: 'ma note 0', parent_id: folder1.id });
const note1 = await Note.save({ title: 'ma note 1', parent_id: folder1.id });

View File

@@ -1,5 +1,5 @@
import Setting from '@joplin/lib/models/Setting';
import time from '@joplin/lib/time';
import Setting from '../models/Setting';
import time from '../time';
describe('dateFormats', function() {

View File

@@ -777,6 +777,15 @@
"@babel/types": "^7.3.0"
}
},
"@types/fs-extra": {
"version": "9.0.11",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.11.tgz",
"integrity": "sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/graceful-fs": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz",
@@ -1014,6 +1023,12 @@
"typical": "^2.6.1"
}
},
"array-flatten": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz",
"integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==",
"dev": true
},
"array-unique": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
@@ -1265,6 +1280,40 @@
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
"integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ=="
},
"bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dev": true,
"requires": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
},
"dependencies": {
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"dev": true,
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"block-stream": {
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
@@ -1494,6 +1543,16 @@
"source-map": "~0.6.0"
}
},
"clean-html": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/clean-html/-/clean-html-1.5.0.tgz",
"integrity": "sha512-eDu0vN44ZBvoEU0oRIKwWPIccGWXtdnUNmKJuTukZ1de00Uoqavb5pfIMKiC7/r+knQ5RbvAjGuVZiN3JwJL4Q==",
"dev": true,
"requires": {
"htmlparser2": "^3.8.2",
"minimist": "^1.1.1"
}
},
"cliss": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/cliss/-/cliss-0.0.2.tgz",
@@ -1788,6 +1847,15 @@
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
},
"decompress-response": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
"dev": true,
"requires": {
"mimic-response": "^2.0.0"
}
},
"deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
@@ -1882,6 +1950,55 @@
"integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==",
"dev": true
},
"dom-serializer": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
"integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
"dev": true,
"requires": {
"domelementtype": "^2.0.1",
"entities": "^2.0.0"
},
"dependencies": {
"domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
"dev": true
},
"entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"dev": true
}
}
},
"domelementtype": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
"integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
"dev": true
},
"domhandler": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
"integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
"dev": true,
"requires": {
"domelementtype": "1"
}
},
"domutils": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
"integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
"dev": true,
"requires": {
"dom-serializer": "0",
"domelementtype": "1"
}
},
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@@ -1930,6 +2047,12 @@
"once": "^1.4.0"
}
},
"entities": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
"dev": true
},
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -2071,6 +2194,12 @@
}
}
},
"expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
"dev": true
},
"expect": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz",
@@ -2284,6 +2413,12 @@
"map-cache": "^0.2.2"
}
},
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"dev": true
},
"fs-extra": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
@@ -2404,6 +2539,12 @@
"assert-plus": "^1.0.0"
}
},
"github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=",
"dev": true
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
@@ -2563,6 +2704,33 @@
"uglify-js": "3.4.x"
}
},
"htmlparser2": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
"integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
"dev": true,
"requires": {
"domelementtype": "^1.3.1",
"domhandler": "^2.3.0",
"domutils": "^1.5.1",
"entities": "^1.1.1",
"inherits": "^2.0.1",
"readable-stream": "^3.1.1"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"http-errors": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz",
@@ -3891,6 +4059,24 @@
"verror": "1.10.0"
}
},
"keytar": {
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/keytar/-/keytar-7.7.0.tgz",
"integrity": "sha512-YEY9HWqThQc5q5xbXbRwsZTh2PJ36OSYRjSv3NN2xf5s5dpLTjEZnC2YikR29OaVybf9nQ0dJ/80i40RS97t/A==",
"dev": true,
"requires": {
"node-addon-api": "^3.0.0",
"prebuild-install": "^6.0.0"
},
"dependencies": {
"node-addon-api": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.0.tgz",
"integrity": "sha512-kcwSAWhPi4+QzAtsL2+2s/awvDo2GKLsvMCwNRxb5BUshteXU8U97NCyvQDsGKs/m0He9WcG4YWew/BnuLx++w==",
"dev": true
}
}
},
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@@ -3994,6 +4180,15 @@
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
},
"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,
"requires": {
"yallist": "^4.0.0"
}
},
"magicli": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.8.tgz",
@@ -4120,6 +4315,12 @@
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
},
"mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
"dev": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -4162,6 +4363,12 @@
"minimist": "^1.2.5"
}
},
"mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
"dev": true
},
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
@@ -4219,6 +4426,12 @@
}
}
},
"napi-build-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
"dev": true
},
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -4249,6 +4462,23 @@
"lower-case": "^1.1.1"
}
},
"node-abi": {
"version": "2.26.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.26.0.tgz",
"integrity": "sha512-ag/Vos/mXXpWLLAYWsAoQdgS+gW7IwvgMLOgqopm/DbzAjazLltzgzpVMsFlgmo9TzG5hGXeaBZx2AI731RIsQ==",
"dev": true,
"requires": {
"semver": "^5.4.1"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
"node-addon-api": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz",
@@ -4387,6 +4617,12 @@
}
}
},
"noop-logger": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
"integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=",
"dev": true
},
"nopt": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
@@ -4788,6 +5024,28 @@
"integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
"dev": true
},
"prebuild-install": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.2.tgz",
"integrity": "sha512-PzYWIKZeP+967WuKYXlTOhYBgGOvTRSfaKI89XnfJ0ansRAH7hDU45X+K+FZeI1Wb/7p/NnuctPH3g0IqKUuSQ==",
"dev": true,
"requires": {
"detect-libc": "^1.0.3",
"expand-template": "^2.0.3",
"github-from-package": "0.0.0",
"minimist": "^1.2.3",
"mkdirp-classic": "^0.5.3",
"napi-build-utils": "^1.0.1",
"node-abi": "^2.21.0",
"noop-logger": "^0.1.1",
"npmlog": "^4.0.1",
"pump": "^3.0.0",
"rc": "^1.2.7",
"simple-get": "^3.0.3",
"tar-fs": "^2.0.0",
"tunnel-agent": "^0.6.0"
}
},
"prelude-ls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
@@ -5440,6 +5698,77 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"sharp": {
"version": "0.26.3",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.26.3.tgz",
"integrity": "sha512-NdEJ9S6AMr8Px0zgtFo1TJjMK/ROMU92MkDtYn2BBrDjIx3YfH9TUyGdzPC+I/L619GeYQc690Vbaxc5FPCCWg==",
"dev": true,
"requires": {
"array-flatten": "^3.0.0",
"color": "^3.1.3",
"detect-libc": "^1.0.3",
"node-addon-api": "^3.0.2",
"npmlog": "^4.1.2",
"prebuild-install": "^6.0.0",
"semver": "^7.3.2",
"simple-get": "^4.0.0",
"tar-fs": "^2.1.1",
"tunnel-agent": "^0.6.0"
},
"dependencies": {
"color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz",
"integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==",
"dev": true,
"requires": {
"color-convert": "^1.9.1",
"color-string": "^1.5.4"
}
},
"decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dev": true,
"requires": {
"mimic-response": "^3.1.0"
}
},
"mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"dev": true
},
"node-addon-api": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.0.tgz",
"integrity": "sha512-kcwSAWhPi4+QzAtsL2+2s/awvDo2GKLsvMCwNRxb5BUshteXU8U97NCyvQDsGKs/m0He9WcG4YWew/BnuLx++w==",
"dev": true
},
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"simple-get": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.0.tgz",
"integrity": "sha512-ZalZGexYr3TA0SwySsr5HlgOOinS4Jsa8YB2GJ6lUNAazyAu4KG/VmzMTwAt2YVXzzVj8QmefmAonZIK2BSGcQ==",
"dev": true,
"requires": {
"decompress-response": "^6.0.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
}
}
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@@ -5465,6 +5794,23 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
},
"simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"dev": true
},
"simple-get": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
"dev": true,
"requires": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
@@ -6029,6 +6375,44 @@
}
}
},
"tar-fs": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
"dev": true,
"requires": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.1.4"
}
},
"tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dev": true,
"requires": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"tcp-port-used": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-0.1.2.tgz",
@@ -6600,6 +6984,12 @@
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"dev": true
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",

View File

@@ -18,8 +18,12 @@
"devDependencies": {
"@types/jest": "^26.0.15",
"@types/node": "^14.14.6",
"@types/fs-extra": "^9.0.6",
"jest": "^26.6.3",
"typescript": "^4.0.5"
"sharp": "^0.26.2",
"keytar": "^7.0.0",
"typescript": "^4.0.5",
"clean-html": "^1.5.0"
},
"dependencies": {
"@joplin/fork-htmlparser2": "^4.1.24",

View File

@@ -1,8 +1,4 @@
/* eslint-disable no-unused-vars */
const { extractExecutablePath, quotePath, unquotePath, friendlySafeFilename, toFileProtocolPath } = require('@joplin/lib/path-utils');
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const { extractExecutablePath, quotePath, unquotePath, friendlySafeFilename, toFileProtocolPath } = require('./path-utils');
describe('pathUtils', function() {

View File

@@ -1,12 +1,7 @@
/* eslint-disable no-unused-vars */
const { setupDatabaseAndSynchronizer, switchClient, createNTestNotes, createNTestFolders, createNTestTags } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
const Tag = require('@joplin/lib/models/Tag').default;
const reducer = require('@joplin/lib/reducer').default;
const { defaultState, stateUtils, MAX_HISTORY } = require('@joplin/lib/reducer');
const { ALL_NOTES_FILTER_ID } = require('@joplin/lib/reserved-ids');
const { setupDatabaseAndSynchronizer, switchClient, createNTestNotes, createNTestFolders, createNTestTags } = require('./testing/test-utils.js');
const reducer = require('./reducer').default;
const { defaultState, MAX_HISTORY } = require('./reducer');
const { ALL_NOTES_FILTER_ID } = require('./reserved-ids');
function initTestState(folders, selectedFolderIndex, notes, selectedNoteIndexes, tags = null, selectedTagIndex = null) {
let state = defaultState;
@@ -86,19 +81,13 @@ function getIds(items, indexes = null) {
return ids;
}
let insideBeforeEach = false;
describe('reducer', function() {
beforeEach(async (done) => {
insideBeforeEach = true;
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
insideBeforeEach = false;
});
// tests for NOTE_DELETE
@@ -560,7 +549,6 @@ describe('reducer', function() {
// select the 1st folder and the 1st note
let state = initTestState(folders, 0, notes, [0]);
const idx = 0;
for (let i = 0; i < 2 * MAX_HISTORY; i++) {
state = goToNote(notes, [i % 5], state);
}

View File

@@ -1,5 +1,5 @@
import Setting from '@joplin/lib/models/Setting';
import { reg } from '@joplin/lib/registry';
import Setting from './models/Setting';
import { reg } from './registry';
const sync = {
start: jest.fn().mockReturnValue({}),

View File

@@ -1,10 +1,10 @@
import MenuUtils from '@joplin/lib/services/commands/MenuUtils';
import ToolbarButtonUtils from '@joplin/lib/services/commands/ToolbarButtonUtils';
import CommandService, { CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
import stateToWhenClauseContext from '@joplin/lib/services/commands/stateToWhenClauseContext';
import KeymapService from '@joplin/lib/services/KeymapService';
import MenuUtils from '../services/commands/MenuUtils';
import ToolbarButtonUtils from '../services/commands/ToolbarButtonUtils';
import CommandService, { CommandDeclaration, CommandRuntime } from '../services/CommandService';
import stateToWhenClauseContext from '../services/commands/stateToWhenClauseContext';
import KeymapService from '../services/KeymapService';
const { setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('./test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('../testing/test-utils.js');
interface TestCommand {
declaration: CommandDeclaration;

View File

@@ -1,18 +1,12 @@
/* eslint-disable no-unused-vars */
/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars, prefer-const */
const time = require('@joplin/lib/time').default;
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
const Tag = require('@joplin/lib/models/Tag').default;
const Database = require('@joplin/lib/database').default;
const Setting = require('@joplin/lib/models/Setting').default;
const BaseItem = require('@joplin/lib/models/BaseItem').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
const MasterKey = require('@joplin/lib/models/MasterKey').default;
const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry.js');
const EncryptionService = require('@joplin/lib/services/EncryptionService').default;
const { fileContentEqual, setupDatabaseAndSynchronizer, supportDir, switchClient, objectsEqual, checkThrowAsync } = require('../testing/test-utils.js');
const Folder = require('../models/Folder').default;
const Note = require('../models/Note').default;
const Setting = require('../models/Setting').default;
const BaseItem = require('../models/BaseItem').default;
const MasterKey = require('../models/MasterKey').default;
const EncryptionService = require('../services/EncryptionService').default;
let service = null;
@@ -256,7 +250,7 @@ describe('services_EncryptionService', function() {
masterKey = await MasterKey.save(masterKey);
await service.loadMasterKey_(masterKey, '123456', true);
const sourcePath = `${__dirname}/../tests/support/photo.jpg`;
const sourcePath = `${supportDir}/photo.jpg`;
const encryptedPath = `${Setting.value('tempDir')}/photo.crypted`;
const decryptedPath = `${Setting.value('tempDir')}/photo.jpg`;

View File

@@ -1,6 +1,6 @@
const { tempFilePath } = require('./test-utils.js');
const KeymapService = require('@joplin/lib/services/KeymapService').default;
const { tempFilePath } = require('../testing/test-utils.js');
const KeymapService = require('../services/KeymapService').default;
const keymapService = KeymapService.instance();
keymapService.initialize([]);

View File

@@ -1,8 +1,5 @@
/* eslint-disable no-unused-vars */
const { fileContentEqual, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const KvStore = require('@joplin/lib/services/KvStore').default;
const { setupDatabaseAndSynchronizer, db, switchClient } = require('../testing/test-utils.js');
const KvStore = require('../services/KvStore').default;
function setupStore() {
const store = KvStore.instance();

View File

@@ -1,13 +1,13 @@
import time from '@joplin/lib/time';
import NoteResource from '@joplin/lib/models/NoteResource';
import ResourceService from '@joplin/lib/services/ResourceService';
import shim from '@joplin/lib/shim';
import time from '../time';
import NoteResource from '../models/NoteResource';
import ResourceService from '../services/ResourceService';
import shim from '../shim';
const { resourceService, decryptionWorker, encryptionService, loadEncryptionMasterKey, allSyncTargetItemsEncrypted, setupDatabaseAndSynchronizer, db, synchronizer, switchClient } = require('./test-utils.js');
import Folder from '@joplin/lib/models/Folder';
import Note from '@joplin/lib/models/Note';
import Resource from '@joplin/lib/models/Resource';
import SearchEngine from '@joplin/lib/services/searchengine/SearchEngine';
const { resourceService, decryptionWorker, supportDir, encryptionService, loadEncryptionMasterKey, allSyncTargetItemsEncrypted, setupDatabaseAndSynchronizer, db, synchronizer, switchClient } = require('../testing/test-utils.js');
import Folder from '../models/Folder';
import Note from '../models/Note';
import Resource from '../models/Resource';
import SearchEngine from '../services/searchengine/SearchEngine';
describe('services_ResourceService', function() {
@@ -23,7 +23,7 @@ describe('services_ResourceService', function() {
const folder1 = await Folder.save({ title: 'folder1' });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
note1 = await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`);
note1 = await shim.attachFileToNote(note1, `${supportDir}/photo.jpg`);
const resource1 = (await Resource.all())[0];
const resourcePath = Resource.fullPath(resource1);
@@ -55,7 +55,7 @@ describe('services_ResourceService', function() {
const folder1 = await Folder.save({ title: 'folder1' });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
const note2 = await Note.save({ title: 'ma deuxième note', parent_id: folder1.id });
note1 = await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`);
note1 = await shim.attachFileToNote(note1, `${supportDir}/photo.jpg`);
const resource1 = (await Resource.all())[0];
await service.indexNoteResources();
@@ -75,7 +75,7 @@ describe('services_ResourceService', function() {
it('should not delete a resource that has never been associated with any note, because it probably means the resource came via sync, and associated note has not arrived yet', (async () => {
const service = new ResourceService();
await shim.createResourceFromPath(`${__dirname}/../tests/support/photo.jpg`);
await shim.createResourceFromPath(`${supportDir}/photo.jpg`);
await service.indexNoteResources();
await service.deleteOrphanResources(0);
@@ -88,7 +88,7 @@ describe('services_ResourceService', function() {
const folder1 = await Folder.save({ title: 'folder1' });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
note1 = await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`);
note1 = await shim.attachFileToNote(note1, `${supportDir}/photo.jpg`);
const resource1 = (await Resource.all())[0];
await service.indexNoteResources();
@@ -107,7 +107,7 @@ describe('services_ResourceService', function() {
const folder1 = await Folder.save({ title: 'folder1' });
const note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`);
await shim.attachFileToNote(note1, `${supportDir}/photo.jpg`);
await service.indexNoteResources();
@@ -143,7 +143,7 @@ describe('services_ResourceService', function() {
await encryptionService().loadMasterKeysFromSettings();
const folder1 = await Folder.save({ title: 'folder1' });
const note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`); // R1
await shim.attachFileToNote(note1, `${supportDir}/photo.jpg`); // R1
await resourceService().indexNoteResources();
await synchronizer().start();
expect(await allSyncTargetItemsEncrypted()).toBe(true);
@@ -156,7 +156,7 @@ describe('services_ResourceService', function() {
await decryptionWorker().start();
{
const n1 = await Note.load(note1.id);
await shim.attachFileToNote(n1, `${__dirname}/../tests/support/photo.jpg`); // R2
await shim.attachFileToNote(n1, `${supportDir}/photo.jpg`); // R2
}
await synchronizer().start();
@@ -173,7 +173,7 @@ describe('services_ResourceService', function() {
const folder1 = await Folder.save({ title: 'folder1' });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
note1 = await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`);
note1 = await shim.attachFileToNote(note1, `${supportDir}/photo.jpg`);
await resourceService().indexNoteResources();
const bodyWithResource = note1.body;
await Note.save({ id: note1.id, body: '' });
@@ -198,7 +198,7 @@ describe('services_ResourceService', function() {
// const service = new ResourceService();
// let note = await Note.save({});
// note = await shim.attachFileToNote(note, `${__dirname}/../tests/support/photo.jpg`);
// note = await shim.attachFileToNote(note, `${supportDir}/photo.jpg`);
// const resource = (await Resource.all())[0];
// const noteIds = await NoteResource.associatedNoteIds(resource.id);

View File

@@ -1,18 +1,13 @@
/* eslint-disable no-unused-vars */
/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars, prefer-const */
const time = require('@joplin/lib/time').default;
const { fileContentEqual, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder').default;
const Setting = require('@joplin/lib/models/Setting').default;
const Note = require('@joplin/lib/models/Note').default;
const NoteTag = require('@joplin/lib/models/NoteTag').default;
const ItemChange = require('@joplin/lib/models/ItemChange').default;
const Tag = require('@joplin/lib/models/Tag').default;
const Revision = require('@joplin/lib/models/Revision').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
const RevisionService = require('@joplin/lib/services/RevisionService').default;
const shim = require('@joplin/lib/shim').default;
const time = require('../time').default;
const { revisionService, setupDatabaseAndSynchronizer, switchClient } = require('../testing/test-utils.js');
const Setting = require('../models/Setting').default;
const Note = require('../models/Note').default;
const ItemChange = require('../models/ItemChange').default;
const Revision = require('../models/Revision').default;
const BaseModel = require('../BaseModel').default;
const RevisionService = require('../services/RevisionService').default;
describe('services_Revision', function() {

View File

@@ -1,14 +1,14 @@
import InteropService from '@joplin/lib/services/interop/InteropService';
import { CustomExportContext, CustomImportContext, Module, ModuleType } from '@joplin/lib/services/interop/types';
import shim from '@joplin/lib/shim';
import InteropService from '../../services/interop/InteropService';
import { CustomExportContext, CustomImportContext, Module, ModuleType } from '../../services/interop/types';
import shim from '../../shim';
const { fileContentEqual, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync, exportDir } = require('./test-utils.js');
import Folder from '@joplin/lib/models/Folder';
import Note from '@joplin/lib/models/Note';
import Tag from '@joplin/lib/models/Tag';
import Resource from '@joplin/lib/models/Resource';
const { fileContentEqual, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync, exportDir, supportDir } = require('../../testing/test-utils.js');
import Folder from '../../models/Folder';
import Note from '../../models/Note';
import Tag from '../../models/Tag';
import Resource from '../../models/Resource';
const fs = require('fs-extra');
const ArrayUtils = require('@joplin/lib/ArrayUtils');
const ArrayUtils = require('../../ArrayUtils');
async function recreateExportDir() {
const dir = exportDir();
@@ -209,7 +209,7 @@ describe('services_InteropService', function() {
const filePath = `${exportDir()}/test.jex`;
const folder1 = await Folder.save({ title: 'folder1' });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`);
await shim.attachFileToNote(note1, `${supportDir}/photo.jpg`);
note1 = await Note.load(note1.id);
let resourceIds = await Note.linkedResourceIds(note1.body);
const resource1 = await Resource.load(resourceIds[0]);
@@ -499,7 +499,7 @@ describe('services_InteropService', function() {
const folder1 = await Folder.save({ title: 'folder1' });
const note1 = await Note.save({ title: 'note1', parent_id: folder1.id });
await Note.save({ title: 'note2', parent_id: folder1.id });
await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`);
await shim.attachFileToNote(note1, `${supportDir}/photo.jpg`);
const filePath = `${exportDir()}/example.test`;

View File

@@ -1,11 +1,11 @@
import InteropService from '@joplin/lib/services/interop/InteropService';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import { setupDatabaseAndSynchronizer, switchClient, exportDir } from './test-utils';
import Folder from '@joplin/lib/models/Folder';
import Note from '@joplin/lib/models/Note';
import InteropService from '../../services/interop/InteropService';
import { PluginStates } from '../../services/plugins/reducer';
import { setupDatabaseAndSynchronizer, switchClient, exportDir } from '../../testing/test-utils';
import Folder from '../../models/Folder';
import Note from '../../models/Note';
import * as fs from 'fs-extra';
import { tempFilePath } from './test-utils';
import { ContentScriptType } from '@joplin/lib/services/plugins/api/types';
import { tempFilePath } from '../../testing/test-utils';
import { ContentScriptType } from '../../services/plugins/api/types';
async function recreateExportDir() {
const dir = exportDir();

View File

@@ -39,8 +39,9 @@ export default class InteropService_Exporter_Html extends InteropService_Exporte
this.resourceDir_ = this.destDir_ ? `${this.destDir_}/_resources` : null;
await shim.fsDriver().mkdir(this.destDir_);
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml({
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml(null, {
extraRendererRules: contentScriptsToRendererRules(options.plugins),
customCss: this.customCss_ || '',
});
this.style_ = themeStyle(Setting.THEME_LIGHT);
}
@@ -105,7 +106,6 @@ export default class InteropService_Exporter_Html extends InteropService_Exporte
const result = await this.markupToHtml_.render(item.markup_language, bodyMd, this.style_, {
resources: this.resources_,
plainResourceRendering: true,
userCss: this.customCss_,
});
const noteContent = [];
if (item.title) noteContent.push(`<div class="exported-note-title">${escapeHtml(item.title)}</div>`);

View File

@@ -2,13 +2,13 @@
const fs = require('fs-extra');
const { setupDatabaseAndSynchronizer, switchClient, exportDir } = require('./test-utils.js');
const InteropService_Exporter_Md = require('@joplin/lib/services/interop/InteropService_Exporter_Md').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
const Folder = require('@joplin/lib/models/Folder').default;
const Resource = require('@joplin/lib/models/Resource').default;
const Note = require('@joplin/lib/models/Note').default;
const shim = require('@joplin/lib/shim').default;
const { setupDatabaseAndSynchronizer, switchClient, exportDir, supportDir } = require('../../testing/test-utils.js');
const InteropService_Exporter_Md = require('../../services/interop/InteropService_Exporter_Md').default;
const BaseModel = require('../../BaseModel').default;
const Folder = require('../../models/Folder').default;
const Resource = require('../../models/Resource').default;
const Note = require('../../models/Note').default;
const shim = require('../../shim').default;
describe('services_InteropService_Exporter_Md', function() {
@@ -43,7 +43,7 @@ describe('services_InteropService_Exporter_Md', function() {
const folder1 = await Folder.save({ title: 'folder1' });
let note1 = await Note.save({ title: 'note1', parent_id: folder1.id });
const note2 = await Note.save({ title: 'note2', parent_id: folder1.id });
await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`);
await shim.attachFileToNote(note1, `${supportDir}/photo.jpg`);
note1 = await Note.load(note1.id);
queueExportItem(BaseModel.TYPE_FOLDER, folder1.id);
queueExportItem(BaseModel.TYPE_NOTE, note1);
@@ -52,7 +52,7 @@ describe('services_InteropService_Exporter_Md', function() {
const folder2 = await Folder.save({ title: 'folder2' });
let note3 = await Note.save({ title: 'note3', parent_id: folder2.id });
await shim.attachFileToNote(note3, `${__dirname}/../tests/support/photo.jpg`);
await shim.attachFileToNote(note3, `${supportDir}/photo.jpg`);
note3 = await Note.load(note3.id);
queueExportItem(BaseModel.TYPE_FOLDER, folder2.id);
queueExportItem(BaseModel.TYPE_NOTE, note3);
@@ -138,7 +138,7 @@ describe('services_InteropService_Exporter_Md', function() {
const folder1 = await Folder.save({ title: 'folder1' });
let note1 = await Note.save({ title: 'note1', parent_id: folder1.id });
await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`);
await shim.attachFileToNote(note1, `${supportDir}/photo.jpg`);
note1 = await Note.load(note1.id);
queueExportItem(BaseModel.TYPE_FOLDER, folder1.id);
queueExportItem(BaseModel.TYPE_NOTE, note1);
@@ -147,7 +147,7 @@ describe('services_InteropService_Exporter_Md', function() {
const folder2 = await Folder.save({ title: 'folder2', parent_id: folder1.id });
let note2 = await Note.save({ title: 'note2', parent_id: folder2.id });
await shim.attachFileToNote(note2, `${__dirname}/../tests/support/photo.jpg`);
await shim.attachFileToNote(note2, `${supportDir}/photo.jpg`);
note2 = await Note.load(note2.id);
queueExportItem(BaseModel.TYPE_FOLDER, folder2.id);
queueExportItem(BaseModel.TYPE_NOTE, note2);
@@ -243,14 +243,14 @@ describe('services_InteropService_Exporter_Md', function() {
const folder1 = await Folder.save({ title: 'folder1' });
let note1 = await Note.save({ title: 'note1', parent_id: folder1.id });
await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`);
await shim.attachFileToNote(note1, `${supportDir}/photo.jpg`);
note1 = await Note.load(note1.id);
queueExportItem(BaseModel.TYPE_NOTE, note1);
const resource1 = await Resource.load((await Note.linkedResourceIds(note1.body))[0]);
const folder2 = await Folder.save({ title: 'folder2', parent_id: folder1.id });
let note2 = await Note.save({ title: 'note2', parent_id: folder2.id });
await shim.attachFileToNote(note2, `${__dirname}/../tests/support/photo.jpg`);
await shim.attachFileToNote(note2, `${supportDir}/photo.jpg`);
note2 = await Note.load(note2.id);
queueExportItem(BaseModel.TYPE_NOTE, note2);
const resource2 = await Resource.load((await Note.linkedResourceIds(note2.body))[0]);

Some files were not shown because too many files have changed in this diff Show More