mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-01 23:12:21 +00:00
* merge branch develop with main * bump desktop version * fix the merge * sync Cargo.lock to desktop 2.0.0 * fix cli conftest.py --------- Co-authored-by: deeleeramone <>
632 lines
23 KiB
Python
632 lines
23 KiB
Python
"""Test the packaged FRED dashboard, its layout, its groups and its widget schemas."""
|
|
|
|
import datetime
|
|
|
|
import pytest
|
|
|
|
from openbb_fred import ECONOMY_INSTALLED
|
|
|
|
economy_owned = pytest.mark.skipif(
|
|
ECONOMY_INSTALLED,
|
|
reason="openbb_economy owns the economy and survey commands when it is importable,"
|
|
" so the widgets the template places are not published by this package",
|
|
)
|
|
|
|
|
|
class TestInterestRates:
|
|
"""The rates tab, from the curve down to what it costs to borrow."""
|
|
|
|
CURVE = "fixedincome_government_yield_curve_fred_obb"
|
|
SLOPE = "fixedincome_spreads_tcm_fred_obb"
|
|
TIPS = "fixedincome_government_tips_yields_fred_obb"
|
|
IORB = "fixedincome_rate_iorb_fred_obb"
|
|
DPCREDIT = "fixedincome_rate_dpcredit_fred_obb"
|
|
ECB = "fixedincome_rate_ecb_fred_obb"
|
|
MORTGAGES = "fixedincome_mortgage_indices_fred_obb"
|
|
HQM = "fixedincome_corporate_hqm_fred_obb"
|
|
PAPER = "fixedincome_corporate_commercial_paper_fred_obb"
|
|
SPOT = "fixedincome_corporate_spot_rates_fred_obb"
|
|
BONDS = "fixedincome_bond_indices_fred_obb"
|
|
|
|
ORDER = (
|
|
CURVE,
|
|
SLOPE,
|
|
TIPS,
|
|
"fixedincome_rate_effr_fred_obb",
|
|
"fixedincome_rate_sofr_fred_obb",
|
|
IORB,
|
|
DPCREDIT,
|
|
ECB,
|
|
MORTGAGES,
|
|
PAPER,
|
|
HQM,
|
|
SPOT,
|
|
BONDS,
|
|
)
|
|
|
|
@pytest.fixture(scope="class")
|
|
@classmethod
|
|
def served(cls):
|
|
"""Read the template as the API serves it."""
|
|
from openbb_core.provider.utils.helpers import run_async
|
|
|
|
from openbb_fred.fred_router import fred_apps
|
|
|
|
return run_async(fred_apps)[0]
|
|
|
|
@pytest.fixture(scope="class")
|
|
@classmethod
|
|
def registry(cls):
|
|
"""Build the widget registry the template refers to."""
|
|
from openbb_core.api.rest_api import app
|
|
from openbb_platform_api.utils.widgets import build_json
|
|
|
|
return build_json(app.openapi(), [])
|
|
|
|
@pytest.fixture(scope="class")
|
|
@classmethod
|
|
def cells(cls, served):
|
|
"""Index the tab by the id each widget is declared under."""
|
|
from openbb_fred.fred_router import widget_id
|
|
|
|
laid_out = {c["i"]: c for c in served["tabs"]["InterestRates"]["layout"]}
|
|
|
|
return {declared: laid_out[widget_id(declared)] for declared in cls.ORDER}
|
|
|
|
@staticmethod
|
|
def _charted(registry, cell):
|
|
"""Return the columns a charted widget plots, keyed by chart role."""
|
|
table = (registry[cell["i"]].get("data") or {}).get("table") or {}
|
|
roles: dict = {}
|
|
|
|
for column in table.get("columnsDefs") or []:
|
|
roles.setdefault(column.get("chartDataType"), []).append(column["field"])
|
|
|
|
return roles
|
|
|
|
def test_the_tab_reads_from_the_curve_to_the_cost_of_borrowing(self, served):
|
|
"""Term structure, then money market, then policy, then credit."""
|
|
from openbb_fred.fred_router import widget_id
|
|
|
|
placed = [c["i"] for c in served["tabs"]["InterestRates"]["layout"]]
|
|
|
|
assert placed == [widget_id(declared) for declared in self.ORDER]
|
|
|
|
def test_the_curve_is_answered_by_its_own_slope(self, cells):
|
|
"""The snapshot alone cannot say whether the curve is inverting."""
|
|
curve, slope = cells[self.CURVE], cells[self.SLOPE]
|
|
|
|
assert curve["y"] == slope["y"] == 0
|
|
assert curve["x"] + curve["w"] == slope["x"]
|
|
assert slope["state"]["params"]["maturity"] == "3m"
|
|
|
|
def test_every_time_series_opens_as_a_line(self, cells):
|
|
"""A rate is read as a line over time, not as a page of numbers."""
|
|
for declared, cell in cells.items():
|
|
if declared == self.TIPS:
|
|
continue
|
|
|
|
assert cell["state"]["chartView"]["chartType"] == "line", declared
|
|
|
|
def test_a_security_level_yield_stays_a_table(self, cells):
|
|
"""One line per outstanding issue is a blob, not a chart."""
|
|
assert cells[self.TIPS]["state"]["chartView"]["enabled"] is False
|
|
|
|
def test_a_point_in_time_curve_is_charted_by_maturity(self, cells, registry):
|
|
"""Every HQM row shares one date, so a date axis draws one stack of dots."""
|
|
cell = cells[self.HQM]
|
|
|
|
if cell["i"] not in registry:
|
|
pytest.skip("the namespace owner serves these widgets in this installation")
|
|
|
|
roles = self._charted(registry, cell)
|
|
|
|
assert roles.get("time", []) == []
|
|
assert roles["category"] == ["maturity"]
|
|
assert roles["series"] == ["rate"]
|
|
|
|
def test_the_yield_curve_is_charted_by_maturity(self, cells, registry):
|
|
"""Its rows share a date, so maturity carries the axis and date is dropped."""
|
|
cell = cells[self.CURVE]
|
|
|
|
if cell["i"] not in registry:
|
|
pytest.skip("the namespace owner serves these widgets in this installation")
|
|
|
|
roles = self._charted(registry, cell)
|
|
|
|
assert roles["category"] == ["maturity"]
|
|
assert roles["series"] == ["rate"]
|
|
assert "date" in roles["excluded"]
|
|
assert "time" not in roles
|
|
|
|
def test_the_yield_curve_is_left_on_its_latest_date(self, cells):
|
|
"""Two dates would stack two curves onto one set of maturity ticks."""
|
|
assert "date" not in cells[self.CURVE]["state"]["params"]
|
|
|
|
def test_no_volume_or_index_level_shares_an_axis_with_a_rate(self, cells, registry):
|
|
"""A notional in billions flattens every percent it is plotted beside."""
|
|
for declared in (
|
|
"fixedincome_rate_effr_fred_obb",
|
|
"fixedincome_rate_sofr_fred_obb",
|
|
):
|
|
cell = cells[declared]
|
|
|
|
if cell["i"] not in registry:
|
|
pytest.skip(
|
|
"the namespace owner serves these widgets in this installation"
|
|
)
|
|
|
|
roles = self._charted(registry, cell)
|
|
|
|
assert "volume" in roles["excluded"], declared
|
|
assert "volume" not in roles["series"], declared
|
|
assert not {"volume", "index"} & set(roles["series"]), declared
|
|
|
|
def test_the_tips_default_narrows_to_a_single_tenor(self, cells):
|
|
"""All tenors at once draws every outstanding security as one blob."""
|
|
assert cells[self.TIPS]["state"]["params"]["maturity"] == "10"
|
|
|
|
def test_a_mortgage_index_asks_for_the_whole_primary_group(self):
|
|
"""Wide output carries each index in its own column, so all seven chart."""
|
|
from openbb_fred.models.mortgage_indices import MORTGAGE_CHOICES_TO_ID
|
|
|
|
assert len(MORTGAGE_CHOICES_TO_ID["primary"].split(",")) == 7
|
|
|
|
def test_a_bond_index_asks_for_the_whole_curve(self, cells):
|
|
"""Wide output carries each maturity band in its own column."""
|
|
from openbb_fred.models.bond_indices import BAML_CATEGORIES
|
|
|
|
params = cells[self.BONDS]["state"]["params"]
|
|
buckets = BAML_CATEGORIES[params["category"]][params["index"]]
|
|
|
|
assert params["index"] == "yield_curve"
|
|
assert len(buckets) == 6
|
|
|
|
def test_a_commercial_paper_default_spans_the_short_curve(self, cells):
|
|
"""Wide output carries each maturity in its own column."""
|
|
params = cells[self.PAPER]["state"]["params"]
|
|
|
|
assert params["maturity"].split(",") == ["overnight", "30d", "90d"]
|
|
|
|
def test_a_spot_rate_default_spans_four_tenors(self, cells):
|
|
"""Wide output carries each tenor in its own column."""
|
|
params = cells[self.SPOT]["state"]["params"]
|
|
|
|
assert params["maturity"].split(",") == ["2", "5", "10", "30"]
|
|
|
|
def test_the_administered_rates_share_one_row(self, cells):
|
|
"""One series each, so three of them fit the width two charts would take."""
|
|
row = [cells[self.IORB], cells[self.DPCREDIT], cells[self.ECB]]
|
|
|
|
assert len({cell["y"] for cell in row}) == 1
|
|
assert sum(cell["w"] for cell in row) == 40
|
|
assert max(cell["w"] for cell in row) < min(
|
|
cells[declared]["w"]
|
|
for declared in self.ORDER
|
|
if declared not in {self.IORB, self.DPCREDIT, self.ECB}
|
|
)
|
|
|
|
def test_the_crowded_charts_run_the_full_width(self, cells):
|
|
"""A tenor of TIPS and the whole credit curve each need the legend room."""
|
|
assert cells[self.TIPS]["w"] == 40
|
|
assert cells["fixedincome_bond_indices_fred_obb"]["w"] == 40
|
|
|
|
def test_no_two_widgets_overlap(self, served):
|
|
"""A cell drawn over another hides it completely."""
|
|
held: set = set()
|
|
|
|
for cell in served["tabs"]["InterestRates"]["layout"]:
|
|
covered = {
|
|
(x, y)
|
|
for x in range(cell["x"], cell["x"] + cell["w"])
|
|
for y in range(cell["y"], cell["y"] + cell["h"])
|
|
}
|
|
|
|
assert covered.isdisjoint(held), cell["i"]
|
|
|
|
held |= covered
|
|
|
|
def test_one_control_redates_every_history_on_the_tab(self, served, registry):
|
|
"""Without the group each chart has to be re-dated on its own."""
|
|
from openbb_fred.fred_router import widget_id
|
|
|
|
group = next(g for g in served["groups"] if g["name"] == "History")
|
|
dated = {
|
|
widget_id(declared)
|
|
for declared in self.ORDER
|
|
if declared not in {self.CURVE, self.HQM}
|
|
}
|
|
|
|
assert group["paramName"] == "start_date"
|
|
assert set(group["widgetIds"]) == dated
|
|
|
|
for declared in (self.CURVE, self.HQM):
|
|
assert widget_id(declared) not in group["widgetIds"]
|
|
|
|
if not dated <= set(registry):
|
|
pytest.skip("the namespace owner serves these widgets in this installation")
|
|
|
|
for identifier in group["widgetIds"]:
|
|
assert any(
|
|
param["paramName"] == "start_date"
|
|
for param in registry[identifier]["params"]
|
|
), identifier
|
|
|
|
def test_every_preset_is_a_value_the_widget_offers(self, served, registry):
|
|
"""A saved parameter the widget does not offer returns nothing."""
|
|
placed = [
|
|
cell
|
|
for cell in served["tabs"]["InterestRates"]["layout"]
|
|
if cell["i"] in registry
|
|
]
|
|
|
|
if not placed:
|
|
pytest.skip("the namespace owner serves these widgets in this installation")
|
|
|
|
checked = 0
|
|
|
|
for cell in placed:
|
|
offered = {
|
|
param["paramName"]: {
|
|
option["value"] for option in param.get("options") or []
|
|
}
|
|
for param in registry[cell["i"]]["params"]
|
|
}
|
|
|
|
for name, value in (cell["state"].get("params") or {}).items():
|
|
assert name in offered, (cell["i"], name)
|
|
|
|
if offered[name]:
|
|
checked += 1
|
|
|
|
assert set(str(value).split(",")) <= offered[name], (
|
|
cell["i"],
|
|
name,
|
|
)
|
|
|
|
assert checked
|
|
|
|
|
|
class TestSeriesDrilldown:
|
|
"""Clicking a symbol in the release table charts that series."""
|
|
|
|
SEARCH = "economy_fred_search_fred_obb"
|
|
TABLE = "fred_economy_release_table_custom_obb"
|
|
SERIES = "economy_fred_series_fred_obb"
|
|
|
|
@pytest.fixture(scope="class")
|
|
@classmethod
|
|
def served(cls):
|
|
"""Read the template as the API serves it."""
|
|
from openbb_core.provider.utils.helpers import run_async
|
|
|
|
from openbb_fred.fred_router import fred_apps
|
|
|
|
return run_async(fred_apps)[0]
|
|
|
|
@pytest.fixture(scope="class")
|
|
@classmethod
|
|
def registry(cls):
|
|
"""Build the widget registry the template refers to."""
|
|
from openbb_core.api.rest_api import app
|
|
from openbb_platform_api.utils.widgets import build_json
|
|
|
|
return build_json(app.openapi(), [])
|
|
|
|
@pytest.fixture(scope="class")
|
|
@classmethod
|
|
def placed(cls):
|
|
"""Name the drilldown widgets as this installation publishes them."""
|
|
from openbb_fred.fred_router import widget_id
|
|
|
|
return [widget_id(cls.SEARCH), widget_id(cls.TABLE), widget_id(cls.SERIES)]
|
|
|
|
def test_the_series_chart_sits_below_the_table(self, served, placed):
|
|
laid_out = [c["i"] for c in served["tabs"]["ReleaseTables"]["layout"]]
|
|
|
|
assert laid_out == placed
|
|
|
|
def test_the_chart_opens_as_a_line(self, served, placed):
|
|
cell = [
|
|
c for c in served["tabs"]["ReleaseTables"]["layout"] if c["i"] == placed[-1]
|
|
][0]
|
|
|
|
assert cell["state"]["chartView"] == {"enabled": True, "chartType": "line"}
|
|
|
|
def test_a_symbol_click_drives_the_chart(self, served, registry, placed):
|
|
group = next(g for g in served["groups"] if g["name"] == "Series")
|
|
|
|
assert group["paramName"] == "symbol"
|
|
assert group["widgetIds"] == placed
|
|
|
|
table = registry[placed[1]]["data"]["table"]
|
|
column = next(c for c in table["columnsDefs"] if c["field"] == "symbol")
|
|
|
|
assert column["renderFn"] == "cellOnClick"
|
|
assert column["renderFnParams"] == {
|
|
"actionType": "groupBy",
|
|
"groupBy": {"paramName": "symbol"},
|
|
}
|
|
|
|
@economy_owned
|
|
def test_every_group_resolves_on_the_tab_it_belongs_to(self, served, registry):
|
|
"""Every member of a group shares one tab and the parameter."""
|
|
for group in served["groups"]:
|
|
members = set(group["widgetIds"])
|
|
tab = next(
|
|
(
|
|
t
|
|
for t in served["tabs"].values()
|
|
if members <= {c["i"] for c in t["layout"]}
|
|
),
|
|
None,
|
|
)
|
|
|
|
assert tab is not None, group["name"]
|
|
|
|
for widget in group["widgetIds"]:
|
|
assert any(
|
|
p["paramName"] == group["paramName"]
|
|
for p in registry[widget]["params"]
|
|
), (group["name"], widget)
|
|
|
|
@economy_owned
|
|
def test_every_click_column_writes_to_a_grouped_parameter(
|
|
self, served, registry, placed
|
|
):
|
|
"""A click has nowhere to write without a group on the parameter."""
|
|
clicked: dict = {}
|
|
laid_out = {
|
|
cell["i"] for tab in served["tabs"].values() for cell in tab["layout"]
|
|
}
|
|
|
|
for identifier in sorted(laid_out):
|
|
table = (registry[identifier].get("data") or {}).get("table") or {}
|
|
|
|
for column in table.get("columnsDefs") or []:
|
|
if column.get("renderFn") != "cellOnClick":
|
|
continue
|
|
|
|
params = column["renderFnParams"]
|
|
|
|
assert params["actionType"] == "groupBy"
|
|
|
|
name = params["groupBy"]["paramName"]
|
|
clicked[identifier, column["field"]] = name
|
|
|
|
assert any(
|
|
g["paramName"] == name and identifier in g["widgetIds"]
|
|
for g in served["groups"]
|
|
), (identifier, column["field"])
|
|
|
|
assert clicked[placed[0], "series_id"] == "symbol"
|
|
assert clicked[placed[1], "symbol"] == "symbol"
|
|
|
|
def test_the_search_is_not_bound_to_the_presented_release(self, served, placed):
|
|
"""Binding the release would turn the search into a release listing."""
|
|
bound = {
|
|
g["paramName"] for g in served["groups"] if placed[0] in g["widgetIds"]
|
|
}
|
|
|
|
assert bound == {"symbol"}
|
|
assert "release_id" not in {g["paramName"] for g in served["groups"]}
|
|
|
|
search = next(
|
|
c for c in served["tabs"]["ReleaseTables"]["layout"] if c["i"] == placed[0]
|
|
)
|
|
|
|
assert search["state"]["params"]["search_type"] == "full_text"
|
|
assert "release_id" not in search["state"]["params"]
|
|
|
|
|
|
class TestSparkline:
|
|
"""The trend column drawn beside the periods."""
|
|
|
|
LINES = [{"series_id": "A", "name": "Total", "level": 1, "table": "T"}]
|
|
SERIES = {
|
|
"A": {
|
|
"series_id": "A",
|
|
"title": "A",
|
|
"units": "Index",
|
|
"frequency": "Monthly",
|
|
"observations": [
|
|
{"date": f"{datetime.date.today().year}-{m:02d}-01", "value": str(m)}
|
|
for m in range(1, 13)
|
|
],
|
|
}
|
|
}
|
|
|
|
@pytest.fixture
|
|
def release(self, monkeypatch):
|
|
"""Serve one table without touching the network."""
|
|
|
|
async def observed(release_id, api_key, use_cache=True):
|
|
return {"release_id": 1, "name": "One"}, self.SERIES
|
|
|
|
async def structure(release_id, element_id, api_key, use_cache=True):
|
|
return self.LINES
|
|
|
|
monkeypatch.setattr(
|
|
"openbb_fred.utils.v2.release_observations", observed, raising=False
|
|
)
|
|
monkeypatch.setattr(
|
|
"openbb_fred.utils.release_tables.table_lines", structure, raising=False
|
|
)
|
|
|
|
async def test_the_trend_is_a_list_of_values(self, release):
|
|
from openbb_fred.utils.release_tables import build_release_table
|
|
|
|
row = (await build_release_table("1", "1", "all", 3, "key"))[0]
|
|
|
|
assert row["trend"] == [float(m) for m in range(1, 13)]
|
|
|
|
async def test_the_trend_runs_oldest_to_newest(self, release):
|
|
"""A sparkline reads left to right, unlike the period columns."""
|
|
from openbb_fred.utils.release_tables import build_release_table
|
|
|
|
row = (await build_release_table("1", "1", "all", 3, "key"))[0]
|
|
|
|
assert row["trend"][0] < row["trend"][-1]
|
|
assert list(row)[4] > list(row)[5]
|
|
|
|
async def test_the_trend_sits_before_the_first_period(self, release):
|
|
from openbb_fred.utils.release_tables import build_release_table
|
|
|
|
row = (await build_release_table("1", "1", "all", 3, "key"))[0]
|
|
|
|
assert list(row)[:4] == ["series", "symbol", "units", "trend"]
|
|
|
|
async def test_the_trend_outruns_the_visible_window(self, release):
|
|
"""Showing the same points twice would make the column pointless."""
|
|
from openbb_fred.utils.release_tables import build_release_table
|
|
|
|
row = (await build_release_table("1", "1", "all", 3, "key"))[0]
|
|
periods = [k for k in row if k.startswith("20")]
|
|
|
|
assert len(row["trend"]) > len(periods)
|
|
|
|
def test_the_column_is_declared_with_a_sparkline(self):
|
|
from openbb_core.api.rest_api import app
|
|
from openbb_platform_api.utils.widgets import build_json
|
|
|
|
table = build_json(app.openapi(), [])["fred_economy_release_table_custom_obb"][
|
|
"data"
|
|
]["table"]
|
|
trend = next(c for c in table["columnsDefs"] if c["field"] == "trend")
|
|
|
|
assert trend["sparkline"]["type"] == "line"
|
|
assert table["showAll"] is True
|
|
|
|
def test_the_sparkline_declares_every_option_the_workspace_reads(self):
|
|
"""The Workspace rejects the whole widget when an option is absent."""
|
|
from openbb_core.api.rest_api import app
|
|
from openbb_platform_api.utils.widgets import build_json
|
|
|
|
table = build_json(app.openapi(), [])["fred_economy_release_table_custom_obb"][
|
|
"data"
|
|
]["table"]
|
|
trend = next(c for c in table["columnsDefs"] if c["field"] == "trend")
|
|
options = trend["sparkline"]["options"]
|
|
|
|
assert isinstance(options["customFormatter"], str)
|
|
assert options["strokeWidth"] == 2
|
|
assert set(options["pointsOfInterest"]) == {"maximum", "minimum"}
|
|
|
|
for point in options["pointsOfInterest"].values():
|
|
assert set(point) == {"fill", "stroke", "size"}
|
|
|
|
def test_the_period_columns_stay_inferred(self):
|
|
"""Declaring them is impossible - the axis changes with the release."""
|
|
from openbb_core.api.rest_api import app
|
|
from openbb_platform_api.utils.widgets import build_json
|
|
|
|
table = build_json(app.openapi(), [])["fred_economy_release_table_custom_obb"][
|
|
"data"
|
|
]["table"]
|
|
|
|
assert [c["field"] for c in table["columnsDefs"]] == [
|
|
"series",
|
|
"symbol",
|
|
"units",
|
|
"trend",
|
|
]
|
|
|
|
|
|
class TestTableLabels:
|
|
"""The table picker reads without the repeated lead."""
|
|
|
|
def test_the_repeated_lead_is_dropped(self):
|
|
"""Every table in release 9 is named after the release itself."""
|
|
from openbb_fred.utils.release_tables import (
|
|
list_elements,
|
|
release_map,
|
|
shared_prefix,
|
|
)
|
|
|
|
release = release_map()["9"]
|
|
names = [
|
|
element["name"]
|
|
for element in release["elements"]
|
|
if element.get("type", "table") == "table"
|
|
]
|
|
lead = shared_prefix(names)
|
|
labels = [element["label"].lstrip("— ") for element in list_elements("9")]
|
|
|
|
assert lead.startswith(release["name"])
|
|
assert labels == [name.removeprefix(lead) for name in names]
|
|
assert not any(label.startswith(lead) for label in labels)
|
|
|
|
def test_a_lead_is_cut_at_a_separator(self):
|
|
from openbb_fred.utils.release_tables import shared_prefix
|
|
|
|
assert shared_prefix(["Sales: Total, A", "Sales: Total, B"]) == "Sales: Total, "
|
|
|
|
def test_a_partial_word_is_not_cut(self):
|
|
"""Trimming mid-word would leave the labels unreadable."""
|
|
from openbb_fred.utils.release_tables import shared_prefix
|
|
|
|
assert shared_prefix(["Retail Sales", "Retail Stores"]) == ""
|
|
|
|
def test_a_single_table_keeps_its_name(self):
|
|
from openbb_fred.utils.release_tables import shared_prefix
|
|
|
|
assert shared_prefix(["Only One Table"]) == ""
|
|
|
|
def test_names_that_differ_from_the_start_are_untouched(self):
|
|
from openbb_fred.utils.release_tables import (
|
|
list_elements,
|
|
release_map,
|
|
shared_prefix,
|
|
)
|
|
|
|
released = release_map()
|
|
|
|
assert released
|
|
|
|
for release_id in released:
|
|
labels = [
|
|
element["label"].lstrip("— ") for element in list_elements(release_id)
|
|
]
|
|
|
|
assert shared_prefix(labels) == "", release_id
|
|
|
|
def test_the_pickers_open_wide(self):
|
|
"""A table name runs long, so the popup takes the maximum width."""
|
|
from openbb_core.api.rest_api import app
|
|
from openbb_platform_api.utils.widgets import build_json
|
|
|
|
widget = build_json(app.openapi(), [])["fred_economy_release_table_custom_obb"]
|
|
params = {p["paramName"]: p for p in widget["params"]}
|
|
|
|
assert params["release_id"]["style"] == {"popupWidth": 1000}
|
|
assert params["element_id"]["style"] == {"popupWidth": 1000}
|
|
|
|
|
|
class TestReleaseTableWidget:
|
|
"""The schema the release table widget declares."""
|
|
|
|
def test_the_grid_infers_the_period_columns(self):
|
|
"""Declared columns cannot describe a period axis that varies."""
|
|
from openbb_core.api.rest_api import app
|
|
from openbb_platform_api.utils.widgets import build_json
|
|
|
|
table = build_json(app.openapi(), [])["fred_economy_release_table_custom_obb"][
|
|
"data"
|
|
]["table"]
|
|
declared = {c["field"] for c in table["columnsDefs"]}
|
|
|
|
assert table["showAll"] is True
|
|
assert not any(field.startswith("20") for field in declared)
|
|
|
|
def test_the_table_picker_depends_on_the_release(self):
|
|
from openbb_core.api.rest_api import app
|
|
from openbb_platform_api.utils.widgets import build_json
|
|
|
|
widget = build_json(app.openapi(), [])["fred_economy_release_table_custom_obb"]
|
|
params = {p["paramName"]: p for p in widget["params"]}
|
|
|
|
assert params["element_id"]["optionsParams"] == {"release_id": "$release_id"}
|
|
assert params["element_id"]["optionsEndpoint"].endswith(
|
|
"/economy/element_choices"
|
|
)
|