gloomberb 0.12.0 → 0.13.2
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 +2 -0
- package/package.json +1 -2
- package/src/api-client/auth.ts +2 -1
- package/src/api-client/index.test.ts +34 -0
- package/src/api-client/index.ts +10 -2
- package/src/api-client/request.ts +4 -1
- package/src/api-client/research-activity.ts +61 -0
- package/src/api-client/types.ts +2 -1
- package/src/cli/commands/plugins.ts +4 -3
- package/src/cli/desktop-pane-shot.ts +79 -10
- package/src/cli/pane-functions/capabilities.ts +76 -23
- package/src/cli/pane-functions/cloud-session.test.ts +54 -0
- package/src/cli/pane-functions/cloud-session.ts +38 -0
- package/src/cli/pane-functions/dom.test.ts +41 -0
- package/src/cli/pane-functions/dom.ts +199 -0
- package/src/cli/pane-functions/index.test.ts +13 -2
- package/src/cli/pane-functions/index.ts +9 -5
- package/src/cli/pane-functions/intraday-shot.test.ts +307 -0
- package/src/cli/pane-functions/intraday-shot.ts +334 -0
- package/src/cli/pane-functions/report.test.ts +29 -1
- package/src/cli/pane-functions/report.ts +68 -7
- package/src/cli/pane-functions/screenshot.ts +195 -42
- package/src/cli/restore-plugins.ts +10 -3
- package/src/components/chart/composite/composite-chart.test.tsx +68 -1
- package/src/components/chart/composite/composite-chart.tsx +87 -21
- package/src/components/chart/composite/rasterizer.ts +33 -0
- package/src/components/chart/composite/renderers.test.ts +51 -4
- package/src/components/chart/composite/scene.test.ts +128 -2
- package/src/components/chart/composite/scene.ts +92 -16
- package/src/components/chart/composite/text-renderer.ts +13 -0
- package/src/components/chart/composite/time-scale.ts +30 -10
- package/src/components/chart/composite/types.ts +26 -0
- package/src/components/command-bar/assist/runtime.ts +38 -30
- package/src/components/command-bar/surface/assist.test.tsx +63 -9
- package/src/components/command-bar/surface/test-harness.tsx +23 -4
- package/src/components/onboarding/account-step/chooser-panel.tsx +2 -2
- package/src/components/onboarding/account-step/qr-panel.tsx +2 -10
- package/src/components/onboarding/onboarding-wizard.test.tsx +38 -12
- package/src/components/onboarding/onboarding-wizard.tsx +49 -14
- package/src/data/config/store/index.test.ts +23 -0
- package/src/data/config/store/node.ts +7 -1
- package/src/data/config/store/normalize.ts +4 -0
- package/src/news/types.ts +1 -0
- package/src/plugins/builtin/cds/client.test.ts +20 -0
- package/src/plugins/builtin/cds/client.ts +13 -3
- package/src/plugins/builtin/cds/headless.test.ts +30 -0
- package/src/plugins/builtin/cds/headless.ts +113 -0
- package/src/plugins/builtin/cds/index.tsx +4 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -1
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- package/src/plugins/builtin/chat/controller/channels.ts +2 -1
- package/src/plugins/builtin/chat/controller/index.ts +13 -5
- package/src/plugins/builtin/chat/controller/realtime.ts +24 -0
- package/src/plugins/builtin/chat/controller/session-runtime.ts +76 -8
- package/src/plugins/builtin/chat/controller/state.ts +1 -0
- package/src/plugins/builtin/chat/controller.test.ts +260 -4
- package/src/plugins/builtin/cloud/askg/executor.test.ts +160 -0
- package/src/plugins/builtin/cloud/askg/executor.ts +429 -0
- package/src/plugins/builtin/cloud/askg/manifest.test.ts +153 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +314 -0
- package/src/plugins/builtin/cloud/askg/protocol.test.ts +190 -0
- package/src/plugins/builtin/cloud/askg/protocol.ts +286 -0
- package/src/plugins/builtin/cloud/askg/undo.test.ts +44 -0
- package/src/plugins/builtin/cloud/askg/undo.ts +277 -0
- package/src/plugins/builtin/cloud/askg/verify.ts +130 -0
- package/src/plugins/builtin/cloud/auth-dialog.tsx +7 -1
- package/src/plugins/builtin/cloud/device-signin-dialog.tsx +21 -8
- package/src/plugins/builtin/cloud/plugin.tsx +2 -0
- package/src/plugins/builtin/cloud/verification-panel.tsx +56 -0
- package/src/plugins/builtin/congress-trades/client.ts +11 -0
- package/src/plugins/builtin/congress-trades/detail.tsx +3 -3
- package/src/plugins/builtin/congress-trades/headless.test.ts +98 -0
- package/src/plugins/builtin/congress-trades/headless.ts +145 -0
- package/src/plugins/builtin/congress-trades/pane.tsx +3 -3
- package/src/plugins/builtin/credit-conditions/client.ts +1 -1
- package/src/plugins/builtin/credit-conditions/headless.test.ts +31 -0
- package/src/plugins/builtin/credit-conditions/headless.ts +55 -0
- package/src/plugins/builtin/credit-conditions/index.tsx +4 -0
- package/src/plugins/builtin/dividend-yield/headless.test.ts +88 -0
- package/src/plugins/builtin/dividend-yield/headless.ts +135 -0
- package/src/plugins/builtin/dividend-yield/index.tsx +12 -8
- package/src/plugins/builtin/dividend-yield/model.ts +4 -22
- package/src/plugins/builtin/dividend-yield/view.ts +17 -0
- package/src/plugins/builtin/earnings/headless.test.ts +82 -0
- package/src/plugins/builtin/earnings/headless.ts +162 -0
- package/src/plugins/builtin/earnings/index.tsx +2 -0
- package/src/plugins/builtin/earnings-calls/data.ts +29 -8
- package/src/plugins/builtin/earnings-calls/headless.test.ts +192 -0
- package/src/plugins/builtin/earnings-calls/headless.ts +300 -0
- package/src/plugins/builtin/earnings-calls/index.tsx +19 -9
- package/src/plugins/builtin/earnings-calls/model.ts +238 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +4 -0
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +13 -11
- package/src/plugins/builtin/fear-greed/headless.test.ts +53 -0
- package/src/plugins/builtin/fear-greed/headless.ts +84 -0
- package/src/plugins/builtin/fear-greed/index.tsx +4 -0
- package/src/plugins/builtin/holders/client.ts +29 -0
- package/src/plugins/builtin/holders/headless.test.ts +58 -0
- package/src/plugins/builtin/holders/headless.ts +162 -0
- package/src/plugins/builtin/holders/index.tsx +14 -8
- package/src/plugins/builtin/holders/pane.tsx +7 -1
- package/src/plugins/builtin/insider/client.ts +50 -0
- package/src/plugins/builtin/insider/headless.test.ts +122 -0
- package/src/plugins/builtin/insider/headless.ts +117 -0
- package/src/plugins/builtin/insider/index.tsx +21 -50
- package/src/plugins/builtin/insider/insider-data.ts +1 -1
- package/src/plugins/builtin/insider/model.ts +72 -0
- package/src/plugins/builtin/ipo-calendar/client.ts +15 -0
- package/src/plugins/builtin/ipo-calendar/headless.test.ts +89 -0
- package/src/plugins/builtin/ipo-calendar/headless.ts +127 -0
- package/src/plugins/builtin/ipo-calendar/index.tsx +2 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +3 -15
- package/src/plugins/builtin/market-movers/client.ts +76 -0
- package/src/plugins/builtin/market-movers/headless.test.ts +34 -0
- package/src/plugins/builtin/market-movers/headless.ts +73 -0
- package/src/plugins/builtin/market-movers/index.tsx +11 -51
- package/src/plugins/builtin/news/headless.test.ts +138 -0
- package/src/plugins/builtin/news/headless.ts +190 -0
- package/src/plugins/builtin/news/index.tsx +13 -9
- package/src/plugins/builtin/news/wire/index.tsx +2 -1
- package/src/plugins/builtin/research/analyst-headless.test.ts +60 -0
- package/src/plugins/builtin/research/analyst-headless.ts +172 -0
- package/src/plugins/builtin/research/analyst-model.ts +219 -0
- package/src/plugins/builtin/research/analyst-pane.tsx +31 -221
- package/src/plugins/builtin/research/client.ts +12 -0
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -237
- package/src/plugins/builtin/research/event-model.ts +245 -0
- package/src/plugins/builtin/research/events-headless.test.ts +32 -0
- package/src/plugins/builtin/research/events-headless.ts +87 -0
- package/src/plugins/builtin/research/headless.test.ts +93 -0
- package/src/plugins/builtin/research/headless.ts +161 -0
- package/src/plugins/builtin/research/index.tsx +32 -18
- package/src/plugins/builtin/research-search/pane.tsx +4 -0
- package/src/plugins/builtin/scanner/headless.test.ts +59 -0
- package/src/plugins/builtin/scanner/headless.ts +125 -0
- package/src/plugins/builtin/scanner/index.ts +4 -0
- package/src/plugins/builtin/sec/client.ts +16 -0
- package/src/plugins/builtin/sec/headless.test.ts +72 -0
- package/src/plugins/builtin/sec/headless.ts +69 -0
- package/src/plugins/builtin/sec/index.tsx +20 -75
- package/src/plugins/builtin/sec/model.ts +89 -0
- package/src/plugins/builtin/sectors/client.ts +54 -0
- package/src/plugins/builtin/sectors/headless.test.ts +28 -0
- package/src/plugins/builtin/sectors/headless.ts +80 -0
- package/src/plugins/builtin/sectors/index.tsx +6 -51
- package/src/plugins/builtin/shared/cloud-upgrade.ts +8 -4
- package/src/plugins/builtin/short-interest/client.ts +5 -2
- package/src/plugins/builtin/short-interest/headless.test.ts +96 -0
- package/src/plugins/builtin/short-interest/headless.ts +111 -0
- package/src/plugins/builtin/short-interest/index.tsx +15 -9
- package/src/plugins/builtin/thirteenf/data.ts +13 -11
- package/src/plugins/builtin/thirteenf/headless.test.ts +112 -0
- package/src/plugins/builtin/thirteenf/headless.ts +286 -0
- package/src/plugins/builtin/thirteenf/index.tsx +4 -0
- package/src/plugins/builtin/thirteenf/model.ts +13 -0
- package/src/plugins/builtin/thirteenf/table.ts +3 -15
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -0
- package/src/plugins/builtin/treasury-auctions/headless.test.ts +34 -0
- package/src/plugins/builtin/treasury-auctions/headless.ts +95 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +4 -0
- package/src/plugins/builtin/volatility/client.ts +16 -3
- package/src/plugins/builtin/volatility/headless.test.ts +35 -0
- package/src/plugins/builtin/volatility/headless.ts +75 -0
- package/src/plugins/builtin/volatility/index.tsx +4 -0
- package/src/plugins/builtin/world-indices/client.ts +43 -0
- package/src/plugins/builtin/world-indices/headless.test.ts +32 -0
- package/src/plugins/builtin/world-indices/headless.ts +109 -0
- package/src/plugins/builtin/world-indices/index.tsx +4 -0
- package/src/plugins/builtin/yield-curve/headless.test.ts +24 -0
- package/src/plugins/builtin/yield-curve/headless.ts +67 -0
- package/src/plugins/builtin/yield-curve/index.tsx +6 -8
- package/src/plugins/builtin/yield-curve/treasury-data.ts +16 -6
- package/src/plugins/bundle.test.ts +58 -1
- package/src/plugins/bundle.ts +4 -2
- package/src/plugins/catalog-backend.test.ts +26 -0
- package/src/plugins/catalog-backend.ts +9 -2
- package/src/plugins/catalog-browser.test.ts +0 -13
- package/src/plugins/catalog-browser.ts +4 -0
- package/src/plugins/catalog-manifest.test.ts +15 -0
- package/src/plugins/catalog-manifest.ts +12 -5
- package/src/plugins/catalog-ui.ts +0 -6
- package/src/plugins/host-contract.ts +9 -0
- package/src/plugins/host-modules.ts +12 -0
- package/src/plugins/loader.ts +63 -15
- package/src/plugins/prediction-markets/headless.test.ts +119 -0
- package/src/plugins/prediction-markets/headless.ts +255 -0
- package/src/plugins/prediction-markets/index.tsx +2 -0
- package/src/plugins/seed.ts +3 -0
- package/src/public/broker.ts +6 -0
- package/src/remote/schema.test.ts +65 -0
- package/src/remote/schema.ts +322 -39
- package/src/remote/types.ts +49 -2
- package/src/renderers/browser/app-services.ts +3 -0
- package/src/renderers/browser/config-host.ts +24 -8
- package/src/renderers/browser/deeplink-bridge.test.ts +11 -0
- package/src/renderers/browser/deeplink-bridge.ts +15 -3
- package/src/renderers/browser/main.tsx +3 -1
- package/src/renderers/browser/research-entry.test.ts +8 -0
- package/src/renderers/browser/research-entry.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +2 -2
- package/src/renderers/electrobun/bun/desktop/initialization.ts +10 -3
- package/src/renderers/electrobun/bun/external-plugins.ts +20 -4
- package/src/renderers/electrobun/view/build-assets.ts +0 -2
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +63 -10
- package/src/sources/gloomberb-cloud/news.ts +1 -0
- package/src/sync/core-contributors.test.ts +111 -0
- package/src/sync/core-contributors.ts +155 -1
- package/src/time-series/resolve.test.ts +118 -1
- package/src/time-series/resolve.ts +14 -16
- package/src/types/config.ts +2 -0
- package/src/plugins/broker-sync/broker-sync.test.ts +0 -188
- package/src/plugins/broker-sync/native-loader.ts +0 -10
- package/src/plugins/broker-sync/normalize.ts +0 -308
- package/src/plugins/broker-sync/public.ts +0 -122
- package/src/plugins/broker-sync/robinhood-native.ts +0 -367
- package/src/plugins/broker-sync/robinhood.ts +0 -114
- package/src/plugins/broker-sync/simplefin-native.ts +0 -201
- package/src/plugins/broker-sync/simplefin.ts +0 -70
- package/src/renderers/electrobun/view/native-stubs/broker-sync-native-loader.ts +0 -7
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import type { ResolvedPaneFunction } from "./resolver";
|
|
3
|
+
import type { PaneScreenshotResult } from "./screenshot";
|
|
4
|
+
import { buildDomPaneReportFromRender } from "./dom";
|
|
5
|
+
|
|
6
|
+
test("marks rendered reports as truncated when a visible cell contains an ellipsis", () => {
|
|
7
|
+
const resolved = {
|
|
8
|
+
token: "INS",
|
|
9
|
+
label: "Insider",
|
|
10
|
+
capability: { id: "insider-pane" },
|
|
11
|
+
options: {},
|
|
12
|
+
} as unknown as ResolvedPaneFunction;
|
|
13
|
+
const screenshot = {
|
|
14
|
+
symbols: ["AVGO"],
|
|
15
|
+
render: {
|
|
16
|
+
visibleText: "NVIDIA Corporati…",
|
|
17
|
+
rows: [{
|
|
18
|
+
tableIndex: 0,
|
|
19
|
+
rowIndex: 0,
|
|
20
|
+
selected: false,
|
|
21
|
+
cells: [{ columnLabel: "Issuer", text: "NVIDIA Corporati…" }],
|
|
22
|
+
}],
|
|
23
|
+
truncated: false,
|
|
24
|
+
truncationReasons: [],
|
|
25
|
+
loadingStateDetected: false,
|
|
26
|
+
errorStateDetected: false,
|
|
27
|
+
errorStateMarkers: [],
|
|
28
|
+
emptyStateDetected: false,
|
|
29
|
+
emptyStateMarkers: [],
|
|
30
|
+
},
|
|
31
|
+
} as unknown as PaneScreenshotResult;
|
|
32
|
+
|
|
33
|
+
const report = buildDomPaneReportFromRender(resolved, screenshot);
|
|
34
|
+
expect(report.data).toMatchObject({
|
|
35
|
+
kind: "rendered-view",
|
|
36
|
+
rowCount: 1,
|
|
37
|
+
truncated: true,
|
|
38
|
+
complete: false,
|
|
39
|
+
limitation: expect.stringContaining("visible"),
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type { DesktopPaneShotRenderedRow } from "../desktop-pane-shot";
|
|
2
|
+
import { renderSection, renderTable } from "../../utils/cli-output";
|
|
3
|
+
import type { MarketContext } from "../types";
|
|
4
|
+
import type { PaneFunctionReport } from "./report";
|
|
5
|
+
import type { ResolvedPaneFunction } from "./resolver";
|
|
6
|
+
import { collectShotSymbols } from "./data";
|
|
7
|
+
import { renderDesktopShot, type PaneScreenshotResult } from "./screenshot";
|
|
8
|
+
|
|
9
|
+
const DOM_REPORT_WIDTH = 1280;
|
|
10
|
+
const DOM_REPORT_HEIGHT = 720;
|
|
11
|
+
const DOM_LIMITATION =
|
|
12
|
+
"Only values visible in the rendered pane are returned; clipped or off-screen data may be omitted.";
|
|
13
|
+
|
|
14
|
+
function rowsContainEllipsis(rows: DesktopPaneShotRenderedRow[]): boolean {
|
|
15
|
+
return rows.some((row) => row.cells.some((cell) => /\u2026|\.\.\./.test(cell.text)));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isDomReportTruncated(
|
|
19
|
+
render: Pick<PaneScreenshotResult["render"], "rows" | "truncated">,
|
|
20
|
+
): boolean {
|
|
21
|
+
return render.truncated || rowsContainEllipsis(render.rows);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function renderDomTables(rows: DesktopPaneShotRenderedRow[]): string[] {
|
|
25
|
+
const byTable = new Map<number, DesktopPaneShotRenderedRow[]>();
|
|
26
|
+
for (const row of rows) {
|
|
27
|
+
const tableRows = byTable.get(row.tableIndex) ?? [];
|
|
28
|
+
tableRows.push(row);
|
|
29
|
+
byTable.set(row.tableIndex, tableRows);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return [...byTable.entries()].flatMap(([tableIndex, tableRows], index) => {
|
|
33
|
+
const columnCount = Math.max(0, ...tableRows.map((row) => row.cells.length));
|
|
34
|
+
const columns = Array.from({ length: columnCount }, (_, columnIndex) => ({
|
|
35
|
+
header: tableRows.find((row) => row.cells[columnIndex])?.cells[columnIndex]?.columnLabel
|
|
36
|
+
?? `Column ${columnIndex + 1}`,
|
|
37
|
+
}));
|
|
38
|
+
const output = renderTable(
|
|
39
|
+
columns,
|
|
40
|
+
tableRows.map((row) => columns.map((_column, columnIndex) => row.cells[columnIndex]?.text ?? "")),
|
|
41
|
+
);
|
|
42
|
+
return [
|
|
43
|
+
...(index > 0 ? [""] : []),
|
|
44
|
+
...(byTable.size > 1 ? [renderSection(`Rendered table ${tableIndex + 1}`)] : []),
|
|
45
|
+
output,
|
|
46
|
+
];
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function renderedFailureReason(
|
|
51
|
+
result: PaneScreenshotResult,
|
|
52
|
+
rows: DesktopPaneShotRenderedRow[],
|
|
53
|
+
): string | null {
|
|
54
|
+
if (result.render.loadingStateDetected) return "The rendered pane was still loading.";
|
|
55
|
+
if (result.render.errorStateDetected) {
|
|
56
|
+
const marker = result.render.errorStateMarkers[0];
|
|
57
|
+
return marker ? `The rendered pane reported: ${marker}` : "The rendered pane reported an error.";
|
|
58
|
+
}
|
|
59
|
+
if (result.render.emptyStateDetected) {
|
|
60
|
+
const marker = result.render.emptyStateMarkers[0];
|
|
61
|
+
return marker ? `The rendered pane reported: ${marker}` : "The rendered pane reported an empty state.";
|
|
62
|
+
}
|
|
63
|
+
if (rows.length === 0) return "The rendered pane exposed no structured rows or visible text.";
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function reportRows(result: PaneScreenshotResult): DesktopPaneShotRenderedRow[] {
|
|
68
|
+
if (result.render.rows.length > 0) return result.render.rows;
|
|
69
|
+
if (
|
|
70
|
+
result.render.loadingStateDetected
|
|
71
|
+
|| result.render.errorStateDetected
|
|
72
|
+
|| result.render.emptyStateDetected
|
|
73
|
+
|| !result.render.visibleText
|
|
74
|
+
) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
return [{
|
|
78
|
+
tableIndex: 0,
|
|
79
|
+
rowIndex: 0,
|
|
80
|
+
selected: false,
|
|
81
|
+
cells: [{ columnLabel: "Rendered view", text: result.render.visibleText }],
|
|
82
|
+
}];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function buildDomPaneReportFromRender(
|
|
86
|
+
resolved: ResolvedPaneFunction,
|
|
87
|
+
result: PaneScreenshotResult,
|
|
88
|
+
): PaneFunctionReport {
|
|
89
|
+
const rows = reportRows(result);
|
|
90
|
+
const hasStructuredRows = result.render.rows.length > 0;
|
|
91
|
+
const truncated = isDomReportTruncated({ ...result.render, rows });
|
|
92
|
+
const truncationReasons = [...result.render.truncationReasons];
|
|
93
|
+
if (rowsContainEllipsis(rows) && !truncationReasons.includes("one or more cells are visibly clipped")) {
|
|
94
|
+
truncationReasons.push("one or more cells are visibly clipped");
|
|
95
|
+
}
|
|
96
|
+
const failureReason = renderedFailureReason(result, rows);
|
|
97
|
+
const unavailableSymbols = failureReason && result.symbols.length > 0 ? result.symbols : [];
|
|
98
|
+
const textLines = [resolved.label, ""];
|
|
99
|
+
if (hasStructuredRows) {
|
|
100
|
+
textLines.push(...renderDomTables(rows));
|
|
101
|
+
} else if (result.render.visibleText) {
|
|
102
|
+
textLines.push(result.render.visibleText);
|
|
103
|
+
} else {
|
|
104
|
+
textLines.push(failureReason ?? "No rendered values were available.");
|
|
105
|
+
}
|
|
106
|
+
if (failureReason && rows.length > 0) textLines.push("", failureReason);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
data: {
|
|
110
|
+
kind: "rendered-view",
|
|
111
|
+
target: resolved.token,
|
|
112
|
+
capabilityId: resolved.capability.id,
|
|
113
|
+
symbols: result.symbols,
|
|
114
|
+
options: resolved.options,
|
|
115
|
+
rowCount: rows.length,
|
|
116
|
+
empty: rows.length === 0,
|
|
117
|
+
complete: failureReason === null && !truncated,
|
|
118
|
+
unavailableSymbols,
|
|
119
|
+
rows,
|
|
120
|
+
visibleText: result.render.visibleText,
|
|
121
|
+
truncated,
|
|
122
|
+
truncationReasons,
|
|
123
|
+
limitation: DOM_LIMITATION,
|
|
124
|
+
...(failureReason ? { reason: failureReason } : {}),
|
|
125
|
+
},
|
|
126
|
+
text: textLines.join("\n").trimEnd(),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function appendDomReportFooter(
|
|
131
|
+
text: string,
|
|
132
|
+
elapsedMs: number,
|
|
133
|
+
truncated: boolean,
|
|
134
|
+
): string {
|
|
135
|
+
const clipping = truncated ? "The rendered view is clipped." : "The rendered view may be clipped.";
|
|
136
|
+
return [
|
|
137
|
+
text,
|
|
138
|
+
"",
|
|
139
|
+
`Rendered view: values come from the visible pane. ${clipping} Render time: ${elapsedMs} ms.`,
|
|
140
|
+
].join("\n");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function failedDomReport(
|
|
144
|
+
resolved: ResolvedPaneFunction,
|
|
145
|
+
rawArg: string,
|
|
146
|
+
error: unknown,
|
|
147
|
+
): PaneFunctionReport {
|
|
148
|
+
const message = (error instanceof Error ? error.message : String(error))
|
|
149
|
+
.split("\n")[0]!
|
|
150
|
+
.replace(/^Error:\s*/, "")
|
|
151
|
+
.trim();
|
|
152
|
+
const reason = `The rendered view could not be read: ${message || "unknown renderer error"}`;
|
|
153
|
+
const symbols = collectShotSymbols(resolved, rawArg);
|
|
154
|
+
return {
|
|
155
|
+
data: {
|
|
156
|
+
kind: "rendered-view",
|
|
157
|
+
target: resolved.token,
|
|
158
|
+
capabilityId: resolved.capability.id,
|
|
159
|
+
symbols,
|
|
160
|
+
options: resolved.options,
|
|
161
|
+
rowCount: 0,
|
|
162
|
+
empty: true,
|
|
163
|
+
complete: false,
|
|
164
|
+
unavailableSymbols: symbols,
|
|
165
|
+
rows: [],
|
|
166
|
+
visibleText: "",
|
|
167
|
+
truncated: false,
|
|
168
|
+
truncationReasons: [],
|
|
169
|
+
limitation: DOM_LIMITATION,
|
|
170
|
+
reason,
|
|
171
|
+
},
|
|
172
|
+
text: [resolved.label, "", reason].join("\n"),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export async function buildDomFunctionReport(
|
|
177
|
+
resolved: ResolvedPaneFunction,
|
|
178
|
+
context: MarketContext,
|
|
179
|
+
rawArg: string,
|
|
180
|
+
): Promise<PaneFunctionReport> {
|
|
181
|
+
try {
|
|
182
|
+
const result = await renderDesktopShot({
|
|
183
|
+
resolved,
|
|
184
|
+
context,
|
|
185
|
+
rawArg,
|
|
186
|
+
outputPath: "",
|
|
187
|
+
width: DOM_REPORT_WIDTH,
|
|
188
|
+
height: DOM_REPORT_HEIGHT,
|
|
189
|
+
theme: null,
|
|
190
|
+
scale: 1,
|
|
191
|
+
watermark: null,
|
|
192
|
+
options: {},
|
|
193
|
+
captureImage: false,
|
|
194
|
+
});
|
|
195
|
+
return buildDomPaneReportFromRender(resolved, result);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
return failedDomReport(resolved, rawArg, error);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
filterPaneCatalogEntries,
|
|
11
11
|
renderPaneCatalogReport,
|
|
12
12
|
getPaneFunctionCapability,
|
|
13
|
+
isDataPaneForDomFallback,
|
|
13
14
|
normalizeCapabilityOptions,
|
|
14
15
|
capabilityPluginState,
|
|
15
16
|
} = paneFunctionTestInternals;
|
|
@@ -208,13 +209,23 @@ describe("pane function CLI args", () => {
|
|
|
208
209
|
});
|
|
209
210
|
});
|
|
210
211
|
|
|
211
|
-
test("
|
|
212
|
+
test("maps data panes to rendered reports and keeps interactive panes unsupported", () => {
|
|
212
213
|
expect(capabilityFor("new-api-pane")).toMatchObject({
|
|
213
|
-
id: "
|
|
214
|
+
id: "new-api-pane",
|
|
214
215
|
botSafe: false,
|
|
216
|
+
outputKind: "rendered-view",
|
|
217
|
+
reportReadiness: "live-dom",
|
|
218
|
+
screenshotReadiness: "live-dom",
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const helpPane = { ...dummyPane, id: "help" };
|
|
222
|
+
expect(getPaneFunctionCapability(undefined, helpPane)).toMatchObject({
|
|
223
|
+
id: "help",
|
|
215
224
|
reportReadiness: "unsupported",
|
|
216
225
|
screenshotReadiness: "live-dom",
|
|
217
226
|
});
|
|
227
|
+
expect(isDataPaneForDomFallback(dummyPane)).toBe(true);
|
|
228
|
+
expect(isDataPaneForDomFallback(helpPane)).toBe(false);
|
|
218
229
|
});
|
|
219
230
|
|
|
220
231
|
test("rejects financial statement options on a price comparison", () => {
|
|
@@ -25,8 +25,10 @@ import {
|
|
|
25
25
|
import {
|
|
26
26
|
capabilityPluginState,
|
|
27
27
|
getPaneFunctionCapability,
|
|
28
|
+
isDataPaneForDomFallback,
|
|
28
29
|
normalizeCapabilityOptions,
|
|
29
30
|
} from "./capabilities";
|
|
31
|
+
import { withPersistedCloudSession } from "./cloud-session";
|
|
30
32
|
|
|
31
33
|
async function withPaneRuntime<T>(
|
|
32
34
|
ctx: CliCommandContext,
|
|
@@ -62,7 +64,10 @@ export async function runPaneFunction(args: string[], ctx: CliCommandContext) {
|
|
|
62
64
|
+ `Use "gloomberb catalog ${resolved.token}" to inspect readiness.`,
|
|
63
65
|
);
|
|
64
66
|
}
|
|
65
|
-
const report = await
|
|
67
|
+
const report = await withPersistedCloudSession(
|
|
68
|
+
context,
|
|
69
|
+
() => buildFunctionReport(resolved, context, parsed.arg),
|
|
70
|
+
);
|
|
66
71
|
if (parsed.requireBotSafe && (report.data.empty || !report.data.complete)) {
|
|
67
72
|
const unavailable = report.data.unavailableSymbols.length > 0
|
|
68
73
|
? ` Missing data for ${report.data.unavailableSymbols.join(", ")}.`
|
|
@@ -106,16 +111,14 @@ export async function runPaneScreenshot(args: string[], ctx: CliCommandContext)
|
|
|
106
111
|
});
|
|
107
112
|
if (parsed.requireBotSafe && !result.usable) {
|
|
108
113
|
throw new Error(
|
|
109
|
-
`${resolved.token} did not produce a usable bot-safe screenshot:
|
|
110
|
-
+ `${result.empty ? "empty render" : ""}${result.empty && !result.complete ? ", " : ""}`
|
|
111
|
-
+ `${!result.complete ? `missing data for ${result.unavailableSymbols.join(", ")}` : ""}`
|
|
112
|
-
+ `${result.semanticMismatch ? `${(result.empty || !result.complete) ? ", " : ""}rendered content did not match the requested capability` : ""}`,
|
|
114
|
+
`${resolved.token} did not produce a usable bot-safe screenshot: ${result.unusableReason ?? "unknown reason"}`,
|
|
113
115
|
);
|
|
114
116
|
}
|
|
115
117
|
ctx.printResult({ data: result }, {
|
|
116
118
|
text: (data) => [
|
|
117
119
|
`Saved screenshot to ${data.outputPath}`,
|
|
118
120
|
`Result: ${data.rowCount} semantic rows; empty=${data.empty}; complete=${data.complete}; mismatch=${data.semanticMismatch}; usable=${data.usable}`,
|
|
121
|
+
...(data.unusableReason ? [`Unusable reason: ${data.unusableReason}`] : []),
|
|
119
122
|
...(data.unavailableSymbols.length > 0
|
|
120
123
|
? [`Unavailable symbols: ${data.unavailableSymbols.join(", ")}`]
|
|
121
124
|
: []),
|
|
@@ -180,6 +183,7 @@ export const paneFunctionTestInternals = {
|
|
|
180
183
|
filterPaneCatalogEntries,
|
|
181
184
|
renderPaneCatalogReport,
|
|
182
185
|
getPaneFunctionCapability,
|
|
186
|
+
isDataPaneForDomFallback,
|
|
183
187
|
normalizeCapabilityOptions,
|
|
184
188
|
capabilityPluginState,
|
|
185
189
|
};
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { CHART_COMPOSER_PANE_ID, createDefaultConfig } from "../../types/config";
|
|
3
|
+
import type { DataProvider } from "../../types/data-provider";
|
|
4
|
+
import type { PricePoint, TickerFinancials } from "../../types/financials";
|
|
5
|
+
import {
|
|
6
|
+
buildIntradayPriceChartPreset,
|
|
7
|
+
buildPriceChartPreset,
|
|
8
|
+
} from "../../plugins/builtin/chart-composer/presets";
|
|
9
|
+
import type { MarketContext } from "../types";
|
|
10
|
+
import {
|
|
11
|
+
buildDesktopShotPayload,
|
|
12
|
+
intradayChartEvidenceMismatchesFor,
|
|
13
|
+
shotUnusableReasonFor,
|
|
14
|
+
} from "./screenshot";
|
|
15
|
+
import type { ResolvedPaneFunction } from "./resolver";
|
|
16
|
+
import {
|
|
17
|
+
resolveShotIntradaySessionWindow,
|
|
18
|
+
resolveShotIntradayRequest,
|
|
19
|
+
} from "./intraday-shot";
|
|
20
|
+
|
|
21
|
+
function sessionBars(date: string, base: number): PricePoint[] {
|
|
22
|
+
return [
|
|
23
|
+
{
|
|
24
|
+
date: new Date(`${date}T13:30:00.000Z`),
|
|
25
|
+
open: base,
|
|
26
|
+
high: base + 1,
|
|
27
|
+
low: base - 1,
|
|
28
|
+
close: base + 0.25,
|
|
29
|
+
volume: 100,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
date: new Date(`${date}T13:35:00.000Z`),
|
|
33
|
+
open: base + 0.25,
|
|
34
|
+
high: base + 1.25,
|
|
35
|
+
low: base,
|
|
36
|
+
close: base + 0.5,
|
|
37
|
+
volume: 200,
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function financials(priceHistory: PricePoint[] = []): TickerFinancials {
|
|
43
|
+
return {
|
|
44
|
+
quote: {
|
|
45
|
+
symbol: "AAPL",
|
|
46
|
+
price: 108,
|
|
47
|
+
currency: "USD",
|
|
48
|
+
change: 1,
|
|
49
|
+
changePercent: 1,
|
|
50
|
+
lastUpdated: Date.parse("2026-09-03T20:00:00.000Z"),
|
|
51
|
+
listingExchangeName: "NASDAQ",
|
|
52
|
+
},
|
|
53
|
+
annualStatements: [],
|
|
54
|
+
quarterlyStatements: [],
|
|
55
|
+
priceHistory,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function contextWithProvider(provider: DataProvider): MarketContext {
|
|
60
|
+
return {
|
|
61
|
+
config: createDefaultConfig("/tmp/gloomberb-shot-test"),
|
|
62
|
+
dataDir: "/tmp/gloomberb-shot-test",
|
|
63
|
+
persistence: {
|
|
64
|
+
pluginState: { get: () => null },
|
|
65
|
+
},
|
|
66
|
+
store: {
|
|
67
|
+
loadTicker: async () => ({
|
|
68
|
+
metadata: {
|
|
69
|
+
ticker: "AAPL",
|
|
70
|
+
exchange: "NASDAQ",
|
|
71
|
+
currency: "USD",
|
|
72
|
+
name: "Apple",
|
|
73
|
+
portfolios: [],
|
|
74
|
+
watchlists: [],
|
|
75
|
+
positions: [],
|
|
76
|
+
custom: {},
|
|
77
|
+
tags: [],
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
},
|
|
81
|
+
dataProvider: provider,
|
|
82
|
+
} as unknown as MarketContext;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function resolvedChart(
|
|
86
|
+
capabilityId: "intraday-price-chart" | "price-chart",
|
|
87
|
+
spec: ReturnType<typeof buildIntradayPriceChartPreset>,
|
|
88
|
+
options: Record<string, string>,
|
|
89
|
+
): ResolvedPaneFunction {
|
|
90
|
+
return {
|
|
91
|
+
pane: { id: CHART_COMPOSER_PANE_ID },
|
|
92
|
+
capability: { id: capabilityId, options: [] },
|
|
93
|
+
options,
|
|
94
|
+
instance: {
|
|
95
|
+
instanceId: `${capabilityId}:test`,
|
|
96
|
+
paneId: CHART_COMPOSER_PANE_ID,
|
|
97
|
+
title: "AAPL",
|
|
98
|
+
binding: { kind: "fixed", symbol: "AAPL" },
|
|
99
|
+
settings: { chartSpec: spec },
|
|
100
|
+
},
|
|
101
|
+
createOptions: { symbol: "AAPL" },
|
|
102
|
+
} as unknown as ResolvedPaneFunction;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function payloadFor(
|
|
106
|
+
resolved: ResolvedPaneFunction,
|
|
107
|
+
provider: DataProvider,
|
|
108
|
+
) {
|
|
109
|
+
return buildDesktopShotPayload(
|
|
110
|
+
resolved,
|
|
111
|
+
contextWithProvider(provider),
|
|
112
|
+
"AAPL",
|
|
113
|
+
{},
|
|
114
|
+
1200,
|
|
115
|
+
675,
|
|
116
|
+
"tokyo",
|
|
117
|
+
1.5,
|
|
118
|
+
null,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
describe("GIP session windows", () => {
|
|
123
|
+
const history = [
|
|
124
|
+
...sessionBars("2026-08-27", 100),
|
|
125
|
+
...sessionBars("2026-08-28", 101),
|
|
126
|
+
...sessionBars("2026-08-31", 102),
|
|
127
|
+
...sessionBars("2026-09-01", 103),
|
|
128
|
+
...sessionBars("2026-09-02", 104),
|
|
129
|
+
...sessionBars("2026-09-03", 105),
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
test("resolves the latest session, latest five sessions, and an explicit session", () => {
|
|
133
|
+
const latest = resolveShotIntradaySessionWindow(history, {
|
|
134
|
+
rangePreset: "1D",
|
|
135
|
+
timeZone: "America/New_York",
|
|
136
|
+
});
|
|
137
|
+
expect(latest.sessionDates).toEqual(["2026-09-03"]);
|
|
138
|
+
expect(latest.points).toHaveLength(2);
|
|
139
|
+
|
|
140
|
+
const week = resolveShotIntradaySessionWindow(history, {
|
|
141
|
+
rangePreset: "1W",
|
|
142
|
+
timeZone: "America/New_York",
|
|
143
|
+
});
|
|
144
|
+
expect(week.sessionDates).toEqual([
|
|
145
|
+
"2026-08-28",
|
|
146
|
+
"2026-08-31",
|
|
147
|
+
"2026-09-01",
|
|
148
|
+
"2026-09-02",
|
|
149
|
+
"2026-09-03",
|
|
150
|
+
]);
|
|
151
|
+
expect(week.points).toHaveLength(10);
|
|
152
|
+
|
|
153
|
+
const explicit = resolveShotIntradaySessionWindow(history, {
|
|
154
|
+
rangePreset: "1W",
|
|
155
|
+
session: "2026-09-01",
|
|
156
|
+
timeZone: "America/New_York",
|
|
157
|
+
});
|
|
158
|
+
expect(explicit.sessionDates).toEqual(["2026-09-01"]);
|
|
159
|
+
expect(explicit.points.map((point) => point.close)).toEqual([103.25, 103.5]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("maps Auto to the preset interval and validates explicit dates", () => {
|
|
163
|
+
expect(resolveShotIntradayRequest({ rangePreset: "1D", chartResolution: "auto" }))
|
|
164
|
+
.toEqual({ rangePreset: "1D", resolution: "1m", session: null });
|
|
165
|
+
expect(resolveShotIntradayRequest({ rangePreset: "1W", chartResolution: "auto" }))
|
|
166
|
+
.toEqual({ rangePreset: "1W", resolution: "5m", session: null });
|
|
167
|
+
expect(resolveShotIntradayRequest({
|
|
168
|
+
rangePreset: "1W",
|
|
169
|
+
chartResolution: "15m",
|
|
170
|
+
session: "2026-09-02",
|
|
171
|
+
})).toEqual({ rangePreset: "1D", resolution: "15m", session: "2026-09-02" });
|
|
172
|
+
expect(() => resolveShotIntradayRequest({ session: "2026-02-30" }))
|
|
173
|
+
.toThrow("real calendar date");
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe("GIP screenshot payload", () => {
|
|
178
|
+
test("injects resolution-aware intraday bars for GIP", async () => {
|
|
179
|
+
const calls: Array<{ range: string; resolution: string }> = [];
|
|
180
|
+
const history = [
|
|
181
|
+
...sessionBars("2026-09-02", 100),
|
|
182
|
+
...sessionBars("2026-09-03", 105),
|
|
183
|
+
];
|
|
184
|
+
const provider = {
|
|
185
|
+
id: "test",
|
|
186
|
+
name: "Test",
|
|
187
|
+
getTickerFinancials: async () => financials([
|
|
188
|
+
{ date: new Date("2026-09-03T00:00:00.000Z"), close: 105 },
|
|
189
|
+
]),
|
|
190
|
+
getPriceHistory: async () => [],
|
|
191
|
+
getPriceHistoryForResolution: async (_symbol, _exchange, range, resolution) => {
|
|
192
|
+
calls.push({ range, resolution });
|
|
193
|
+
return history;
|
|
194
|
+
},
|
|
195
|
+
getDetailedPriceHistory: async () => [],
|
|
196
|
+
} as DataProvider;
|
|
197
|
+
const spec = buildIntradayPriceChartPreset("AAPL");
|
|
198
|
+
spec.viewport.resolution = "auto";
|
|
199
|
+
const payload = await payloadFor(
|
|
200
|
+
resolvedChart("intraday-price-chart", spec, {
|
|
201
|
+
rangePreset: "1D",
|
|
202
|
+
chartResolution: "auto",
|
|
203
|
+
}),
|
|
204
|
+
provider,
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
expect(calls).toEqual([{ range: "1W", resolution: "1m" }]);
|
|
208
|
+
expect(payload.intradayHistories).toHaveLength(1);
|
|
209
|
+
expect(payload.intradayHistories[0]).toMatchObject({
|
|
210
|
+
symbol: "AAPL",
|
|
211
|
+
exchange: "NASDAQ",
|
|
212
|
+
rangePreset: "1D",
|
|
213
|
+
resolution: "1m",
|
|
214
|
+
requestedSession: null,
|
|
215
|
+
sessionDates: ["2026-09-03"],
|
|
216
|
+
unavailableReason: null,
|
|
217
|
+
});
|
|
218
|
+
expect(payload.intradayHistories[0]?.points).toHaveLength(2);
|
|
219
|
+
expect(payload.financials[0]?.[1].priceHistory).toHaveLength(2);
|
|
220
|
+
|
|
221
|
+
const renderedEvidence = [{
|
|
222
|
+
role: "chart-data",
|
|
223
|
+
metadata: {
|
|
224
|
+
kind: "chart-composer",
|
|
225
|
+
projectedPointCount: 2,
|
|
226
|
+
baseSeries: [{
|
|
227
|
+
pointCount: 2,
|
|
228
|
+
first: { date: "2026-09-03T13:30:00.000Z", value: 105.25 },
|
|
229
|
+
last: { date: "2026-09-03T13:35:00.000Z", value: 105.5 },
|
|
230
|
+
}],
|
|
231
|
+
},
|
|
232
|
+
}];
|
|
233
|
+
const resolved = resolvedChart("intraday-price-chart", spec, {
|
|
234
|
+
rangePreset: "1D",
|
|
235
|
+
chartResolution: "auto",
|
|
236
|
+
});
|
|
237
|
+
expect(intradayChartEvidenceMismatchesFor(resolved, payload, renderedEvidence as never))
|
|
238
|
+
.toEqual([]);
|
|
239
|
+
renderedEvidence[0]!.metadata.baseSeries[0]!.pointCount = 1;
|
|
240
|
+
expect(intradayChartEvidenceMismatchesFor(resolved, payload, renderedEvidence as never))
|
|
241
|
+
.toContain("rendered intraday point count does not match");
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("keeps GP on daily history without an intraday payload", async () => {
|
|
245
|
+
let intradayCalls = 0;
|
|
246
|
+
const daily = [
|
|
247
|
+
{ date: new Date("2026-09-02T00:00:00.000Z"), close: 100 },
|
|
248
|
+
{ date: new Date("2026-09-03T00:00:00.000Z"), close: 105 },
|
|
249
|
+
];
|
|
250
|
+
const provider = {
|
|
251
|
+
id: "test",
|
|
252
|
+
name: "Test",
|
|
253
|
+
getTickerFinancials: async () => financials(),
|
|
254
|
+
getPriceHistory: async () => daily,
|
|
255
|
+
getPriceHistoryForResolution: async () => {
|
|
256
|
+
intradayCalls += 1;
|
|
257
|
+
return [];
|
|
258
|
+
},
|
|
259
|
+
} as DataProvider;
|
|
260
|
+
const payload = await payloadFor(
|
|
261
|
+
resolvedChart("price-chart", buildPriceChartPreset("AAPL"), { rangePreset: "3M" }),
|
|
262
|
+
provider,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
expect(intradayCalls).toBe(0);
|
|
266
|
+
expect(payload.intradayHistories).toEqual([]);
|
|
267
|
+
expect(payload.financials[0]?.[1].priceHistory).toEqual(daily);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("records an explicit unusable reason when no intraday bars exist", async () => {
|
|
271
|
+
const provider = {
|
|
272
|
+
id: "test",
|
|
273
|
+
name: "Test",
|
|
274
|
+
getTickerFinancials: async () => financials([
|
|
275
|
+
{ date: new Date("2026-09-03T00:00:00.000Z"), close: 105 },
|
|
276
|
+
]),
|
|
277
|
+
getPriceHistory: async () => [],
|
|
278
|
+
getPriceHistoryForResolution: async () => [],
|
|
279
|
+
getDetailedPriceHistory: async () => [],
|
|
280
|
+
} as DataProvider;
|
|
281
|
+
const spec = buildIntradayPriceChartPreset("AAPL");
|
|
282
|
+
spec.viewport.resolution = "auto";
|
|
283
|
+
const resolved = resolvedChart("intraday-price-chart", spec, {
|
|
284
|
+
rangePreset: "1D",
|
|
285
|
+
chartResolution: "auto",
|
|
286
|
+
session: "2020-01-02",
|
|
287
|
+
});
|
|
288
|
+
const payload = await payloadFor(resolved, provider);
|
|
289
|
+
|
|
290
|
+
expect(payload.intradayHistories[0]?.points).toEqual([]);
|
|
291
|
+
expect(payload.intradayHistories[0]?.start).toBe("2020-01-02T05:00:00.000Z");
|
|
292
|
+
expect(payload.intradayHistories[0]?.end).toBe("2020-01-03T05:00:00.000Z");
|
|
293
|
+
expect(payload.intradayHistories[0]?.unavailableReason)
|
|
294
|
+
.toBe("No intraday price history is available for AAPL for session 2020-01-02.");
|
|
295
|
+
expect(shotUnusableReasonFor(
|
|
296
|
+
resolved,
|
|
297
|
+
payload,
|
|
298
|
+
{
|
|
299
|
+
loadingStateDetected: false,
|
|
300
|
+
errorStateDetected: false,
|
|
301
|
+
emptyStateDetected: true,
|
|
302
|
+
},
|
|
303
|
+
["AAPL"],
|
|
304
|
+
false,
|
|
305
|
+
)).toBe("No intraday price history is available for AAPL for session 2020-01-02.");
|
|
306
|
+
});
|
|
307
|
+
});
|