basalt-core 0.0.0-rc5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/cli/basalt.d.ts +3 -0
- package/dist/cli/basalt.d.ts.map +1 -0
- package/dist/cli/basalt.js +47 -0
- package/dist/cli/basalt.js.map +1 -0
- package/dist/cli/bundle-cli.d.ts +12 -0
- package/dist/cli/bundle-cli.d.ts.map +1 -0
- package/dist/cli/bundle-cli.js +124 -0
- package/dist/cli/bundle-cli.js.map +1 -0
- package/dist/cli/bundleWithAssets.d.ts +79 -0
- package/dist/cli/bundleWithAssets.d.ts.map +1 -0
- package/dist/cli/bundleWithAssets.js +198 -0
- package/dist/cli/bundleWithAssets.js.map +1 -0
- package/dist/cli/desktop.d.ts +339 -0
- package/dist/cli/desktop.d.ts.map +1 -0
- package/dist/cli/desktop.js +1029 -0
- package/dist/cli/desktop.js.map +1 -0
- package/dist/cli/doctor.d.ts +31 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +283 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/init.d.ts +77 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +442 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/metro.d.ts +49 -0
- package/dist/cli/metro.d.ts.map +1 -0
- package/dist/cli/metro.js +148 -0
- package/dist/cli/metro.js.map +1 -0
- package/dist/cli/packageApp.d.ts +102 -0
- package/dist/cli/packageApp.d.ts.map +1 -0
- package/dist/cli/packageApp.js +319 -0
- package/dist/cli/packageApp.js.map +1 -0
- package/dist/cli/steps.d.ts +34 -0
- package/dist/cli/steps.d.ts.map +1 -0
- package/dist/cli/steps.js +53 -0
- package/dist/cli/steps.js.map +1 -0
- package/dist/metro-config.d.ts +168 -0
- package/dist/metro-config.d.ts.map +1 -0
- package/dist/metro-config.js +671 -0
- package/dist/metro-config.js.map +1 -0
- package/dist/react-native.config.d.ts +7 -0
- package/dist/react-native.config.d.ts.map +1 -0
- package/dist/react-native.config.js +80 -0
- package/dist/react-native.config.js.map +1 -0
- package/dist/src/DragSource.d.ts +52 -0
- package/dist/src/DragSource.d.ts.map +1 -0
- package/dist/src/DragSource.js +97 -0
- package/dist/src/DragSource.js.map +1 -0
- package/dist/src/DropTarget.d.ts +58 -0
- package/dist/src/DropTarget.d.ts.map +1 -0
- package/dist/src/DropTarget.js +131 -0
- package/dist/src/DropTarget.js.map +1 -0
- package/dist/src/KeyHandler.d.ts +57 -0
- package/dist/src/KeyHandler.d.ts.map +1 -0
- package/dist/src/KeyHandler.js +48 -0
- package/dist/src/KeyHandler.js.map +1 -0
- package/dist/src/Menu.d.ts +72 -0
- package/dist/src/Menu.d.ts.map +1 -0
- package/dist/src/Menu.js +179 -0
- package/dist/src/Menu.js.map +1 -0
- package/dist/src/TitleBar.d.ts +75 -0
- package/dist/src/TitleBar.d.ts.map +1 -0
- package/dist/src/TitleBar.js +236 -0
- package/dist/src/TitleBar.js.map +1 -0
- package/dist/src/Window.d.ts +87 -0
- package/dist/src/Window.d.ts.map +1 -0
- package/dist/src/Window.js +248 -0
- package/dist/src/Window.js.map +1 -0
- package/dist/src/closeRequest.d.ts +55 -0
- package/dist/src/closeRequest.d.ts.map +1 -0
- package/dist/src/closeRequest.js +134 -0
- package/dist/src/closeRequest.js.map +1 -0
- package/dist/src/index.d.ts +45 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +92 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/overrides/Alert.d.ts +69 -0
- package/dist/src/overrides/Alert.d.ts.map +1 -0
- package/dist/src/overrides/Alert.js +112 -0
- package/dist/src/overrides/Alert.js.map +1 -0
- package/dist/src/overrides/BaseViewConfig.d.ts +28 -0
- package/dist/src/overrides/BaseViewConfig.d.ts.map +1 -0
- package/dist/src/overrides/BaseViewConfig.js +72 -0
- package/dist/src/overrides/BaseViewConfig.js.map +1 -0
- package/dist/src/overrides/Platform.linux.d.ts +10 -0
- package/dist/src/overrides/Platform.linux.d.ts.map +1 -0
- package/dist/src/overrides/Platform.linux.js +15 -0
- package/dist/src/overrides/Platform.linux.js.map +1 -0
- package/dist/src/overrides/Platform.macos.d.ts +10 -0
- package/dist/src/overrides/Platform.macos.d.ts.map +1 -0
- package/dist/src/overrides/Platform.macos.js +15 -0
- package/dist/src/overrides/Platform.macos.js.map +1 -0
- package/dist/src/overrides/Platform.windows.d.ts +10 -0
- package/dist/src/overrides/Platform.windows.d.ts.map +1 -0
- package/dist/src/overrides/Platform.windows.js +15 -0
- package/dist/src/overrides/Platform.windows.js.map +1 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts +24 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts.map +1 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.js +30 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.js.map +1 -0
- package/dist/src/overrides/RefreshControl.d.ts +52 -0
- package/dist/src/overrides/RefreshControl.d.ts.map +1 -0
- package/dist/src/overrides/RefreshControl.js +92 -0
- package/dist/src/overrides/RefreshControl.js.map +1 -0
- package/dist/src/overrides/ScrollView.d.ts +39 -0
- package/dist/src/overrides/ScrollView.d.ts.map +1 -0
- package/dist/src/overrides/ScrollView.js +59 -0
- package/dist/src/overrides/ScrollView.js.map +1 -0
- package/dist/src/overrides/Share.d.ts +96 -0
- package/dist/src/overrides/Share.d.ts.map +1 -0
- package/dist/src/overrides/Share.js +97 -0
- package/dist/src/overrides/Share.js.map +1 -0
- package/dist/src/overrides/TextInput.d.ts +81 -0
- package/dist/src/overrides/TextInput.d.ts.map +1 -0
- package/dist/src/overrides/TextInput.js +213 -0
- package/dist/src/overrides/TextInput.js.map +1 -0
- package/dist/src/overrides/View.d.ts +36 -0
- package/dist/src/overrides/View.d.ts.map +1 -0
- package/dist/src/overrides/View.js +99 -0
- package/dist/src/overrides/View.js.map +1 -0
- package/dist/src/overrides/createPlatform.d.ts +32 -0
- package/dist/src/overrides/createPlatform.d.ts.map +1 -0
- package/dist/src/overrides/createPlatform.js +84 -0
- package/dist/src/overrides/createPlatform.js.map +1 -0
- package/dist/src/overrides/setUpDeveloperTools.d.ts +28 -0
- package/dist/src/overrides/setUpDeveloperTools.d.ts.map +1 -0
- package/dist/src/overrides/setUpDeveloperTools.js +84 -0
- package/dist/src/overrides/setUpDeveloperTools.js.map +1 -0
- package/dist/src/overrides/setUpXHR.d.ts +46 -0
- package/dist/src/overrides/setUpXHR.d.ts.map +1 -0
- package/dist/src/overrides/setUpXHR.js +236 -0
- package/dist/src/overrides/setUpXHR.js.map +1 -0
- package/dist/src/quitRequest.d.ts +55 -0
- package/dist/src/quitRequest.d.ts.map +1 -0
- package/dist/src/quitRequest.js +121 -0
- package/dist/src/quitRequest.js.map +1 -0
- package/dist/src/resourcePath.d.ts +18 -0
- package/dist/src/resourcePath.d.ts.map +1 -0
- package/dist/src/resourcePath.js +31 -0
- package/dist/src/resourcePath.js.map +1 -0
- package/dist/src/responseBody.d.ts +76 -0
- package/dist/src/responseBody.d.ts.map +1 -0
- package/dist/src/responseBody.js +94 -0
- package/dist/src/responseBody.js.map +1 -0
- package/dist/src/responseHeaders.d.ts +40 -0
- package/dist/src/responseHeaders.d.ts.map +1 -0
- package/dist/src/responseHeaders.js +75 -0
- package/dist/src/responseHeaders.js.map +1 -0
- package/dist/src/titleBarState.d.ts +43 -0
- package/dist/src/titleBarState.d.ts.map +1 -0
- package/dist/src/titleBarState.js +77 -0
- package/dist/src/titleBarState.js.map +1 -0
- package/dist/src/useContextMenu.d.ts +113 -0
- package/dist/src/useContextMenu.d.ts.map +1 -0
- package/dist/src/useContextMenu.js +109 -0
- package/dist/src/useContextMenu.js.map +1 -0
- package/dist/src/useDialog.d.ts +98 -0
- package/dist/src/useDialog.d.ts.map +1 -0
- package/dist/src/useDialog.js +112 -0
- package/dist/src/useDialog.js.map +1 -0
- package/dist/src/useDisplays.d.ts +70 -0
- package/dist/src/useDisplays.d.ts.map +1 -0
- package/dist/src/useDisplays.js +118 -0
- package/dist/src/useDisplays.js.map +1 -0
- package/dist/src/useHandledKeys.d.ts +38 -0
- package/dist/src/useHandledKeys.d.ts.map +1 -0
- package/dist/src/useHandledKeys.js +70 -0
- package/dist/src/useHandledKeys.js.map +1 -0
- package/dist/src/useWindow.d.ts +140 -0
- package/dist/src/useWindow.d.ts.map +1 -0
- package/dist/src/useWindow.js +233 -0
- package/dist/src/useWindow.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/native/CMakeLists.txt +355 -0
- package/native/bootstrap.sh +613 -0
- package/native/cmake/CompilerFlags.cmake +49 -0
- package/native/cmake/Expo.cmake +125 -0
- package/native/cmake/ReactNativeCore.cmake +612 -0
- package/native/cmake/Reanimated.cmake +108 -0
- package/native/cmake/Skia.cmake +379 -0
- package/native/cmake/ThirdParty.cmake +290 -0
- package/native/cmake/Worklets.cmake +67 -0
- package/native/cmake/codegen/CMakeLists.txt +32 -0
- package/native/core/AppIdentity.cpp +85 -0
- package/native/core/AppIdentity.h +59 -0
- package/native/core/AppPaths.cpp +96 -0
- package/native/core/AppPaths.h +53 -0
- package/native/core/AppearanceModule.cpp +60 -0
- package/native/core/AppearanceModule.h +40 -0
- package/native/core/Backface.h +59 -0
- package/native/core/BlobModule.cpp +283 -0
- package/native/core/BlobModule.h +76 -0
- package/native/core/BlobRegistry.cpp +75 -0
- package/native/core/BlobRegistry.h +44 -0
- package/native/core/ColorScheme.cpp +105 -0
- package/native/core/ColorScheme.h +59 -0
- package/native/core/ComponentRegistry.h +30 -0
- package/native/core/ControlMetrics.h +37 -0
- package/native/core/CoreModules.cpp +592 -0
- package/native/core/CoreModules.h +308 -0
- package/native/core/DebuggingOverlay.cpp +106 -0
- package/native/core/DebuggingOverlay.h +65 -0
- package/native/core/DesktopControls.cpp +232 -0
- package/native/core/DesktopControls.h +170 -0
- package/native/core/DevBundle.cpp +108 -0
- package/native/core/DevBundle.h +69 -0
- package/native/core/DevMenu.cpp +99 -0
- package/native/core/DevMenu.h +75 -0
- package/native/core/DialogModule.cpp +150 -0
- package/native/core/DialogModule.h +51 -0
- package/native/core/DragAndDrop.cpp +45 -0
- package/native/core/DragAndDrop.h +225 -0
- package/native/core/ExpoImageComponent.cpp +79 -0
- package/native/core/ExpoImageComponent.h +80 -0
- package/native/core/ExpoModules.cpp +415 -0
- package/native/core/ExpoModules.h +115 -0
- package/native/core/ExpoRuntime.cpp +366 -0
- package/native/core/ExpoRuntime.h +42 -0
- package/native/core/FocusRing.h +36 -0
- package/native/core/FontRegistry.h +51 -0
- package/native/core/GestureHandlerModule.cpp +136 -0
- package/native/core/GestureHandlerModule.h +71 -0
- package/native/core/Gestures.cpp +533 -0
- package/native/core/Gestures.h +145 -0
- package/native/core/HoverTracker.cpp +69 -0
- package/native/core/HoverTracker.h +109 -0
- package/native/core/HttpClient.cpp +282 -0
- package/native/core/ImageBytes.cpp +205 -0
- package/native/core/ImageBytes.h +29 -0
- package/native/core/ImageCache.cpp +51 -0
- package/native/core/ImageCache.h +89 -0
- package/native/core/JsRuntimeAccess.cpp +70 -0
- package/native/core/JsRuntimeAccess.h +60 -0
- package/native/core/JsiPromise.h +51 -0
- package/native/core/KeyEvents.cpp +116 -0
- package/native/core/KeyEvents.h +144 -0
- package/native/core/LogBoxSurface.cpp +46 -0
- package/native/core/LogBoxSurface.h +68 -0
- package/native/core/MenuModel.h +117 -0
- package/native/core/MenuModule.cpp +209 -0
- package/native/core/MenuModule.h +66 -0
- package/native/core/MountingWalk.h +524 -0
- package/native/core/PackageModules.cpp.in +25 -0
- package/native/core/PackageModules.h +50 -0
- package/native/core/PlatformConstantsModule.cpp +63 -0
- package/native/core/PlatformConstantsModule.h +64 -0
- package/native/core/PlatformServices.h +213 -0
- package/native/core/PointerButtons.h +63 -0
- package/native/core/PullToRefresh.cpp +57 -0
- package/native/core/PullToRefresh.h +73 -0
- package/native/core/ReanimatedCompat.h +47 -0
- package/native/core/ReanimatedModule.cpp +203 -0
- package/native/core/ReanimatedModule.h +55 -0
- package/native/core/ScrollAnimation.cpp +59 -0
- package/native/core/ScrollAnimation.h +70 -0
- package/native/core/ScrollBounds.cpp +26 -0
- package/native/core/ScrollBounds.h +68 -0
- package/native/core/ScrollIndicator.cpp +50 -0
- package/native/core/ScrollIndicator.h +69 -0
- package/native/core/ScrollMomentum.cpp +71 -0
- package/native/core/ScrollMomentum.h +73 -0
- package/native/core/ScrollSnap.cpp +123 -0
- package/native/core/ScrollSnap.h +80 -0
- package/native/core/ShareFallback.cpp +101 -0
- package/native/core/ShareFallback.h +45 -0
- package/native/core/SkiaPictureViewComponent.cpp +11 -0
- package/native/core/SkiaPictureViewComponent.h +47 -0
- package/native/core/SourceCodeModule.cpp +55 -0
- package/native/core/SourceCodeModule.h +53 -0
- package/native/core/StatusBarModule.cpp +23 -0
- package/native/core/StatusBarModule.h +42 -0
- package/native/core/TestDialog.cpp +148 -0
- package/native/core/TestDialog.h +96 -0
- package/native/core/TestKeys.cpp +78 -0
- package/native/core/TestKeys.h +50 -0
- package/native/core/TestQuitFile.cpp +33 -0
- package/native/core/TestQuitFile.h +60 -0
- package/native/core/TestSettle.h +192 -0
- package/native/core/TitleBarRegions.h +31 -0
- package/native/core/TurboModuleProxy.cpp +51 -0
- package/native/core/TurboModuleProxy.h +46 -0
- package/native/core/UIManagerAccess.cpp +43 -0
- package/native/core/UIManagerAccess.h +43 -0
- package/native/core/WindowBoundsCache.cpp +319 -0
- package/native/core/WindowControl.h +235 -0
- package/native/core/WindowHost.h +158 -0
- package/native/core/WindowMethods.cpp +167 -0
- package/native/core/WindowMethods.h +84 -0
- package/native/core/WindowsModule.cpp +448 -0
- package/native/core/WindowsModule.h +133 -0
- package/native/core/WorkletsModule.cpp +199 -0
- package/native/core/WorkletsModule.h +57 -0
- package/native/core/portability_probe.cpp +210 -0
- package/native/tests/EventRecorder.h +124 -0
- package/native/tests/TestHarness.cpp +109 -0
- package/native/tests/TestHarness.h +89 -0
- package/native/tests/TreeDump.h +49 -0
- package/native/tests/test_app_identity.cpp +114 -0
- package/native/tests/test_app_paths.cpp +48 -0
- package/native/tests/test_appearance.cpp +107 -0
- package/native/tests/test_backface.cpp +79 -0
- package/native/tests/test_blobs.cpp +157 -0
- package/native/tests/test_controls.cpp +185 -0
- package/native/tests/test_devbundle.cpp +103 -0
- package/native/tests/test_drag_and_drop.cpp +143 -0
- package/native/tests/test_gestures.cpp +331 -0
- package/native/tests/test_hover.cpp +92 -0
- package/native/tests/test_image_cache.cpp +109 -0
- package/native/tests/test_js_runtime_access.cpp +137 -0
- package/native/tests/test_key_events.cpp +235 -0
- package/native/tests/test_momentum.cpp +138 -0
- package/native/tests/test_quit_file.cpp +104 -0
- package/native/tests/test_scroll_animation.cpp +120 -0
- package/native/tests/test_scroll_bounds.cpp +66 -0
- package/native/tests/test_scroll_indicator.cpp +149 -0
- package/native/tests/test_scroll_snap.cpp +112 -0
- package/native/tests/test_settle.cpp +115 -0
- package/native/tests/test_share.cpp +75 -0
- package/native/tests/test_sourcecode.cpp +62 -0
- package/native/tests/test_window_close.cpp +119 -0
- package/native/tests/test_window_limits.cpp +123 -0
- package/package.json +43 -0
- package/react-native.config.js +86 -0
- package/supported-versions.json +66 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// Tests for the blob byte store.
|
|
2
|
+
//
|
|
3
|
+
// Core's, so compiled into both platforms' suites -- a blob is a byte array
|
|
4
|
+
// with a name everywhere, and there is nothing here a toolkit could change.
|
|
5
|
+
//
|
|
6
|
+
// What is *not* tested here is the module: createFromParts and readAsText take
|
|
7
|
+
// jsi values, which need a runtime, and the thing worth proving about them --
|
|
8
|
+
// that a Blob round-trips through JavaScript -- is proven by js/blob.js running
|
|
9
|
+
// on both hosts. This covers the half underneath, where an off-by-one lives.
|
|
10
|
+
|
|
11
|
+
#include "TestHarness.h"
|
|
12
|
+
|
|
13
|
+
#include "BlobModule.h"
|
|
14
|
+
#include "BlobRegistry.h"
|
|
15
|
+
|
|
16
|
+
#include <sstream>
|
|
17
|
+
#include <string>
|
|
18
|
+
|
|
19
|
+
namespace {
|
|
20
|
+
|
|
21
|
+
// Unique per test, so one test's leftovers cannot be another's fixture -- the
|
|
22
|
+
// registry is process-wide and nothing evicts from it.
|
|
23
|
+
std::string freshId(const char *name) {
|
|
24
|
+
static int counter = 0;
|
|
25
|
+
return std::string("test-") + name + "-" + std::to_string(++counter);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
} // namespace
|
|
29
|
+
|
|
30
|
+
TEST(a_stored_blob_reads_back) {
|
|
31
|
+
const auto id = freshId("roundtrip");
|
|
32
|
+
basalt::storeBlob(id, "hello");
|
|
33
|
+
|
|
34
|
+
EXPECT_EQ(static_cast<int>(basalt::blobSize(id)), 5);
|
|
35
|
+
const auto bytes = basalt::blobBytes(id);
|
|
36
|
+
EXPECT(bytes.has_value());
|
|
37
|
+
EXPECT_EQ(bytes.value_or(""), std::string("hello"));
|
|
38
|
+
|
|
39
|
+
basalt::releaseBlob(id);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
TEST(an_unknown_blob_is_absent_rather_than_empty) {
|
|
43
|
+
// The distinction matters: "" is a legal blob, and an app that asked for a
|
|
44
|
+
// released one should be told so rather than handed nothing.
|
|
45
|
+
EXPECT(!basalt::blobBytes("never-stored").has_value());
|
|
46
|
+
EXPECT(!basalt::blobSlice("never-stored", 0, 10).has_value());
|
|
47
|
+
EXPECT_EQ(static_cast<int>(basalt::blobSize("never-stored")), 0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
TEST(slicing_takes_the_part_asked_for) {
|
|
51
|
+
const auto id = freshId("slice");
|
|
52
|
+
basalt::storeBlob(id, "0123456789");
|
|
53
|
+
|
|
54
|
+
EXPECT_EQ(basalt::blobSlice(id, 0, 3).value_or(""), std::string("012"));
|
|
55
|
+
EXPECT_EQ(basalt::blobSlice(id, 2, 3).value_or(""), std::string("234"));
|
|
56
|
+
EXPECT_EQ(basalt::blobSlice(id, 7, 3).value_or(""), std::string("789"));
|
|
57
|
+
|
|
58
|
+
basalt::releaseBlob(id);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// JavaScript computes offsets from sizes it was told, so a disagreement should
|
|
62
|
+
// be a short read rather than an exception three layers from the cause.
|
|
63
|
+
TEST(a_slice_past_the_end_is_clamped_not_rejected) {
|
|
64
|
+
const auto id = freshId("clamp");
|
|
65
|
+
basalt::storeBlob(id, "abcde");
|
|
66
|
+
|
|
67
|
+
// Runs off the end.
|
|
68
|
+
EXPECT_EQ(basalt::blobSlice(id, 3, 100).value_or("!"), std::string("de"));
|
|
69
|
+
// Starts past the end: empty, and still *present*, because the blob exists.
|
|
70
|
+
const auto beyond = basalt::blobSlice(id, 50, 10);
|
|
71
|
+
EXPECT(beyond.has_value());
|
|
72
|
+
EXPECT_EQ(beyond.value_or("!"), std::string(""));
|
|
73
|
+
|
|
74
|
+
basalt::releaseBlob(id);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
TEST(releasing_removes_it) {
|
|
78
|
+
const auto id = freshId("release");
|
|
79
|
+
basalt::storeBlob(id, "temporary");
|
|
80
|
+
EXPECT(basalt::blobBytes(id).has_value());
|
|
81
|
+
|
|
82
|
+
basalt::releaseBlob(id);
|
|
83
|
+
EXPECT(!basalt::blobBytes(id).has_value());
|
|
84
|
+
|
|
85
|
+
// Twice is not an error: JavaScript's Blob.close() can be called again, and
|
|
86
|
+
// a garbage collector may follow it.
|
|
87
|
+
basalt::releaseBlob(id);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
TEST(storing_twice_replaces) {
|
|
91
|
+
const auto id = freshId("replace");
|
|
92
|
+
basalt::storeBlob(id, "first");
|
|
93
|
+
basalt::storeBlob(id, "second");
|
|
94
|
+
|
|
95
|
+
EXPECT_EQ(basalt::blobBytes(id).value_or(""), std::string("second"));
|
|
96
|
+
basalt::releaseBlob(id);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Blobs are bytes, not text. A store that assumed UTF-8 or stopped at a NUL
|
|
100
|
+
// would corrupt every image an app ever fetched.
|
|
101
|
+
TEST(binary_content_survives) {
|
|
102
|
+
const auto id = freshId("binary");
|
|
103
|
+
const std::string bytes("\x00\x01\xFF\x00 tail", 9);
|
|
104
|
+
basalt::storeBlob(id, bytes);
|
|
105
|
+
|
|
106
|
+
EXPECT_EQ(static_cast<int>(basalt::blobSize(id)), 9);
|
|
107
|
+
EXPECT(basalt::blobBytes(id).value_or("") == bytes);
|
|
108
|
+
|
|
109
|
+
basalt::releaseBlob(id);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// --- base64 ----------------------------------------------------------------
|
|
113
|
+
//
|
|
114
|
+
// On the path of every `fetch` since the setUpXHR override: a blob response is
|
|
115
|
+
// requested as base64 and decoded here, so a bug in this codec is a bug in
|
|
116
|
+
// every request an app makes, not only in `.blob()`.
|
|
117
|
+
|
|
118
|
+
TEST(base64_round_trips_every_tail_length) {
|
|
119
|
+
// Lengths 0..8 cover both padded cases and the aligned one, several times
|
|
120
|
+
// over. The tail is where base64 goes wrong and it goes wrong nowhere else.
|
|
121
|
+
const std::string source = "ABCDEFGH";
|
|
122
|
+
for (size_t length = 0; length <= source.size(); length++) {
|
|
123
|
+
const std::string original = source.substr(0, length);
|
|
124
|
+
const std::string encoded = basalt::detail::base64Encode(original);
|
|
125
|
+
EXPECT_EQ(basalt::detail::base64Decode(encoded), original);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
TEST(base64_encodes_the_padding_the_usual_way) {
|
|
130
|
+
EXPECT_EQ(basalt::detail::base64Encode("A"), std::string("QQ=="));
|
|
131
|
+
EXPECT_EQ(basalt::detail::base64Encode("AB"), std::string("QUI="));
|
|
132
|
+
EXPECT_EQ(basalt::detail::base64Encode("ABC"), std::string("QUJD"));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
TEST(base64_decodes_what_a_server_actually_sends) {
|
|
136
|
+
// The exact string a 404 from Metro produced while this was being written,
|
|
137
|
+
// which is how the round trip was first shown to be byte-exact.
|
|
138
|
+
EXPECT_EQ(basalt::detail::base64Decode("QXNzZXQgbm90IGZvdW5k"),
|
|
139
|
+
std::string("Asset not found"));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
TEST(base64_survives_bytes_that_are_not_text) {
|
|
143
|
+
// The reason a blob response cannot be delivered as a string: every byte
|
|
144
|
+
// value, including the high half and an embedded NUL.
|
|
145
|
+
std::string binary;
|
|
146
|
+
for (int i = 0; i < 256; i++) {
|
|
147
|
+
binary.push_back(static_cast<char>(i));
|
|
148
|
+
}
|
|
149
|
+
EXPECT_EQ(basalt::detail::base64Decode(basalt::detail::base64Encode(binary)), binary);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
TEST(base64_ignores_whitespace_and_a_truncated_group) {
|
|
153
|
+
// Newlines are legal in base64 in the wild, and a single leftover character
|
|
154
|
+
// encodes no whole byte, so it contributes nothing rather than a zero.
|
|
155
|
+
EXPECT_EQ(basalt::detail::base64Decode("QUJD\nQUJD"), std::string("ABCABC"));
|
|
156
|
+
EXPECT_EQ(basalt::detail::base64Decode("QUJDQ"), std::string("ABC"));
|
|
157
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// The four components that are a control rather than a box, minus the pixels.
|
|
2
|
+
//
|
|
3
|
+
// What is asserted here is the half that three hosts share: how a ShadowView's
|
|
4
|
+
// props become a ControlState, what the tree dump says about it, and the pull
|
|
5
|
+
// gesture that fires a <RefreshControl>. None of it needs a toolkit, which is
|
|
6
|
+
// the point -- a GtkSwitch, an NSSwitch and a Direct2D rounded rectangle have
|
|
7
|
+
// nothing in common, and everything above them has to be identical or the same
|
|
8
|
+
// app is a different app on each desktop.
|
|
9
|
+
//
|
|
10
|
+
// The pull is the part most worth a test. It has exactly one rule that is easy
|
|
11
|
+
// to get wrong and invisible when it is -- fire once per gesture, not once per
|
|
12
|
+
// wheel notch -- and a mouse cannot be asked about it.
|
|
13
|
+
|
|
14
|
+
#include "TestHarness.h"
|
|
15
|
+
|
|
16
|
+
#include <memory>
|
|
17
|
+
|
|
18
|
+
#include "DesktopControls.h"
|
|
19
|
+
#include "PullToRefresh.h"
|
|
20
|
+
|
|
21
|
+
#include <react/renderer/components/FBReactNativeSpec/Props.h>
|
|
22
|
+
|
|
23
|
+
using basalt::ControlKind;
|
|
24
|
+
using basalt::ControlState;
|
|
25
|
+
using basalt::controlKindFor;
|
|
26
|
+
using basalt::controlStateOf;
|
|
27
|
+
using basalt::describeControl;
|
|
28
|
+
using basalt::kPullToRefreshThreshold;
|
|
29
|
+
using basalt::PullToRefreshTracker;
|
|
30
|
+
using facebook::react::ShadowView;
|
|
31
|
+
|
|
32
|
+
namespace {
|
|
33
|
+
|
|
34
|
+
// A mounted view of a given component name, carrying props built by the caller.
|
|
35
|
+
// Fabric's ShadowView is a plain struct, so this is the whole fixture.
|
|
36
|
+
//
|
|
37
|
+
// The props arrive already in a shared_ptr rather than being copied into one.
|
|
38
|
+
// BaseViewProps has a deleted copy constructor at React Native 0.86 -- which
|
|
39
|
+
// is what a current Expo app installs -- so copying compiled here, against
|
|
40
|
+
// the checkout's newer React Native, and failed in the one place it matters.
|
|
41
|
+
template <typename Props>
|
|
42
|
+
ShadowView viewOf(const char *componentName, std::shared_ptr<Props> props) {
|
|
43
|
+
ShadowView view;
|
|
44
|
+
view.componentName = componentName;
|
|
45
|
+
view.tag = 10;
|
|
46
|
+
view.props = std::move(props);
|
|
47
|
+
return view;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
} // namespace
|
|
51
|
+
|
|
52
|
+
TEST(controls_name_decides_kind) {
|
|
53
|
+
EXPECT(controlKindFor("ActivityIndicatorView") == ControlKind::ActivityIndicator);
|
|
54
|
+
EXPECT(controlKindFor("Switch") == ControlKind::Switch);
|
|
55
|
+
EXPECT(controlKindFor("PullToRefreshView") == ControlKind::PullToRefresh);
|
|
56
|
+
// Everything else, including the modal -- which is a control in the backlog's
|
|
57
|
+
// sense and not in this one: it has no widget, it is an overlay.
|
|
58
|
+
EXPECT(controlKindFor("View") == ControlKind::None);
|
|
59
|
+
EXPECT(controlKindFor("ModalHostView") == ControlKind::None);
|
|
60
|
+
EXPECT(controlKindFor(nullptr) == ControlKind::None);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
TEST(controls_indicator_props_are_read) {
|
|
64
|
+
auto props = std::make_shared<facebook::react::ActivityIndicatorViewProps>();
|
|
65
|
+
props->animating = true;
|
|
66
|
+
props->size = facebook::react::ActivityIndicatorViewSize::Large;
|
|
67
|
+
|
|
68
|
+
const ControlState state = controlStateOf(viewOf("ActivityIndicatorView", props));
|
|
69
|
+
EXPECT(state.kind == ControlKind::ActivityIndicator);
|
|
70
|
+
EXPECT(state.on);
|
|
71
|
+
EXPECT(state.large);
|
|
72
|
+
// React Native's default, which is the one that makes a stopped indicator
|
|
73
|
+
// draw nothing at all rather than draw a still one.
|
|
74
|
+
EXPECT(state.hidesWhenStopped);
|
|
75
|
+
EXPECT(!state.hasForeground);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
TEST(controls_switch_props_are_read) {
|
|
79
|
+
auto props = std::make_shared<facebook::react::SwitchProps>();
|
|
80
|
+
props->value = true;
|
|
81
|
+
props->disabled = true;
|
|
82
|
+
|
|
83
|
+
const ControlState state = controlStateOf(viewOf("Switch", props));
|
|
84
|
+
EXPECT(state.kind == ControlKind::Switch);
|
|
85
|
+
EXPECT(state.on);
|
|
86
|
+
EXPECT(state.disabled);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
TEST(controls_refresh_props_are_read) {
|
|
90
|
+
auto props = std::make_shared<facebook::react::PullToRefreshViewProps>();
|
|
91
|
+
props->refreshing = true;
|
|
92
|
+
|
|
93
|
+
const ControlState state = controlStateOf(viewOf("PullToRefreshView", props));
|
|
94
|
+
EXPECT(state.kind == ControlKind::PullToRefresh);
|
|
95
|
+
EXPECT(state.on);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
TEST(controls_a_plain_view_is_not_a_control) {
|
|
99
|
+
auto props = std::make_shared<facebook::react::ViewProps>();
|
|
100
|
+
const ControlState state = controlStateOf(viewOf("View", props));
|
|
101
|
+
EXPECT(state.kind == ControlKind::None);
|
|
102
|
+
EXPECT(describeControl(state).empty());
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
TEST(controls_description_is_one_string_for_three_hosts) {
|
|
106
|
+
ControlState state;
|
|
107
|
+
state.kind = ControlKind::Switch;
|
|
108
|
+
state.on = true;
|
|
109
|
+
EXPECT_EQ(describeControl(state), std::string("switch:on"));
|
|
110
|
+
state.disabled = true;
|
|
111
|
+
EXPECT_EQ(describeControl(state), std::string("switch:on:disabled"));
|
|
112
|
+
|
|
113
|
+
state = ControlState{};
|
|
114
|
+
state.kind = ControlKind::ActivityIndicator;
|
|
115
|
+
state.on = true;
|
|
116
|
+
EXPECT_EQ(describeControl(state), std::string("spinner:animating"));
|
|
117
|
+
state.large = true;
|
|
118
|
+
EXPECT_EQ(describeControl(state), std::string("spinner-large:animating"));
|
|
119
|
+
state.on = false;
|
|
120
|
+
EXPECT_EQ(describeControl(state), std::string("spinner-large:stopped"));
|
|
121
|
+
|
|
122
|
+
state = ControlState{};
|
|
123
|
+
state.kind = ControlKind::PullToRefresh;
|
|
124
|
+
EXPECT_EQ(describeControl(state), std::string("refresh:idle"));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// --- The pull ----------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
TEST(pull_a_scroll_view_with_no_refresh_control_never_fires) {
|
|
130
|
+
PullToRefreshTracker tracker;
|
|
131
|
+
EXPECT(!tracker.pull(1, 1000.0));
|
|
132
|
+
EXPECT_EQ((long)tracker.controlFor(1), 0L);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
TEST(pull_fires_once_the_threshold_is_crossed) {
|
|
136
|
+
PullToRefreshTracker tracker;
|
|
137
|
+
tracker.attach(1, 2);
|
|
138
|
+
EXPECT_EQ((long)tracker.controlFor(1), 2L);
|
|
139
|
+
|
|
140
|
+
EXPECT(!tracker.pull(1, kPullToRefreshThreshold / 2.0));
|
|
141
|
+
EXPECT(tracker.pull(1, kPullToRefreshThreshold / 2.0 + 1.0));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
TEST(pull_fires_once_per_gesture_and_not_once_per_notch) {
|
|
145
|
+
PullToRefreshTracker tracker;
|
|
146
|
+
tracker.attach(1, 2);
|
|
147
|
+
|
|
148
|
+
EXPECT(tracker.pull(1, kPullToRefreshThreshold + 1.0));
|
|
149
|
+
// The wheel is still turning. React Native's contract is one onRefresh per
|
|
150
|
+
// pull, and without the latch this is sixty a second.
|
|
151
|
+
EXPECT(!tracker.pull(1, 100.0));
|
|
152
|
+
EXPECT(!tracker.pull(1, 100.0));
|
|
153
|
+
|
|
154
|
+
// Let go, and it can fire again.
|
|
155
|
+
tracker.release(1);
|
|
156
|
+
EXPECT(!tracker.pull(1, 10.0));
|
|
157
|
+
EXPECT(tracker.pull(1, kPullToRefreshThreshold));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
TEST(pull_a_scroll_away_from_the_top_rearms_it) {
|
|
161
|
+
PullToRefreshTracker tracker;
|
|
162
|
+
tracker.attach(1, 2);
|
|
163
|
+
EXPECT(!tracker.pull(1, kPullToRefreshThreshold - 1.0));
|
|
164
|
+
// The list moved, so this was not one continuous pull past the top.
|
|
165
|
+
tracker.release(1);
|
|
166
|
+
EXPECT(!tracker.pull(1, kPullToRefreshThreshold - 1.0));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
TEST(pull_forgetting_either_end_drops_the_pair) {
|
|
170
|
+
PullToRefreshTracker tracker;
|
|
171
|
+
|
|
172
|
+
// The control went away -- an app that stopped passing `refreshControl`.
|
|
173
|
+
tracker.attach(1, 2);
|
|
174
|
+
tracker.forget(2);
|
|
175
|
+
EXPECT_EQ((long)tracker.controlFor(1), 0L);
|
|
176
|
+
EXPECT(!tracker.pull(1, 1000.0));
|
|
177
|
+
|
|
178
|
+
// The scroll view went away, which is the ordinary unmount.
|
|
179
|
+
tracker.attach(3, 4);
|
|
180
|
+
tracker.forget(3);
|
|
181
|
+
EXPECT_EQ((long)tracker.controlFor(3), 0L);
|
|
182
|
+
// And the reverse map went with it, so re-attaching 4 elsewhere is clean.
|
|
183
|
+
tracker.attach(5, 4);
|
|
184
|
+
EXPECT_EQ((long)tracker.controlFor(5), 4L);
|
|
185
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Tests for telling the bundle request apart from every other request, and for
|
|
2
|
+
// what is kept when it fails.
|
|
3
|
+
//
|
|
4
|
+
// Core's, so compiled into both platforms' suites: which toolkit is drawing has
|
|
5
|
+
// nothing to do with what Metro answered.
|
|
6
|
+
//
|
|
7
|
+
// The predicate is the part worth proving. It decides whether a response body
|
|
8
|
+
// reaches the JS engine, so a false negative puts Metro's error page back in
|
|
9
|
+
// front of the compiler and a false positive would swallow the body of an app's
|
|
10
|
+
// own fetch -- a 404 from an API, which JavaScript is entitled to read.
|
|
11
|
+
|
|
12
|
+
#include "TestHarness.h"
|
|
13
|
+
|
|
14
|
+
#include "DevBundle.h"
|
|
15
|
+
#include "SourceCodeModule.h"
|
|
16
|
+
|
|
17
|
+
#include <sstream>
|
|
18
|
+
#include <string>
|
|
19
|
+
|
|
20
|
+
namespace {
|
|
21
|
+
|
|
22
|
+
const char *kOriginHost = "localhost";
|
|
23
|
+
constexpr uint32_t kOriginPort = 8081;
|
|
24
|
+
|
|
25
|
+
void withDevServer() {
|
|
26
|
+
basalt::setDevServerOrigin(kOriginHost, kOriginPort);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
} // namespace
|
|
30
|
+
|
|
31
|
+
TEST(the_bundle_request_is_recognised) {
|
|
32
|
+
withDevServer();
|
|
33
|
+
EXPECT(basalt::isDevBundleRequest(
|
|
34
|
+
"GET",
|
|
35
|
+
"http://localhost:8081/index.bundle?platform=android&dev=true&app=basalt-macos"));
|
|
36
|
+
// Metro is also happy to serve it with no query at all.
|
|
37
|
+
EXPECT(basalt::isDevBundleRequest("GET", "http://localhost:8081/index.bundle"));
|
|
38
|
+
// Expo's entry point is not called index.
|
|
39
|
+
EXPECT(basalt::isDevBundleRequest("GET", "http://localhost:8081/node_modules/expo/AppEntry.bundle?platform=ios"));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
TEST(other_dev_server_traffic_is_not_the_bundle) {
|
|
43
|
+
withDevServer();
|
|
44
|
+
// Symbolication posts a stack; it is not a bundle and its body is wanted.
|
|
45
|
+
EXPECT(!basalt::isDevBundleRequest("POST", "http://localhost:8081/symbolicate"));
|
|
46
|
+
// Assets are served by Metro too, out of the same origin.
|
|
47
|
+
EXPECT(!basalt::isDevBundleRequest("GET", "http://localhost:8081/assets/checker.png"));
|
|
48
|
+
EXPECT(!basalt::isDevBundleRequest("GET", "http://localhost:8081/status"));
|
|
49
|
+
// A source map sits next to the bundle and shares its name.
|
|
50
|
+
EXPECT(!basalt::isDevBundleRequest("GET", "http://localhost:8081/index.map?platform=ios"));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
TEST(an_apps_own_request_is_never_the_bundle) {
|
|
54
|
+
withDevServer();
|
|
55
|
+
// The point of anchoring on the origin: another server's `.bundle` is none of
|
|
56
|
+
// this code's business, and its body belongs to whoever asked for it.
|
|
57
|
+
EXPECT(!basalt::isDevBundleRequest("GET", "https://example.com/index.bundle"));
|
|
58
|
+
EXPECT(!basalt::isDevBundleRequest("GET", "http://localhost:9000/index.bundle"));
|
|
59
|
+
// A prefix match on the host alone would accept this one.
|
|
60
|
+
EXPECT(!basalt::isDevBundleRequest("GET", "http://localhost:80811/index.bundle"));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
TEST(metros_json_error_is_unwrapped) {
|
|
64
|
+
withDevServer();
|
|
65
|
+
basalt::recordDevBundleError(
|
|
66
|
+
500,
|
|
67
|
+
"http://localhost:8081/index.bundle",
|
|
68
|
+
R"json({"type":"TransformError","message":"App.js: Unexpected token (12:4)","errors":[]})json");
|
|
69
|
+
|
|
70
|
+
const auto error = basalt::devBundleError();
|
|
71
|
+
EXPECT(error.has_value());
|
|
72
|
+
EXPECT_EQ(static_cast<int>(error->status), 500);
|
|
73
|
+
// The message, not the JSON around it: it is what the developer has to read.
|
|
74
|
+
EXPECT_EQ(error->message, std::string("App.js: Unexpected token (12:4)"));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
TEST(a_body_that_is_not_metros_is_kept_whole) {
|
|
78
|
+
withDevServer();
|
|
79
|
+
// A proxy, a tunnel's error page, a plain string. Unparseable is not a reason
|
|
80
|
+
// to report nothing -- whatever answered is the only clue there is.
|
|
81
|
+
basalt::recordDevBundleError(502, "http://localhost:8081/index.bundle", "<html>Bad Gateway</html>");
|
|
82
|
+
|
|
83
|
+
const auto error = basalt::devBundleError();
|
|
84
|
+
EXPECT(error.has_value());
|
|
85
|
+
EXPECT_EQ(static_cast<int>(error->status), 502);
|
|
86
|
+
EXPECT_EQ(error->message, std::string("<html>Bad Gateway</html>"));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
TEST(a_dev_script_url_names_the_hosts_own_platform) {
|
|
90
|
+
// HMRClient registers this URL with Metro for Fast Refresh, and Metro sends
|
|
91
|
+
// updates for the platform it names. It used to say linux on every host, so
|
|
92
|
+
// a Windows app registered a Linux module graph and no edit ever reached it.
|
|
93
|
+
EXPECT_EQ(basalt::scriptURLFor("build/main.jsbundle.js", true, "localhost", 8081, "index", "windows"),
|
|
94
|
+
std::string("http://localhost:8081/index.bundle?platform=windows&dev=true"));
|
|
95
|
+
EXPECT_EQ(basalt::scriptURLFor("x.js", true, "127.0.0.1", 19000, "node_modules/expo/AppEntry", "macos"),
|
|
96
|
+
std::string("http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=macos&dev=true"));
|
|
97
|
+
|
|
98
|
+
// Out of dev mode the platform plays no part: the answer is the file.
|
|
99
|
+
const std::string file =
|
|
100
|
+
basalt::scriptURLFor("build/main.jsbundle.js", false, "localhost", 8081, "index", "windows");
|
|
101
|
+
EXPECT(file.rfind("file://", 0) == 0);
|
|
102
|
+
EXPECT(file.find("main.jsbundle.js") != std::string::npos);
|
|
103
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// The parts of drag and drop that are a decision rather than a toolkit call.
|
|
2
|
+
//
|
|
3
|
+
// Which view is told, and whether it would take what is being dragged, are the
|
|
4
|
+
// same questions on three desktops and are answered here once. What each
|
|
5
|
+
// toolkit calls its drop target is not.
|
|
6
|
+
|
|
7
|
+
#include "TestHarness.h"
|
|
8
|
+
|
|
9
|
+
#include "DragAndDrop.h"
|
|
10
|
+
|
|
11
|
+
#include <sstream>
|
|
12
|
+
#include <string>
|
|
13
|
+
|
|
14
|
+
using basalt::DragPayload;
|
|
15
|
+
using basalt::DropAcceptsFiles;
|
|
16
|
+
using basalt::DropAcceptsNone;
|
|
17
|
+
using basalt::DropAcceptsText;
|
|
18
|
+
|
|
19
|
+
TEST(drop_an_unmarked_native_id_accepts_nothing) {
|
|
20
|
+
// Every other nativeID an app sets, including the title bar's and its own.
|
|
21
|
+
EXPECT_EQ((int)basalt::dropAcceptsFrom(""), (int)DropAcceptsNone);
|
|
22
|
+
EXPECT_EQ((int)basalt::dropAcceptsFrom("my-view"), (int)DropAcceptsNone);
|
|
23
|
+
EXPECT_EQ((int)basalt::dropAcceptsFrom("basalt-titlebar-drag"), (int)DropAcceptsNone);
|
|
24
|
+
// Close, and not it: a prefix match must be a prefix.
|
|
25
|
+
EXPECT_EQ((int)basalt::dropAcceptsFrom("basalt-drop"), (int)DropAcceptsNone);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
TEST(drop_a_marker_says_what_it_takes) {
|
|
29
|
+
EXPECT_EQ((int)basalt::dropAcceptsFrom("basalt-drop:files"), (int)DropAcceptsFiles);
|
|
30
|
+
EXPECT_EQ((int)basalt::dropAcceptsFrom("basalt-drop:text"), (int)DropAcceptsText);
|
|
31
|
+
EXPECT_EQ((int)basalt::dropAcceptsFrom("basalt-drop:files,text"),
|
|
32
|
+
(int)(DropAcceptsFiles | DropAcceptsText));
|
|
33
|
+
// Order is the app's, not ours.
|
|
34
|
+
EXPECT_EQ((int)basalt::dropAcceptsFrom("basalt-drop:text,files"),
|
|
35
|
+
(int)(DropAcceptsFiles | DropAcceptsText));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
TEST(drop_an_unknown_kind_degrades_rather_than_refuses) {
|
|
39
|
+
// A marker from a newer JavaScript than this host. Taking the kinds it
|
|
40
|
+
// knows is the difference between a view that accepts files and one that
|
|
41
|
+
// accepts nothing at all.
|
|
42
|
+
EXPECT_EQ((int)basalt::dropAcceptsFrom("basalt-drop:files,html"), (int)DropAcceptsFiles);
|
|
43
|
+
EXPECT_EQ((int)basalt::dropAcceptsFrom("basalt-drop:html"), (int)DropAcceptsNone);
|
|
44
|
+
// And an empty list is a marker that accepts nothing, which is not a crash.
|
|
45
|
+
EXPECT_EQ((int)basalt::dropAcceptsFrom("basalt-drop:"), (int)DropAcceptsNone);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
TEST(drop_would_accept_asks_about_what_is_being_dragged) {
|
|
49
|
+
DragPayload files;
|
|
50
|
+
files.files.push_back("/tmp/one.txt");
|
|
51
|
+
|
|
52
|
+
DragPayload text;
|
|
53
|
+
text.text = "hello";
|
|
54
|
+
|
|
55
|
+
DragPayload both;
|
|
56
|
+
both.files.push_back("/tmp/one.txt");
|
|
57
|
+
both.text = "hello";
|
|
58
|
+
|
|
59
|
+
EXPECT(basalt::wouldAccept(DropAcceptsFiles, files));
|
|
60
|
+
EXPECT(!basalt::wouldAccept(DropAcceptsFiles, text));
|
|
61
|
+
EXPECT(basalt::wouldAccept(DropAcceptsText, text));
|
|
62
|
+
EXPECT(!basalt::wouldAccept(DropAcceptsText, files));
|
|
63
|
+
|
|
64
|
+
// Either kind is enough when a view takes both, and a view taking one still
|
|
65
|
+
// takes a payload carrying both -- a file manager commonly offers a file as
|
|
66
|
+
// a path and as its name.
|
|
67
|
+
EXPECT(basalt::wouldAccept(DropAcceptsFiles | DropAcceptsText, files));
|
|
68
|
+
EXPECT(basalt::wouldAccept(DropAcceptsFiles, both));
|
|
69
|
+
EXPECT(basalt::wouldAccept(DropAcceptsText, both));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
TEST(drop_nothing_is_accepted_by_nobody) {
|
|
73
|
+
DragPayload nothing;
|
|
74
|
+
EXPECT(!basalt::wouldAccept(DropAcceptsFiles | DropAcceptsText, nothing));
|
|
75
|
+
// And a view that accepts nothing refuses a payload that has something,
|
|
76
|
+
// which is the case an unmarked view hits on every drag that passes over it.
|
|
77
|
+
DragPayload files;
|
|
78
|
+
files.files.push_back("/tmp/one.txt");
|
|
79
|
+
EXPECT(!basalt::wouldAccept(DropAcceptsNone, files));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
TEST(drop_the_marker_round_trips) {
|
|
83
|
+
// JavaScript writes these strings and C++ reads them, from two files that
|
|
84
|
+
// cannot include each other. A round trip is what keeps them the same.
|
|
85
|
+
for (std::uint16_t accepts : {(std::uint16_t)DropAcceptsFiles,
|
|
86
|
+
(std::uint16_t)DropAcceptsText,
|
|
87
|
+
(std::uint16_t)(DropAcceptsFiles | DropAcceptsText)}) {
|
|
88
|
+
EXPECT_EQ((int)basalt::dropAcceptsFrom(basalt::dropTargetIdFor(accepts)), (int)accepts);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// --- Dragging out -----------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
TEST(drag_an_unmarked_native_id_carries_nothing) {
|
|
95
|
+
EXPECT(basalt::dragPayloadFrom("").empty());
|
|
96
|
+
EXPECT(basalt::dragPayloadFrom("my-view").empty());
|
|
97
|
+
// A drop marker is not a drag marker, which matters because a view may be
|
|
98
|
+
// both and the two prefixes must not collide.
|
|
99
|
+
EXPECT(basalt::dragPayloadFrom("basalt-drop:files").empty());
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
TEST(drag_a_marker_says_what_it_is) {
|
|
103
|
+
const DragPayload files = basalt::dragPayloadFrom("basalt-drag:files:/tmp/one.txt");
|
|
104
|
+
EXPECT_EQ((int)files.files.size(), 1);
|
|
105
|
+
EXPECT_EQ(files.files.empty() ? std::string{} : files.files[0], std::string("/tmp/one.txt"));
|
|
106
|
+
EXPECT(!files.hasText());
|
|
107
|
+
|
|
108
|
+
const DragPayload text = basalt::dragPayloadFrom("basalt-drag:text:hello");
|
|
109
|
+
EXPECT_EQ(text.text, std::string("hello"));
|
|
110
|
+
EXPECT(!text.hasFiles());
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
TEST(drag_a_value_may_contain_a_colon) {
|
|
114
|
+
// Only the first two colons are structure. A Windows path and a URL both
|
|
115
|
+
// depend on this, and splitting on every colon would quietly truncate them.
|
|
116
|
+
const DragPayload url = basalt::dragPayloadFrom("basalt-drag:text:https://example.com/a");
|
|
117
|
+
EXPECT_EQ(url.text, std::string("https://example.com/a"));
|
|
118
|
+
|
|
119
|
+
const DragPayload windows = basalt::dragPayloadFrom("basalt-drag:files:C:\\Users\\a.txt");
|
|
120
|
+
EXPECT_EQ(windows.files.empty() ? std::string{} : windows.files[0],
|
|
121
|
+
std::string("C:\\Users\\a.txt"));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
TEST(drag_an_empty_or_unknown_marker_carries_nothing) {
|
|
125
|
+
EXPECT(basalt::dragPayloadFrom("basalt-drag:text:").empty());
|
|
126
|
+
EXPECT(basalt::dragPayloadFrom("basalt-drag:files:").empty());
|
|
127
|
+
EXPECT(basalt::dragPayloadFrom("basalt-drag:html:<b>").empty());
|
|
128
|
+
// No kind at all, which is what a half-written marker looks like.
|
|
129
|
+
EXPECT(basalt::dragPayloadFrom("basalt-drag:").empty());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
TEST(drag_the_marker_round_trips) {
|
|
133
|
+
DragPayload files;
|
|
134
|
+
files.files.push_back("/tmp/one.txt");
|
|
135
|
+
DragPayload text;
|
|
136
|
+
text.text = "hello world";
|
|
137
|
+
|
|
138
|
+
for (const DragPayload &payload : {files, text}) {
|
|
139
|
+
const DragPayload back = basalt::dragPayloadFrom(basalt::dragSourceIdFor(payload));
|
|
140
|
+
EXPECT_EQ(back.files.size(), payload.files.size());
|
|
141
|
+
EXPECT_EQ(back.text, payload.text);
|
|
142
|
+
}
|
|
143
|
+
}
|