@buoy-gg/network 7.0.41 → 7.0.43
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/README.md +11 -85
- package/lib/commonjs/index.js +1 -332
- package/lib/commonjs/index.web.js +1 -420
- package/lib/commonjs/network/components/DataViewer.web.js +1 -879
- package/lib/commonjs/network/components/NetworkCopyScreen.web.js +1 -48
- package/lib/commonjs/network/components/NetworkCopySettingsView.js +27 -755
- package/lib/commonjs/network/components/NetworkCopySettingsView.web.js +14 -1242
- package/lib/commonjs/network/components/NetworkDetailScreen.web.js +1 -101
- package/lib/commonjs/network/components/NetworkDetailStepper.js +1 -108
- package/lib/commonjs/network/components/NetworkDetailStepper.web.js +1 -298
- package/lib/commonjs/network/components/NetworkEventDetailView.js +15 -1130
- package/lib/commonjs/network/components/NetworkEventDetailView.web.js +15 -1484
- package/lib/commonjs/network/components/NetworkEventItemCompact.js +2 -401
- package/lib/commonjs/network/components/NetworkEventItemCompact.web.js +2 -820
- package/lib/commonjs/network/components/NetworkFilterScreen.web.js +1 -55
- package/lib/commonjs/network/components/NetworkFilterViewV3.js +1 -442
- package/lib/commonjs/network/components/NetworkFilterViewV3.web.js +1 -1310
- package/lib/commonjs/network/components/NetworkHeaderLive.js +1 -375
- package/lib/commonjs/network/components/NetworkHeaderLive.web.js +1 -623
- package/lib/commonjs/network/components/NetworkHeaderMenu.js +1 -190
- package/lib/commonjs/network/components/NetworkListScreen.js +1 -403
- package/lib/commonjs/network/components/NetworkListScreen.web.js +1 -624
- package/lib/commonjs/network/components/NetworkModal.js +1 -786
- package/lib/commonjs/network/components/NetworkNumberPrompt.js +1 -178
- package/lib/commonjs/network/components/NetworkNumberPrompt.web.js +1 -232
- package/lib/commonjs/network/components/NetworkOverrideEditor.js +1 -814
- package/lib/commonjs/network/components/NetworkOverrideEditor.web.js +1 -942
- package/lib/commonjs/network/components/NetworkOverrideHeaderButton.js +1 -107
- package/lib/commonjs/network/components/NetworkOverrideHeaderButton.web.js +1 -133
- package/lib/commonjs/network/components/NetworkOverridesButton.js +1 -90
- package/lib/commonjs/network/components/NetworkOverridesButton.web.js +1 -143
- package/lib/commonjs/network/components/NetworkOverridesScreen.js +1 -543
- package/lib/commonjs/network/components/NetworkOverridesScreen.web.js +1 -830
- package/lib/commonjs/network/components/NetworkSavedScreen.js +1 -226
- package/lib/commonjs/network/components/NetworkSavedScreen.web.js +1 -463
- package/lib/commonjs/network/components/NetworkUpgradeBanner.js +1 -76
- package/lib/commonjs/network/components/response-editor/Explorer.js +1 -973
- package/lib/commonjs/network/components/response-editor/Explorer.web.js +1 -1136
- package/lib/commonjs/network/components/response-editor/deleteNestedDataByPath.js +1 -56
- package/lib/commonjs/network/components/response-editor/editorContext.js +1 -33
- package/lib/commonjs/network/components/response-editor/updateNestedDataByPath.js +1 -61
- package/lib/commonjs/network/conditions/NetworkConditionsControl.js +1 -235
- package/lib/commonjs/network/conditions/NetworkConditionsControl.test.js +1 -136
- package/lib/commonjs/network/conditions/NetworkConditionsIcon.js +1 -33
- package/lib/commonjs/network/conditions/NetworkFloatingTool.js +1 -310
- package/lib/commonjs/network/conditions/NetworkFloatingTool.test.js +1 -219
- package/lib/commonjs/network/conditions/store.js +1 -75
- package/lib/commonjs/network/conditions/wait.js +1 -43
- package/lib/commonjs/network/conditions/xhr.js +1 -135
- package/lib/commonjs/network/hooks/useLiveNetworkEvent.js +1 -62
- package/lib/commonjs/network/hooks/useNetworkBadgeCounts.js +1 -83
- package/lib/commonjs/network/hooks/useNetworkEventList.js +1 -108
- package/lib/commonjs/network/hooks/useNetworkEvents.js +1 -318
- package/lib/commonjs/network/hooks/useNetworkHasMatches.js +1 -56
- package/lib/commonjs/network/hooks/useNetworkSaved.js +1 -38
- package/lib/commonjs/network/hooks/useNetworkSelection.js +1 -53
- package/lib/commonjs/network/hooks/useOverrideRules.js +1 -36
- package/lib/commonjs/network/hooks/useTickEveryMinute.js +1 -21
- package/lib/commonjs/network/overrides/applyToFetch.js +1 -80
- package/lib/commonjs/network/overrides/applyToXhr.js +2 -510
- package/lib/commonjs/network/overrides/engine.js +1 -93
- package/lib/commonjs/network/overrides/matchRule.js +1 -87
- package/lib/commonjs/network/overrides/overriddenRows.js +1 -37
- package/lib/commonjs/network/overrides/overrideRulesStore.js +1 -550
- package/lib/commonjs/network/overrides/presets.js +1 -70
- package/lib/commonjs/network/overrides/resolveOverride.js +1 -158
- package/lib/commonjs/network/overrides/types.js +1 -90
- package/lib/commonjs/network/sync/networkBodyResolver.js +1 -39
- package/lib/commonjs/network/sync/networkSyncAdapter.js +1 -882
- package/lib/commonjs/network/sync/overrideHonesty.release.test.js +1 -36
- package/lib/commonjs/network/types/index.js +1 -5
- package/lib/commonjs/network/utils/extractOperationName.js +1 -80
- package/lib/commonjs/network/utils/filterNetworkEvents.js +1 -96
- package/lib/commonjs/network/utils/formatGraphQLVariables.js +1 -219
- package/lib/commonjs/network/utils/formatting.js +1 -30
- package/lib/commonjs/network/utils/generateCurl.js +2 -71
- package/lib/commonjs/network/utils/generateNetworkCopyText.js +37 -166
- package/lib/commonjs/network/utils/httpCompletion.js +1 -37
- package/lib/commonjs/network/utils/networkEventStore.js +1 -411
- package/lib/commonjs/network/utils/networkListener.js +4 -1352
- package/lib/commonjs/network/utils/networkSavedStore.js +3 -577
- package/lib/commonjs/network/utils/networkSelectionStore.js +1 -193
- package/lib/commonjs/network/utils/realNow.js +1 -0
- package/lib/commonjs/network/utils/requestId.js +1 -16
- package/lib/commonjs/network/utils/resolveRequestUrl.js +1 -15
- package/lib/commonjs/network/utils/selectSavedEvents.js +1 -39
- package/lib/commonjs/network/utils/startNetworkCapture.js +1 -24
- package/lib/commonjs/network/utils/withPinnedFirst.js +1 -32
- package/lib/commonjs/preset.js +1 -93
- package/lib/module/index.js +1 -104
- package/lib/module/index.web.js +1 -82
- package/lib/module/network/components/DataViewer.web.js +1 -875
- package/lib/module/network/components/NetworkCopyScreen.web.js +1 -44
- package/lib/module/network/components/NetworkCopySettingsView.js +27 -750
- package/lib/module/network/components/NetworkCopySettingsView.web.js +14 -1237
- package/lib/module/network/components/NetworkDetailScreen.web.js +1 -97
- package/lib/module/network/components/NetworkDetailStepper.js +1 -104
- package/lib/module/network/components/NetworkDetailStepper.web.js +1 -296
- package/lib/module/network/components/NetworkEventDetailView.js +15 -1129
- package/lib/module/network/components/NetworkEventDetailView.web.js +15 -1482
- package/lib/module/network/components/NetworkEventItemCompact.js +2 -398
- package/lib/module/network/components/NetworkEventItemCompact.web.js +2 -818
- package/lib/module/network/components/NetworkFilterScreen.web.js +1 -51
- package/lib/module/network/components/NetworkFilterViewV3.js +1 -438
- package/lib/module/network/components/NetworkFilterViewV3.web.js +1 -1306
- package/lib/module/network/components/NetworkHeaderLive.js +1 -366
- package/lib/module/network/components/NetworkHeaderLive.web.js +1 -617
- package/lib/module/network/components/NetworkHeaderMenu.js +1 -184
- package/lib/module/network/components/NetworkListScreen.js +1 -399
- package/lib/module/network/components/NetworkListScreen.web.js +1 -622
- package/lib/module/network/components/NetworkModal.js +1 -781
- package/lib/module/network/components/NetworkNumberPrompt.js +1 -174
- package/lib/module/network/components/NetworkNumberPrompt.web.js +1 -230
- package/lib/module/network/components/NetworkOverrideEditor.js +1 -811
- package/lib/module/network/components/NetworkOverrideEditor.web.js +1 -939
- package/lib/module/network/components/NetworkOverrideHeaderButton.js +1 -104
- package/lib/module/network/components/NetworkOverrideHeaderButton.web.js +1 -131
- package/lib/module/network/components/NetworkOverridesButton.js +1 -87
- package/lib/module/network/components/NetworkOverridesButton.web.js +1 -141
- package/lib/module/network/components/NetworkOverridesScreen.js +1 -539
- package/lib/module/network/components/NetworkOverridesScreen.web.js +1 -826
- package/lib/module/network/components/NetworkSavedScreen.js +1 -222
- package/lib/module/network/components/NetworkSavedScreen.web.js +1 -461
- package/lib/module/network/components/NetworkUpgradeBanner.js +1 -72
- package/lib/module/network/components/response-editor/Explorer.js +1 -972
- package/lib/module/network/components/response-editor/Explorer.web.js +1 -1133
- package/lib/module/network/components/response-editor/deleteNestedDataByPath.js +1 -51
- package/lib/module/network/components/response-editor/editorContext.js +1 -29
- package/lib/module/network/components/response-editor/updateNestedDataByPath.js +1 -56
- package/lib/module/network/conditions/NetworkConditionsControl.js +1 -231
- package/lib/module/network/conditions/NetworkConditionsControl.test.js +1 -135
- package/lib/module/network/conditions/NetworkConditionsIcon.js +1 -29
- package/lib/module/network/conditions/NetworkFloatingTool.js +1 -304
- package/lib/module/network/conditions/NetworkFloatingTool.test.js +1 -218
- package/lib/module/network/conditions/store.js +1 -65
- package/lib/module/network/conditions/wait.js +1 -38
- package/lib/module/network/conditions/xhr.js +1 -131
- package/lib/module/network/hooks/useLiveNetworkEvent.js +1 -58
- package/lib/module/network/hooks/useNetworkBadgeCounts.js +1 -79
- package/lib/module/network/hooks/useNetworkEventList.js +1 -104
- package/lib/module/network/hooks/useNetworkEvents.js +1 -314
- package/lib/module/network/hooks/useNetworkHasMatches.js +1 -52
- package/lib/module/network/hooks/useNetworkSaved.js +1 -34
- package/lib/module/network/hooks/useNetworkSelection.js +1 -46
- package/lib/module/network/hooks/useOverrideRules.js +1 -32
- package/lib/module/network/hooks/useTickEveryMinute.js +1 -11
- package/lib/module/network/overrides/applyToFetch.js +1 -72
- package/lib/module/network/overrides/applyToXhr.js +2 -502
- package/lib/module/network/overrides/engine.js +1 -85
- package/lib/module/network/overrides/matchRule.js +1 -81
- package/lib/module/network/overrides/overriddenRows.js +1 -33
- package/lib/module/network/overrides/overrideRulesStore.js +1 -547
- package/lib/module/network/overrides/presets.js +1 -65
- package/lib/module/network/overrides/resolveOverride.js +1 -152
- package/lib/module/network/overrides/types.js +1 -86
- package/lib/module/network/sync/networkBodyResolver.js +1 -34
- package/lib/module/network/sync/networkSyncAdapter.js +1 -880
- package/lib/module/network/sync/overrideHonesty.release.test.js +1 -35
- package/lib/module/network/types/index.js +1 -3
- package/lib/module/network/utils/extractOperationName.js +1 -76
- package/lib/module/network/utils/filterNetworkEvents.js +1 -92
- package/lib/module/network/utils/formatGraphQLVariables.js +1 -213
- package/lib/module/network/utils/formatting.js +1 -9
- package/lib/module/network/utils/generateCurl.js +2 -67
- package/lib/module/network/utils/generateNetworkCopyText.js +37 -162
- package/lib/module/network/utils/httpCompletion.js +1 -32
- package/lib/module/network/utils/networkEventStore.js +1 -409
- package/lib/module/network/utils/networkListener.js +4 -1341
- package/lib/module/network/utils/networkSavedStore.js +3 -572
- package/lib/module/network/utils/networkSelectionStore.js +1 -188
- package/lib/module/network/utils/realNow.js +1 -0
- package/lib/module/network/utils/requestId.js +1 -12
- package/lib/module/network/utils/resolveRequestUrl.js +1 -11
- package/lib/module/network/utils/selectSavedEvents.js +1 -35
- package/lib/module/network/utils/startNetworkCapture.js +1 -21
- package/lib/module/network/utils/withPinnedFirst.js +1 -28
- package/lib/module/preset.js +1 -91
- package/lib/typescript/network/overrides/engine.d.ts +1 -1
- package/lib/typescript/network/overrides/matchRule.d.ts +1 -1
- package/lib/typescript/network/overrides/resolveOverride.d.ts +2 -2
- package/lib/typescript/network/overrides/types.d.ts +9 -0
- package/lib/typescript/network/utils/networkListener.d.ts +11 -0
- package/lib/typescript/network/utils/realNow.d.ts +18 -0
- package/lib/web/index.mjs +73 -74
- package/package.json +3 -6
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/index.web.d.ts.map +0 -1
- package/lib/typescript/network/components/DataViewer.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkCopyScreen.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkCopySettingsView.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkCopySettingsView.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkDetailScreen.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkDetailStepper.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkDetailStepper.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkEventDetailView.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkEventDetailView.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkEventItemCompact.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkEventItemCompact.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkFilterScreen.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkFilterViewV3.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkFilterViewV3.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkHeaderLive.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkHeaderLive.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkHeaderMenu.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkListScreen.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkListScreen.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkModal.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkNumberPrompt.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkNumberPrompt.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverrideEditor.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverrideEditor.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverrideHeaderButton.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverrideHeaderButton.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverridesButton.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverridesButton.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverridesScreen.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverridesScreen.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkSavedScreen.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkSavedScreen.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkUpgradeBanner.d.ts.map +0 -1
- package/lib/typescript/network/components/response-editor/Explorer.d.ts.map +0 -1
- package/lib/typescript/network/components/response-editor/Explorer.web.d.ts.map +0 -1
- package/lib/typescript/network/components/response-editor/deleteNestedDataByPath.d.ts.map +0 -1
- package/lib/typescript/network/components/response-editor/editorContext.d.ts.map +0 -1
- package/lib/typescript/network/components/response-editor/updateNestedDataByPath.d.ts.map +0 -1
- package/lib/typescript/network/conditions/NetworkConditionsControl.d.ts.map +0 -1
- package/lib/typescript/network/conditions/NetworkConditionsControl.test.d.ts.map +0 -1
- package/lib/typescript/network/conditions/NetworkConditionsIcon.d.ts.map +0 -1
- package/lib/typescript/network/conditions/NetworkFloatingTool.d.ts.map +0 -1
- package/lib/typescript/network/conditions/NetworkFloatingTool.test.d.ts.map +0 -1
- package/lib/typescript/network/conditions/store.d.ts.map +0 -1
- package/lib/typescript/network/conditions/wait.d.ts.map +0 -1
- package/lib/typescript/network/conditions/xhr.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useLiveNetworkEvent.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useNetworkBadgeCounts.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useNetworkEventList.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useNetworkEvents.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useNetworkHasMatches.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useNetworkSaved.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useNetworkSelection.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useOverrideRules.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useTickEveryMinute.d.ts.map +0 -1
- package/lib/typescript/network/overrides/applyToFetch.d.ts.map +0 -1
- package/lib/typescript/network/overrides/applyToXhr.d.ts.map +0 -1
- package/lib/typescript/network/overrides/engine.d.ts.map +0 -1
- package/lib/typescript/network/overrides/matchRule.d.ts.map +0 -1
- package/lib/typescript/network/overrides/overriddenRows.d.ts.map +0 -1
- package/lib/typescript/network/overrides/overrideRulesStore.d.ts.map +0 -1
- package/lib/typescript/network/overrides/presets.d.ts.map +0 -1
- package/lib/typescript/network/overrides/resolveOverride.d.ts.map +0 -1
- package/lib/typescript/network/overrides/types.d.ts.map +0 -1
- package/lib/typescript/network/sync/networkBodyResolver.d.ts.map +0 -1
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts.map +0 -1
- package/lib/typescript/network/sync/overrideHonesty.release.test.d.ts.map +0 -1
- package/lib/typescript/network/types/index.d.ts.map +0 -1
- package/lib/typescript/network/utils/extractOperationName.d.ts.map +0 -1
- package/lib/typescript/network/utils/filterNetworkEvents.d.ts.map +0 -1
- package/lib/typescript/network/utils/formatGraphQLVariables.d.ts.map +0 -1
- package/lib/typescript/network/utils/formatting.d.ts.map +0 -1
- package/lib/typescript/network/utils/generateCurl.d.ts.map +0 -1
- package/lib/typescript/network/utils/generateNetworkCopyText.d.ts.map +0 -1
- package/lib/typescript/network/utils/httpCompletion.d.ts.map +0 -1
- package/lib/typescript/network/utils/networkEventStore.d.ts.map +0 -1
- package/lib/typescript/network/utils/networkListener.d.ts.map +0 -1
- package/lib/typescript/network/utils/networkSavedStore.d.ts.map +0 -1
- package/lib/typescript/network/utils/networkSelectionStore.d.ts.map +0 -1
- package/lib/typescript/network/utils/requestId.d.ts.map +0 -1
- package/lib/typescript/network/utils/resolveRequestUrl.d.ts.map +0 -1
- package/lib/typescript/network/utils/selectSavedEvents.d.ts.map +0 -1
- package/lib/typescript/network/utils/startNetworkCapture.d.ts.map +0 -1
- package/lib/typescript/network/utils/withPinnedFirst.d.ts.map +0 -1
- package/lib/typescript/preset.d.ts.map +0 -1
|
@@ -1,304 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
4
|
-
import { ActivityIndicator, StyleSheet, Text, TouchableOpacity, View, useWindowDimensions } from "react-native";
|
|
5
|
-
import { FloatingTool, night, openFloatingTool, persistentStorage, registerPersistentFloatingTool } from "@buoy-gg/shared-ui";
|
|
6
|
-
import { conditionsCanApply, getNetworkCondition, setNetworkCondition, subscribeNetworkConditions } from "./store";
|
|
7
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
|
-
/** A live source keeps the desktop strip current after its launching panel closes. */
|
|
9
|
-
export function createRemoteConditionsSource() {
|
|
10
|
-
let value;
|
|
11
|
-
const listeners = new Set();
|
|
12
|
-
return {
|
|
13
|
-
getSnapshot: () => value,
|
|
14
|
-
subscribe: listener => {
|
|
15
|
-
listeners.add(listener);
|
|
16
|
-
return () => {
|
|
17
|
-
listeners.delete(listener);
|
|
18
|
-
};
|
|
19
|
-
},
|
|
20
|
-
update(next) {
|
|
21
|
-
value = next;
|
|
22
|
-
listeners.forEach(listener => listener());
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
const noSubscribe = () => () => {};
|
|
27
|
-
const noRemote = () => undefined;
|
|
28
|
-
const options = [{
|
|
29
|
-
id: "normal",
|
|
30
|
-
label: "No throttling",
|
|
31
|
-
detail: "No added delay"
|
|
32
|
-
}, {
|
|
33
|
-
id: "slow",
|
|
34
|
-
label: "Slow",
|
|
35
|
-
detail: "+500 ms before sending"
|
|
36
|
-
}, {
|
|
37
|
-
id: "verySlow",
|
|
38
|
-
label: "Very slow",
|
|
39
|
-
detail: "+2 seconds before sending"
|
|
40
|
-
}, {
|
|
41
|
-
id: "offline",
|
|
42
|
-
label: "Offline",
|
|
43
|
-
detail: "New requests fail"
|
|
44
|
-
}];
|
|
45
|
-
const localRegistration = {
|
|
46
|
-
id: "network-throttling",
|
|
47
|
-
render: context => /*#__PURE__*/_jsx(NetworkFloatingTool, {
|
|
48
|
-
...context
|
|
49
|
-
})
|
|
50
|
-
};
|
|
51
|
-
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof navigator !== "undefined" && navigator.product === "ReactNative") {
|
|
52
|
-
registerPersistentFloatingTool(localRegistration, persistentStorage);
|
|
53
|
-
}
|
|
54
|
-
export function openNetworkFloatingTool(source) {
|
|
55
|
-
openFloatingTool({
|
|
56
|
-
id: "network-throttling",
|
|
57
|
-
render: context => /*#__PURE__*/_jsx(NetworkFloatingTool, {
|
|
58
|
-
...context,
|
|
59
|
-
source: source
|
|
60
|
-
})
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
export function NetworkFloatingTool({
|
|
64
|
-
id,
|
|
65
|
-
revealToken,
|
|
66
|
-
dismiss,
|
|
67
|
-
source
|
|
68
|
-
}) {
|
|
69
|
-
const local = useSyncExternalStore(subscribeNetworkConditions, getNetworkCondition, getNetworkCondition);
|
|
70
|
-
const localAvailable = useSyncExternalStore(subscribeNetworkConditions, conditionsCanApply, conditionsCanApply);
|
|
71
|
-
const remote = useSyncExternalStore(source?.subscribe ?? noSubscribe, source?.getSnapshot ?? noRemote, noRemote);
|
|
72
|
-
const profile = source ? remote?.profile ?? null : local;
|
|
73
|
-
const available = source ? remote?.available === true : localAvailable;
|
|
74
|
-
const [pending, setPending] = useState(false);
|
|
75
|
-
const [error, setError] = useState();
|
|
76
|
-
const busy = useRef(false);
|
|
77
|
-
const operation = useRef(0);
|
|
78
|
-
const mounted = useRef(true);
|
|
79
|
-
const {
|
|
80
|
-
width
|
|
81
|
-
} = useWindowDimensions();
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
mounted.current = true;
|
|
84
|
-
return () => {
|
|
85
|
-
mounted.current = false;
|
|
86
|
-
};
|
|
87
|
-
}, []);
|
|
88
|
-
useEffect(() => {
|
|
89
|
-
operation.current += 1;
|
|
90
|
-
busy.current = false;
|
|
91
|
-
setPending(false);
|
|
92
|
-
setError(undefined);
|
|
93
|
-
}, [source, remote?.targetId]);
|
|
94
|
-
const active = profile !== null && profile !== "normal";
|
|
95
|
-
const selected = profile ?? "normal";
|
|
96
|
-
const choice = options.find(option => option.id === selected);
|
|
97
|
-
const next = options[(options.findIndex(option => option.id === selected) + 1) % options.length];
|
|
98
|
-
const bars = !available || selected === "offline" ? 0 : selected === "verySlow" ? 1 : selected === "slow" ? 2 : 4;
|
|
99
|
-
const detail = pending ? "Applying…" : !available ? "Unavailable" : selected === "normal" ? "0 ms" : selected === "slow" ? "+500 ms" : selected === "verySlow" ? "+2000 ms" : "Blocked";
|
|
100
|
-
async function apply(next, close = false) {
|
|
101
|
-
if (busy.current) return;
|
|
102
|
-
const currentOperation = ++operation.current;
|
|
103
|
-
busy.current = true;
|
|
104
|
-
setPending(true);
|
|
105
|
-
setError(undefined);
|
|
106
|
-
const startingRemote = source?.getSnapshot();
|
|
107
|
-
try {
|
|
108
|
-
if (source) {
|
|
109
|
-
if (!startingRemote?.available) throw new Error("Device unavailable. Open Network again to reconnect these controls.");
|
|
110
|
-
await startingRemote.onChange(next);
|
|
111
|
-
} else setNetworkCondition(next);
|
|
112
|
-
if (mounted.current && operation.current === currentOperation) {
|
|
113
|
-
if (close) dismiss();
|
|
114
|
-
}
|
|
115
|
-
} catch (failure) {
|
|
116
|
-
if (mounted.current && operation.current === currentOperation) setError(failure instanceof Error ? failure.message : String(failure));
|
|
117
|
-
} finally {
|
|
118
|
-
if (operation.current === currentOperation) {
|
|
119
|
-
busy.current = false;
|
|
120
|
-
if (mounted.current) setPending(false);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
const accessory = error ? /*#__PURE__*/_jsx(View, {
|
|
125
|
-
style: styles.menu,
|
|
126
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
127
|
-
accessibilityRole: "alert",
|
|
128
|
-
style: styles.error,
|
|
129
|
-
children: error
|
|
130
|
-
})
|
|
131
|
-
}) : undefined;
|
|
132
|
-
return /*#__PURE__*/_jsx(FloatingTool, {
|
|
133
|
-
id: id,
|
|
134
|
-
revealToken: revealToken,
|
|
135
|
-
variant: "strip",
|
|
136
|
-
dragFromSurface: true,
|
|
137
|
-
accessibilityLabel: `Move or hide network throttling. ${active ? "Active" : "Off"}`,
|
|
138
|
-
accessory: accessory,
|
|
139
|
-
children: /*#__PURE__*/_jsxs(View, {
|
|
140
|
-
style: [styles.row, {
|
|
141
|
-
width: Math.max(220, Math.min(292, width - 64))
|
|
142
|
-
}],
|
|
143
|
-
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
|
|
144
|
-
testID: "throttling-cycle",
|
|
145
|
-
accessibilityRole: "button",
|
|
146
|
-
accessibilityLabel: `Network: ${choice.label}. Switch to ${next.label}`,
|
|
147
|
-
accessibilityState: {
|
|
148
|
-
disabled: pending || !available
|
|
149
|
-
},
|
|
150
|
-
disabled: pending || !available,
|
|
151
|
-
onPress: () => void apply(next.id),
|
|
152
|
-
style: styles.signalButton,
|
|
153
|
-
children: pending ? /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
154
|
-
size: "small",
|
|
155
|
-
color: night.color.accent
|
|
156
|
-
}) : /*#__PURE__*/_jsxs(View, {
|
|
157
|
-
testID: `throttling-signal-${selected}`,
|
|
158
|
-
style: styles.signal,
|
|
159
|
-
children: [[7, 12, 18, 24].map((height, index) => /*#__PURE__*/_jsx(View, {
|
|
160
|
-
style: {
|
|
161
|
-
width: 4,
|
|
162
|
-
height,
|
|
163
|
-
borderRadius: 2,
|
|
164
|
-
backgroundColor: index < bars ? selected === "offline" ? night.color.danger : night.color.accent : night.color.fillTertiary
|
|
165
|
-
}
|
|
166
|
-
}, height)), selected === "offline" && /*#__PURE__*/_jsx(View, {
|
|
167
|
-
style: styles.slash
|
|
168
|
-
})]
|
|
169
|
-
})
|
|
170
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
171
|
-
style: styles.profile,
|
|
172
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
173
|
-
testID: "throttling-profile",
|
|
174
|
-
accessibilityLabel: `Connection profile: ${choice.label}`,
|
|
175
|
-
accessibilityLiveRegion: "polite",
|
|
176
|
-
numberOfLines: 1,
|
|
177
|
-
style: styles.profileLabel,
|
|
178
|
-
children: choice.label
|
|
179
|
-
})
|
|
180
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
181
|
-
testID: "throttling-status",
|
|
182
|
-
numberOfLines: 1,
|
|
183
|
-
accessibilityLiveRegion: "polite",
|
|
184
|
-
style: styles.status,
|
|
185
|
-
children: detail
|
|
186
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
187
|
-
style: styles.separator
|
|
188
|
-
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
189
|
-
testID: "throttling-close",
|
|
190
|
-
accessibilityRole: "button",
|
|
191
|
-
accessibilityLabel: "Close and turn off throttling",
|
|
192
|
-
disabled: pending,
|
|
193
|
-
accessibilityState: {
|
|
194
|
-
disabled: pending
|
|
195
|
-
},
|
|
196
|
-
onPress: () => {
|
|
197
|
-
if (profile === "normal") dismiss();else void apply("normal", true);
|
|
198
|
-
},
|
|
199
|
-
style: [styles.action, pending && styles.disabled],
|
|
200
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
201
|
-
style: styles.actionText,
|
|
202
|
-
children: "Close"
|
|
203
|
-
})
|
|
204
|
-
})]
|
|
205
|
-
})
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
const styles = StyleSheet.create({
|
|
209
|
-
row: {
|
|
210
|
-
flexDirection: "row",
|
|
211
|
-
alignItems: "center",
|
|
212
|
-
gap: 6,
|
|
213
|
-
paddingVertical: 4,
|
|
214
|
-
minHeight: 48
|
|
215
|
-
},
|
|
216
|
-
signalButton: {
|
|
217
|
-
width: 36,
|
|
218
|
-
minHeight: 44,
|
|
219
|
-
alignItems: "center",
|
|
220
|
-
justifyContent: "center"
|
|
221
|
-
},
|
|
222
|
-
signal: {
|
|
223
|
-
flexDirection: "row",
|
|
224
|
-
alignItems: "flex-end",
|
|
225
|
-
gap: 3,
|
|
226
|
-
height: 24
|
|
227
|
-
},
|
|
228
|
-
slash: {
|
|
229
|
-
position: "absolute",
|
|
230
|
-
width: 30,
|
|
231
|
-
height: 2,
|
|
232
|
-
backgroundColor: night.color.danger,
|
|
233
|
-
top: 11,
|
|
234
|
-
left: -2,
|
|
235
|
-
transform: [{
|
|
236
|
-
rotate: "-45deg"
|
|
237
|
-
}]
|
|
238
|
-
},
|
|
239
|
-
separator: {
|
|
240
|
-
height: 20,
|
|
241
|
-
width: 1,
|
|
242
|
-
backgroundColor: night.color.separator,
|
|
243
|
-
marginHorizontal: 2
|
|
244
|
-
},
|
|
245
|
-
status: {
|
|
246
|
-
color: night.color.textSecondary,
|
|
247
|
-
fontSize: 10,
|
|
248
|
-
lineHeight: 15,
|
|
249
|
-
flexShrink: 1
|
|
250
|
-
},
|
|
251
|
-
profile: {
|
|
252
|
-
flex: 1,
|
|
253
|
-
flexDirection: "row",
|
|
254
|
-
alignItems: "center",
|
|
255
|
-
gap: 6,
|
|
256
|
-
minHeight: 44
|
|
257
|
-
},
|
|
258
|
-
profileLabel: {
|
|
259
|
-
color: night.color.text,
|
|
260
|
-
fontSize: 13,
|
|
261
|
-
fontWeight: "500",
|
|
262
|
-
flexShrink: 1
|
|
263
|
-
},
|
|
264
|
-
action: {
|
|
265
|
-
minHeight: 36,
|
|
266
|
-
minWidth: 52,
|
|
267
|
-
borderRadius: night.radius.input,
|
|
268
|
-
borderWidth: 1,
|
|
269
|
-
borderColor: night.color.accentBorderStrong,
|
|
270
|
-
alignItems: "center",
|
|
271
|
-
justifyContent: "center",
|
|
272
|
-
paddingHorizontal: 10
|
|
273
|
-
},
|
|
274
|
-
actionText: {
|
|
275
|
-
color: night.color.accent,
|
|
276
|
-
fontSize: 12,
|
|
277
|
-
fontWeight: "600"
|
|
278
|
-
},
|
|
279
|
-
disabled: {
|
|
280
|
-
opacity: 0.4
|
|
281
|
-
},
|
|
282
|
-
menu: {
|
|
283
|
-
marginVertical: 8,
|
|
284
|
-
padding: 6,
|
|
285
|
-
borderRadius: night.radius.card,
|
|
286
|
-
borderWidth: 1,
|
|
287
|
-
borderColor: night.color.separator,
|
|
288
|
-
backgroundColor: night.color.surface,
|
|
289
|
-
shadowColor: "#000",
|
|
290
|
-
shadowOffset: {
|
|
291
|
-
width: 0,
|
|
292
|
-
height: 6
|
|
293
|
-
},
|
|
294
|
-
shadowOpacity: 0.25,
|
|
295
|
-
shadowRadius: 16,
|
|
296
|
-
elevation: 8
|
|
297
|
-
},
|
|
298
|
-
error: {
|
|
299
|
-
color: night.color.danger,
|
|
300
|
-
fontSize: 12,
|
|
301
|
-
lineHeight: 18,
|
|
302
|
-
padding: 10
|
|
303
|
-
}
|
|
304
|
-
});
|
|
1
|
+
"use strict";import{useEffect as _,useRef as R,useState as O,useSyncExternalStore as I}from"react";import{ActivityIndicator as U,StyleSheet as G,Text as p,TouchableOpacity as E,View as d,useWindowDimensions as J}from"react-native";import{FloatingTool as K,night as e,openFloatingTool as Q,persistentStorage as X,registerPersistentFloatingTool as Y}from"@buoy-gg/shared-ui";import{conditionsCanApply as H,getNetworkCondition as z,setNetworkCondition as Z,subscribeNetworkConditions as j}from"./store";import{jsx as t,jsxs as F}from"react/jsx-runtime";export function createRemoteConditionsSource(){let l;const a=new Set;return{getSnapshot:()=>l,subscribe:s=>{a.add(s);return()=>{a.delete(s)}},update(s){l=s;a.forEach(r=>r())}}}const ee=()=>()=>{};const V=()=>void 0;const y=[{id:"normal",label:"No throttling",detail:"No added delay"},{id:"slow",label:"Slow",detail:"+500 ms before sending"},{id:"verySlow",label:"Very slow",detail:"+2 seconds before sending"},{id:"offline",label:"Offline",detail:"New requests fail"}];const te={id:"network-throttling",render:l=>t(NetworkFloatingTool,{...l})};if(typeof __DEV__!=="undefined"&&__DEV__&&typeof navigator!=="undefined"&&navigator.product==="ReactNative"){Y(te,X)}export function openNetworkFloatingTool(l){Q({id:"network-throttling",render:a=>t(NetworkFloatingTool,{...a,source:l})})}export function NetworkFloatingTool({id:l,revealToken:a,dismiss:s,source:r}){const W=I(j,z,z);const A=I(j,H,H);const m=I(r?.subscribe??ee,r?.getSnapshot??V,V);const g=r?m?.profile??null:W;const h=r?m?.available===true:A;const[c,w]=O(false);const[D,S]=O();const b=R(false);const f=R(0);const u=R(true);const{width:$}=J();_(()=>{u.current=true;return()=>{u.current=false}},[]);_(()=>{f.current+=1;b.current=false;w(false);S(void 0)},[r,m?.targetId]);const B=g!==null&&g!=="normal";const i=g??"normal";const v=y.find(n=>n.id===i);const N=y[(y.findIndex(n=>n.id===i)+1)%y.length];const P=!h||i==="offline"?0:i==="verySlow"?1:i==="slow"?2:4;const M=c?"Applying\u2026":!h?"Unavailable":i==="normal"?"0 ms":i==="slow"?"+500 ms":i==="verySlow"?"+2000 ms":"Blocked";async function T(n,x=false){if(b.current)return;const k=++f.current;b.current=true;w(true);S(void 0);const L=r?.getSnapshot();try{if(r){if(!L?.available)throw new Error("Device unavailable. Open Network again to reconnect these controls.");await L.onChange(n)}else Z(n);if(u.current&&f.current===k){if(x)s()}}catch(C){if(u.current&&f.current===k)S(C instanceof Error?C.message:String(C))}finally{if(f.current===k){b.current=false;if(u.current)w(false)}}}const q=D?t(d,{style:o.menu,children:t(p,{accessibilityRole:"alert",style:o.error,children:D})}):void 0;return t(K,{id:l,revealToken:a,variant:"strip",dragFromSurface:true,accessibilityLabel:`Move or hide network throttling. ${B?"Active":"Off"}`,accessory:q,children:F(d,{style:[o.row,{width:Math.max(220,Math.min(292,$-64))}],children:[t(E,{testID:"throttling-cycle",accessibilityRole:"button",accessibilityLabel:`Network: ${v.label}. Switch to ${N.label}`,accessibilityState:{disabled:c||!h},disabled:c||!h,onPress:()=>void T(N.id),style:o.signalButton,children:c?t(U,{size:"small",color:e.color.accent}):F(d,{testID:`throttling-signal-${i}`,style:o.signal,children:[[7,12,18,24].map((n,x)=>t(d,{style:{width:4,height:n,borderRadius:2,backgroundColor:x<P?i==="offline"?e.color.danger:e.color.accent:e.color.fillTertiary}},n)),i==="offline"&&t(d,{style:o.slash})]})}),t(d,{style:o.profile,children:t(p,{testID:"throttling-profile",accessibilityLabel:`Connection profile: ${v.label}`,accessibilityLiveRegion:"polite",numberOfLines:1,style:o.profileLabel,children:v.label})}),t(p,{testID:"throttling-status",numberOfLines:1,accessibilityLiveRegion:"polite",style:o.status,children:M}),t(d,{style:o.separator}),t(E,{testID:"throttling-close",accessibilityRole:"button",accessibilityLabel:"Close and turn off throttling",disabled:c,accessibilityState:{disabled:c},onPress:()=>{if(g==="normal")s();else void T("normal",true)},style:[o.action,c&&o.disabled],children:t(p,{style:o.actionText,children:"Close"})})]})})}const o=G.create({row:{flexDirection:"row",alignItems:"center",gap:6,paddingVertical:4,minHeight:48},signalButton:{width:36,minHeight:44,alignItems:"center",justifyContent:"center"},signal:{flexDirection:"row",alignItems:"flex-end",gap:3,height:24},slash:{position:"absolute",width:30,height:2,backgroundColor:e.color.danger,top:11,left:-2,transform:[{rotate:"-45deg"}]},separator:{height:20,width:1,backgroundColor:e.color.separator,marginHorizontal:2},status:{color:e.color.textSecondary,fontSize:10,lineHeight:15,flexShrink:1},profile:{flex:1,flexDirection:"row",alignItems:"center",gap:6,minHeight:44},profileLabel:{color:e.color.text,fontSize:13,fontWeight:"500",flexShrink:1},action:{minHeight:36,minWidth:52,borderRadius:e.radius.input,borderWidth:1,borderColor:e.color.accentBorderStrong,alignItems:"center",justifyContent:"center",paddingHorizontal:10},actionText:{color:e.color.accent,fontSize:12,fontWeight:"600"},disabled:{opacity:.4},menu:{marginVertical:8,padding:6,borderRadius:e.radius.card,borderWidth:1,borderColor:e.color.separator,backgroundColor:e.color.surface,shadowColor:"#000",shadowOffset:{width:0,height:6},shadowOpacity:.25,shadowRadius:16,elevation:8},error:{color:e.color.danger,fontSize:12,lineHeight:18,padding:10}});
|
|
@@ -1,218 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// @vitest-environment jsdom
|
|
4
|
-
import { act, createElement } from "react";
|
|
5
|
-
import { createRoot } from "react-dom/client";
|
|
6
|
-
import { afterEach, beforeEach, expect, it, vi } from "vitest";
|
|
7
|
-
import { NetworkFloatingTool, createRemoteConditionsSource } from "./NetworkFloatingTool";
|
|
8
|
-
const state = vi.hoisted(() => ({
|
|
9
|
-
profile: "normal",
|
|
10
|
-
available: true,
|
|
11
|
-
listeners: new Set(),
|
|
12
|
-
set: vi.fn()
|
|
13
|
-
}));
|
|
14
|
-
vi.mock("./store", () => ({
|
|
15
|
-
getNetworkCondition: () => state.profile,
|
|
16
|
-
conditionsCanApply: () => state.available,
|
|
17
|
-
subscribeNetworkConditions: listener => {
|
|
18
|
-
state.listeners.add(listener);
|
|
19
|
-
return () => state.listeners.delete(listener);
|
|
20
|
-
},
|
|
21
|
-
setNetworkCondition: profile => {
|
|
22
|
-
state.set(profile);
|
|
23
|
-
state.profile = profile;
|
|
24
|
-
state.listeners.forEach(listener => listener());
|
|
25
|
-
}
|
|
26
|
-
}));
|
|
27
|
-
vi.mock("@buoy-gg/shared-ui", async () => ({
|
|
28
|
-
night: (await vi.importActual("../../../../shared/src/ui/night/nightTheme")).night,
|
|
29
|
-
ChevronDown: () => null,
|
|
30
|
-
Check: () => null,
|
|
31
|
-
openFloatingTool: vi.fn(),
|
|
32
|
-
FloatingTool: ({
|
|
33
|
-
children,
|
|
34
|
-
accessory
|
|
35
|
-
}) => /*#__PURE__*/createElement("div", null, children, accessory)
|
|
36
|
-
}));
|
|
37
|
-
vi.mock("react-native", () => ({
|
|
38
|
-
StyleSheet: {
|
|
39
|
-
create: value => value
|
|
40
|
-
},
|
|
41
|
-
useWindowDimensions: () => ({
|
|
42
|
-
width: 390,
|
|
43
|
-
height: 844
|
|
44
|
-
}),
|
|
45
|
-
View: ({
|
|
46
|
-
children,
|
|
47
|
-
testID
|
|
48
|
-
}) => /*#__PURE__*/createElement("div", {
|
|
49
|
-
"data-testid": testID
|
|
50
|
-
}, children),
|
|
51
|
-
Text: ({
|
|
52
|
-
children,
|
|
53
|
-
testID
|
|
54
|
-
}) => /*#__PURE__*/createElement("span", {
|
|
55
|
-
"data-testid": testID
|
|
56
|
-
}, children),
|
|
57
|
-
ActivityIndicator: () => null,
|
|
58
|
-
TouchableOpacity: ({
|
|
59
|
-
children,
|
|
60
|
-
onPress,
|
|
61
|
-
disabled,
|
|
62
|
-
testID,
|
|
63
|
-
accessibilityLabel
|
|
64
|
-
}) => /*#__PURE__*/createElement("button", {
|
|
65
|
-
onClick: onPress,
|
|
66
|
-
disabled,
|
|
67
|
-
"data-testid": testID,
|
|
68
|
-
"aria-label": accessibilityLabel
|
|
69
|
-
}, children)
|
|
70
|
-
}));
|
|
71
|
-
let root;
|
|
72
|
-
let container;
|
|
73
|
-
const dismiss = vi.fn();
|
|
74
|
-
beforeEach(() => {
|
|
75
|
-
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
|
|
76
|
-
state.profile = "normal";
|
|
77
|
-
state.available = true;
|
|
78
|
-
state.set.mockClear();
|
|
79
|
-
dismiss.mockClear();
|
|
80
|
-
container = document.createElement("div");
|
|
81
|
-
root = createRoot(container);
|
|
82
|
-
});
|
|
83
|
-
afterEach(() => act(() => root.unmount()));
|
|
84
|
-
const button = id => container.querySelector(`[data-testid="${id}"]`);
|
|
85
|
-
const tap = async id => act(async () => button(id).click());
|
|
86
|
-
const status = () => container.querySelector('[data-testid="throttling-status"]')?.textContent;
|
|
87
|
-
function render(source) {
|
|
88
|
-
act(() => root.render(/*#__PURE__*/createElement(NetworkFloatingTool, {
|
|
89
|
-
id: "network-throttling",
|
|
90
|
-
revealToken: 1,
|
|
91
|
-
dismiss,
|
|
92
|
-
source
|
|
93
|
-
})));
|
|
94
|
-
}
|
|
95
|
-
async function choose(value) {
|
|
96
|
-
const order = ["normal", "slow", "verySlow", "offline"];
|
|
97
|
-
const count = (order.indexOf(value) - order.indexOf(state.profile) + order.length) % order.length;
|
|
98
|
-
for (let i = 0; i < count; i++) await tap("throttling-cycle");
|
|
99
|
-
}
|
|
100
|
-
it("cycles and applies every profile immediately, including wrapping to normal", async () => {
|
|
101
|
-
render();
|
|
102
|
-
for (const [profile, label] of [["slow", "+500 ms"], ["verySlow", "+2000 ms"], ["offline", "Blocked"], ["normal", "0 ms"]]) {
|
|
103
|
-
await tap("throttling-cycle");
|
|
104
|
-
expect(state.profile).toBe(profile);
|
|
105
|
-
expect(container.querySelector(`[data-testid="throttling-signal-${profile}"]`)).not.toBeNull();
|
|
106
|
-
expect(status()).toBe(label);
|
|
107
|
-
}
|
|
108
|
-
expect(state.set).toHaveBeenCalledTimes(4);
|
|
109
|
-
});
|
|
110
|
-
it("keeps the profile label informational when tapped", async () => {
|
|
111
|
-
render();
|
|
112
|
-
await tap("throttling-profile");
|
|
113
|
-
expect(state.set).not.toHaveBeenCalled();
|
|
114
|
-
expect(state.profile).toBe("normal");
|
|
115
|
-
expect(button("throttling-profile").closest("button")).toBeNull();
|
|
116
|
-
});
|
|
117
|
-
it("changes active profiles and stops before dismissal", async () => {
|
|
118
|
-
state.profile = "slow";
|
|
119
|
-
render();
|
|
120
|
-
await choose("offline");
|
|
121
|
-
expect(state.profile).toBe("offline");
|
|
122
|
-
await tap("throttling-close");
|
|
123
|
-
expect(state.profile).toBe("normal");
|
|
124
|
-
expect(dismiss).toHaveBeenCalledOnce();
|
|
125
|
-
});
|
|
126
|
-
it("reflects external changes and disables cycling when account admission is revoked", () => {
|
|
127
|
-
render();
|
|
128
|
-
act(() => {
|
|
129
|
-
state.profile = "offline";
|
|
130
|
-
state.listeners.forEach(listener => listener());
|
|
131
|
-
});
|
|
132
|
-
expect(status()).toBe("Blocked");
|
|
133
|
-
act(() => {
|
|
134
|
-
state.profile = "normal";
|
|
135
|
-
state.available = false;
|
|
136
|
-
state.listeners.forEach(listener => listener());
|
|
137
|
-
});
|
|
138
|
-
expect(status()).toBe("Unavailable");
|
|
139
|
-
expect(button("throttling-cycle").disabled).toBe(true);
|
|
140
|
-
});
|
|
141
|
-
it("keeps remote failures visible and does not claim a profile was applied", async () => {
|
|
142
|
-
const source = createRemoteConditionsSource();
|
|
143
|
-
const onChange = vi.fn().mockRejectedValue(new Error("Connection lost"));
|
|
144
|
-
source.update({
|
|
145
|
-
profile: "normal",
|
|
146
|
-
available: true,
|
|
147
|
-
onChange
|
|
148
|
-
});
|
|
149
|
-
render(source);
|
|
150
|
-
await choose("slow");
|
|
151
|
-
expect(container.textContent).toContain("Connection lost");
|
|
152
|
-
expect(status()).toBe("0 ms");
|
|
153
|
-
expect(state.set).not.toHaveBeenCalled();
|
|
154
|
-
});
|
|
155
|
-
it("prevents duplicate remote operations and follows confirmed snapshots", async () => {
|
|
156
|
-
const source = createRemoteConditionsSource();
|
|
157
|
-
let resolve;
|
|
158
|
-
const onChange = vi.fn(() => new Promise(done => {
|
|
159
|
-
resolve = done;
|
|
160
|
-
}));
|
|
161
|
-
source.update({
|
|
162
|
-
profile: "normal",
|
|
163
|
-
available: true,
|
|
164
|
-
onChange
|
|
165
|
-
});
|
|
166
|
-
render(source);
|
|
167
|
-
await tap("throttling-cycle");
|
|
168
|
-
await tap("throttling-cycle");
|
|
169
|
-
expect(onChange).toHaveBeenCalledOnce();
|
|
170
|
-
expect(status()).toBe("Applying…");
|
|
171
|
-
await act(async () => resolve());
|
|
172
|
-
expect(status()).toBe("0 ms");
|
|
173
|
-
act(() => source.update({
|
|
174
|
-
profile: "slow",
|
|
175
|
-
available: true,
|
|
176
|
-
onChange
|
|
177
|
-
}));
|
|
178
|
-
expect(status()).toBe("+500 ms");
|
|
179
|
-
act(() => source.update(undefined));
|
|
180
|
-
expect(status()).toBe("Unavailable");
|
|
181
|
-
});
|
|
182
|
-
it("ignores a previous device's completion after switching devices", async () => {
|
|
183
|
-
const source = createRemoteConditionsSource();
|
|
184
|
-
let reject;
|
|
185
|
-
source.update({
|
|
186
|
-
targetId: "first",
|
|
187
|
-
profile: "normal",
|
|
188
|
-
available: true,
|
|
189
|
-
onChange: () => new Promise((_resolve, fail) => {
|
|
190
|
-
reject = fail;
|
|
191
|
-
})
|
|
192
|
-
});
|
|
193
|
-
render(source);
|
|
194
|
-
await choose("slow");
|
|
195
|
-
act(() => source.update({
|
|
196
|
-
targetId: "second",
|
|
197
|
-
profile: "normal",
|
|
198
|
-
available: true,
|
|
199
|
-
onChange: vi.fn()
|
|
200
|
-
}));
|
|
201
|
-
await act(async () => reject(new Error("Old device failed")));
|
|
202
|
-
expect(container.textContent).not.toContain("Old device failed");
|
|
203
|
-
expect(status()).toBe("0 ms");
|
|
204
|
-
expect(button("throttling-profile").textContent).toBe("No throttling");
|
|
205
|
-
});
|
|
206
|
-
it("keeps the strip open when stopping a remote device fails", async () => {
|
|
207
|
-
const source = createRemoteConditionsSource();
|
|
208
|
-
source.update({
|
|
209
|
-
profile: "offline",
|
|
210
|
-
available: true,
|
|
211
|
-
onChange: vi.fn().mockRejectedValue(new Error("Could not stop"))
|
|
212
|
-
});
|
|
213
|
-
render(source);
|
|
214
|
-
await tap("throttling-close");
|
|
215
|
-
expect(dismiss).not.toHaveBeenCalled();
|
|
216
|
-
expect(status()).toBe("Blocked");
|
|
217
|
-
expect(container.textContent).toContain("Could not stop");
|
|
218
|
-
});
|
|
1
|
+
"use strict";import{act as l,createElement as u}from"react";import{createRoot as y}from"react-dom/client";import{afterEach as b,beforeEach as B,expect as t,it as c,vi as a}from"vitest";import{NetworkFloatingTool as k,createRemoteConditionsSource as m}from"./NetworkFloatingTool";const n=a.hoisted(()=>({profile:"normal",available:true,listeners:new Set,set:a.fn()}));a.mock("./store",()=>({getNetworkCondition:()=>n.profile,conditionsCanApply:()=>n.available,subscribeNetworkConditions:e=>{n.listeners.add(e);return()=>n.listeners.delete(e)},setNetworkCondition:e=>{n.set(e);n.profile=e;n.listeners.forEach(o=>o())}}));a.mock("@buoy-gg/shared-ui",async()=>({night:(await a.importActual("../../../../shared/src/ui/night/nightTheme")).night,ChevronDown:()=>null,Check:()=>null,openFloatingTool:a.fn(),FloatingTool:({children:e,accessory:o})=>u("div",null,e,o)}));a.mock("react-native",()=>({StyleSheet:{create:e=>e},useWindowDimensions:()=>({width:390,height:844}),View:({children:e,testID:o})=>u("div",{"data-testid":o},e),Text:({children:e,testID:o})=>u("span",{"data-testid":o},e),ActivityIndicator:()=>null,TouchableOpacity:({children:e,onPress:o,disabled:r,testID:s,accessibilityLabel:C})=>u("button",{onClick:o,disabled:r,"data-testid":s,"aria-label":C},e)}));let w;let d;const h=a.fn();B(()=>{globalThis.IS_REACT_ACT_ENVIRONMENT=true;n.profile="normal";n.available=true;n.set.mockClear();h.mockClear();d=document.createElement("div");w=y(d)});b(()=>l(()=>w.unmount()));const g=e=>d.querySelector(`[data-testid="${e}"]`);const p=async e=>l(async()=>g(e).click());const i=()=>d.querySelector('[data-testid="throttling-status"]')?.textContent;function f(e){l(()=>w.render(u(k,{id:"network-throttling",revealToken:1,dismiss:h,source:e})))}async function v(e){const o=["normal","slow","verySlow","offline"];const r=(o.indexOf(e)-o.indexOf(n.profile)+o.length)%o.length;for(let s=0;s<r;s++)await p("throttling-cycle")}c("cycles and applies every profile immediately, including wrapping to normal",async()=>{f();for(const[e,o]of[["slow","+500 ms"],["verySlow","+2000 ms"],["offline","Blocked"],["normal","0 ms"]]){await p("throttling-cycle");t(n.profile).toBe(e);t(d.querySelector(`[data-testid="throttling-signal-${e}"]`)).not.toBeNull();t(i()).toBe(o)}t(n.set).toHaveBeenCalledTimes(4)});c("keeps the profile label informational when tapped",async()=>{f();await p("throttling-profile");t(n.set).not.toHaveBeenCalled();t(n.profile).toBe("normal");t(g("throttling-profile").closest("button")).toBeNull()});c("changes active profiles and stops before dismissal",async()=>{n.profile="slow";f();await v("offline");t(n.profile).toBe("offline");await p("throttling-close");t(n.profile).toBe("normal");t(h).toHaveBeenCalledOnce()});c("reflects external changes and disables cycling when account admission is revoked",()=>{f();l(()=>{n.profile="offline";n.listeners.forEach(e=>e())});t(i()).toBe("Blocked");l(()=>{n.profile="normal";n.available=false;n.listeners.forEach(e=>e())});t(i()).toBe("Unavailable");t(g("throttling-cycle").disabled).toBe(true)});c("keeps remote failures visible and does not claim a profile was applied",async()=>{const e=m();const o=a.fn().mockRejectedValue(new Error("Connection lost"));e.update({profile:"normal",available:true,onChange:o});f(e);await v("slow");t(d.textContent).toContain("Connection lost");t(i()).toBe("0 ms");t(n.set).not.toHaveBeenCalled()});c("prevents duplicate remote operations and follows confirmed snapshots",async()=>{const e=m();let o;const r=a.fn(()=>new Promise(s=>{o=s}));e.update({profile:"normal",available:true,onChange:r});f(e);await p("throttling-cycle");await p("throttling-cycle");t(r).toHaveBeenCalledOnce();t(i()).toBe("Applying\u2026");await l(async()=>o());t(i()).toBe("0 ms");l(()=>e.update({profile:"slow",available:true,onChange:r}));t(i()).toBe("+500 ms");l(()=>e.update(void 0));t(i()).toBe("Unavailable")});c("ignores a previous device's completion after switching devices",async()=>{const e=m();let o;e.update({targetId:"first",profile:"normal",available:true,onChange:()=>new Promise((r,s)=>{o=s})});f(e);await v("slow");l(()=>e.update({targetId:"second",profile:"normal",available:true,onChange:a.fn()}));await l(async()=>o(new Error("Old device failed")));t(d.textContent).not.toContain("Old device failed");t(i()).toBe("0 ms");t(g("throttling-profile").textContent).toBe("No throttling")});c("keeps the strip open when stopping a remote device fails",async()=>{const e=m();e.update({profile:"offline",available:true,onChange:a.fn().mockRejectedValue(new Error("Could not stop"))});f(e);await p("throttling-close");t(h).not.toHaveBeenCalled();t(i()).toBe("Blocked");t(d.textContent).toContain("Could not stop")});
|
|
@@ -1,65 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { hasAccountAccess, subscribeAccountAccess } from "@buoy-gg/shared-ui";
|
|
4
|
-
import { addNetworkListener, startNetworkListener, getNetworkListenerCount, stopNetworkListener } from "../utils/networkListener";
|
|
5
|
-
export const NETWORK_CONDITIONS = {
|
|
6
|
-
normal: {
|
|
7
|
-
label: "Normal",
|
|
8
|
-
delayMs: 0
|
|
9
|
-
},
|
|
10
|
-
offline: {
|
|
11
|
-
label: "Offline (requests)",
|
|
12
|
-
delayMs: 0
|
|
13
|
-
},
|
|
14
|
-
slow: {
|
|
15
|
-
label: "Slow (+500 ms)",
|
|
16
|
-
delayMs: 500
|
|
17
|
-
},
|
|
18
|
-
verySlow: {
|
|
19
|
-
label: "Very slow (+2000 ms)",
|
|
20
|
-
delayMs: 2000
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
export const networkConditionsRuntimeId = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
24
|
-
let profile = "normal";
|
|
25
|
-
let keepAlive;
|
|
26
|
-
const listeners = new Set();
|
|
27
|
-
export const conditionsCanApply = () => typeof __DEV__ !== "undefined" && __DEV__ && hasAccountAccess();
|
|
28
|
-
export const getNetworkCondition = () => profile;
|
|
29
|
-
export const subscribeNetworkConditions = listener => {
|
|
30
|
-
listeners.add(listener);
|
|
31
|
-
return () => {
|
|
32
|
-
listeners.delete(listener);
|
|
33
|
-
};
|
|
34
|
-
};
|
|
35
|
-
export function setNetworkCondition(value) {
|
|
36
|
-
if (typeof value !== "string" || !Object.prototype.hasOwnProperty.call(NETWORK_CONDITIONS, value)) {
|
|
37
|
-
throw new Error("Choose normal, offline, slow, or verySlow.");
|
|
38
|
-
}
|
|
39
|
-
if (value !== "normal" && !conditionsCanApply()) {
|
|
40
|
-
throw new Error("Network conditions require an admitted account and a development build.");
|
|
41
|
-
}
|
|
42
|
-
if (value === profile) return profile;
|
|
43
|
-
if (value !== "normal" && !keepAlive) {
|
|
44
|
-
startNetworkListener();
|
|
45
|
-
keepAlive = addNetworkListener(() => {});
|
|
46
|
-
} else if (value === "normal") {
|
|
47
|
-
keepAlive?.();
|
|
48
|
-
keepAlive = undefined;
|
|
49
|
-
if (getNetworkListenerCount() === 0) stopNetworkListener();
|
|
50
|
-
}
|
|
51
|
-
profile = value;
|
|
52
|
-
listeners.forEach(listener => listener());
|
|
53
|
-
return profile;
|
|
54
|
-
}
|
|
55
|
-
export function snapshotNetworkCondition(url) {
|
|
56
|
-
if (profile === "normal" || !conditionsCanApply() || !/^https?:\/\//i.test(url)) return;
|
|
57
|
-
return Object.freeze({
|
|
58
|
-
profile,
|
|
59
|
-
delayMs: NETWORK_CONDITIONS[profile].delayMs
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
subscribeAccountAccess(() => {
|
|
63
|
-
if (!hasAccountAccess()) setNetworkCondition("normal");
|
|
64
|
-
listeners.forEach(listener => listener());
|
|
65
|
-
});
|
|
1
|
+
"use strict";import{hasAccountAccess as n,subscribeAccountAccess as i}from"@buoy-gg/shared-ui";import{addNetworkListener as l,startNetworkListener as a,getNetworkListenerCount as c,stopNetworkListener as d}from"../utils/networkListener";export const NETWORK_CONDITIONS={normal:{label:"Normal",delayMs:0},offline:{label:"Offline (requests)",delayMs:0},slow:{label:"Slow (+500 ms)",delayMs:500},verySlow:{label:"Very slow (+2000 ms)",delayMs:2e3}};export const networkConditionsRuntimeId=`${Date.now()}-${Math.random().toString(36).slice(2)}`;let o="normal";let t;const r=new Set;export const conditionsCanApply=()=>typeof __DEV__!=="undefined"&&__DEV__&&n();export const getNetworkCondition=()=>o;export const subscribeNetworkConditions=e=>{r.add(e);return()=>{r.delete(e)}};export function setNetworkCondition(e){if(typeof e!=="string"||!Object.prototype.hasOwnProperty.call(NETWORK_CONDITIONS,e)){throw new Error("Choose normal, offline, slow, or verySlow.")}if(e!=="normal"&&!conditionsCanApply()){throw new Error("Network conditions require an admitted account and a development build.")}if(e===o)return o;if(e!=="normal"&&!t){a();t=l(()=>{})}else if(e==="normal"){t?.();t=void 0;if(c()===0)d()}o=e;r.forEach(s=>s());return o}export function snapshotNetworkCondition(e){if(o==="normal"||!conditionsCanApply()||!/^https?:\/\//i.test(e))return;return Object.freeze({profile:o,delayMs:NETWORK_CONDITIONS[o].delayMs})}i(()=>{if(!n())setNetworkCondition("normal");r.forEach(e=>e())});
|
|
@@ -1,38 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
export function throwIfAborted(signal) {
|
|
4
|
-
if (!signal?.aborted) return;
|
|
5
|
-
if (signal.reason !== undefined) throw signal.reason;
|
|
6
|
-
const error = new Error("The request was aborted.");
|
|
7
|
-
error.name = "AbortError";
|
|
8
|
-
throw error;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Wait before dispatch, releasing the timer and listener on cancellation. */
|
|
12
|
-
export function waitBeforeDispatch(ms, signal) {
|
|
13
|
-
return new Promise((resolve, reject) => {
|
|
14
|
-
try {
|
|
15
|
-
throwIfAborted(signal);
|
|
16
|
-
} catch (error) {
|
|
17
|
-
reject(error);
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
const cleanup = () => signal?.removeEventListener("abort", abort);
|
|
21
|
-
const abort = () => {
|
|
22
|
-
clearTimeout(timer);
|
|
23
|
-
cleanup();
|
|
24
|
-
try {
|
|
25
|
-
throwIfAborted(signal);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
reject(error);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
const timer = setTimeout(() => {
|
|
31
|
-
cleanup();
|
|
32
|
-
resolve();
|
|
33
|
-
}, ms);
|
|
34
|
-
signal?.addEventListener("abort", abort, {
|
|
35
|
-
once: true
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
}
|
|
1
|
+
"use strict";export function throwIfAborted(e){if(!e?.aborted)return;if(e.reason!==void 0)throw e.reason;const r=new Error("The request was aborted.");r.name="AbortError";throw r}export function waitBeforeDispatch(e,r){return new Promise((u,o)=>{try{throwIfAborted(r)}catch(t){o(t);return}const n=()=>r?.removeEventListener("abort",c);const c=()=>{clearTimeout(a);n();try{throwIfAborted(r)}catch(t){o(t)}};const a=setTimeout(()=>{n();u()},e);r?.addEventListener("abort",c,{once:true})})}
|