reflex/tests/components/core/test_foreach.py
Khaleel Al-Adhami a5c73ad8e5
Use old serializer system in LiteralVar (#3875)
* use serializer system

* add checks for unsupported operands

* and and or are now supported

* format

* remove unnecessary call to JSON

* put base before rest

* fix failing testcase

* add hinting to get static analysis to complain

* damn

* big changes

* get typeguard from extensions

* please darglint

* dangit darglint

* remove one from vars

* add without data and use it in plotly

* DARGLINT

* change format for special props

* add pyi

* delete instances of Var.create

* modify client state to work

* fixed so much

* remove every Var.create

* delete all basevar stuff

* checkpoint

* fix pyi

* get older python to work

* dangit darglint

* add simple fix to last failing testcase

* remove var name unwrapped and put client state on immutable var

* fix older python

* fox event issues

* change forms pyi

* make test less strict

* use rx state directly

* add typeignore to page_id

* implement foreach

* delete .web states folder silly

* update reflex chakra

* fix issue when on mount or on unmount is not set

* nuke Var

* run pyi

* import immutablevar in critical location

* delete unwrap vars

* bring back array ref

* fix style props in app

* /health endpoint for K8 Liveness and Readiness probes (#3855)

* Added API Endpoint

* Added API Endpoint

* Added Unit Tests

* Added Unit Tests

* main

* Apply suggestions from Code Review

* Fix Ruff Formatting

* Update Socket Events

* Async Functions

* Update find_replace (#3886)

* [REF-3592]Promote `rx.progress` from radix themes (#3878)

* Promote `rx.progress` from radix themes

* fix pyi

* add warning when accessing `rx._x.progress`

* Use correct flexgen backend URL (#3891)

* Remove demo template (#3888)

* gitignore .web (#3885)

* update overflowY in AUTO_HEIGHT_JS from hidden to scroll (#3882)

* Retain mutability inside `async with self` block (#3884)

When emitting a state update, restore `_self_mutable` to the value it had
previously so that `yield` in the middle of `async with self` does not result
in an immutable StateProxy.

Fix #3869

* Include child imports in markdown component_map (#3883)

If a component in the markdown component_map contains children components, use
`_get_all_imports` to recursively enumerate them.

Fix #3880

* [REF-3570] Remove deprecated REDIS_URL syntax (#3892)

* mixin computed vars should only be applied to highest level state (#3833)

* improve state hierarchy validation, drop old testing special case (#3894)

* fix var dependency dicts (#3842)

* Adding array to array pluck operation. (#3868)

* fix initial state without cv fallback (#3670)

* add fragment to foreach (#3877)

* Update docker-example (#3324)

* /health endpoint for K8 Liveness and Readiness probes (#3855)

* Added API Endpoint

* Added API Endpoint

* Added Unit Tests

* Added Unit Tests

* main

* Apply suggestions from Code Review

* Fix Ruff Formatting

* Update Socket Events

* Async Functions

* /health endpoint for K8 Liveness and Readiness probes (#3855)

* Added API Endpoint

* Added API Endpoint

* Added Unit Tests

* Added Unit Tests

* main

* Apply suggestions from Code Review

* Fix Ruff Formatting

* Update Socket Events

* Async Functions

* Merge branch 'main' into use-old-serializer-in-literalvar

* [REF-3570] Remove deprecated REDIS_URL syntax (#3892)

* /health endpoint for K8 Liveness and Readiness probes (#3855)

* Added API Endpoint

* Added API Endpoint

* Added Unit Tests

* Added Unit Tests

* main

* Apply suggestions from Code Review

* Fix Ruff Formatting

* Update Socket Events

* Async Functions

* [REF-3570] Remove deprecated REDIS_URL syntax (#3892)

* remove extra var

Co-authored-by: Masen Furer <m_github@0x26.net>

* resolve typo

* write better doc for var.create

* return var value when we know it's literal var

* fix unit test

* less bloat for ToOperations

* simplify ImmutableComputedVar.__get__ (#3902)

* simplify ImmutableComputedVar.__get__

* ruff it

---------

Co-authored-by: Samarth Bhadane <samarthbhadane119@gmail.com>
Co-authored-by: Elijah Ahianyo <elijahahianyo@gmail.com>
Co-authored-by: Masen Furer <m_github@0x26.net>
Co-authored-by: benedikt-bartscher <31854409+benedikt-bartscher@users.noreply.github.com>
Co-authored-by: Vishnu Deva <vishnu.deva12@gmail.com>
Co-authored-by: abulvenz <a.eismann@senbax.de>
2024-09-10 11:43:37 -07:00

293 lines
8.6 KiB
Python

from typing import Dict, List, Set, Tuple, Union
import pytest
from reflex import el
from reflex.components.component import Component
from reflex.components.core.foreach import (
Foreach,
ForeachRenderError,
ForeachVarError,
foreach,
)
from reflex.components.radix.themes.layout.box import box
from reflex.components.radix.themes.typography.text import text
from reflex.ivars.base import ImmutableVar
from reflex.ivars.number import NumberVar
from reflex.ivars.sequence import ArrayVar
from reflex.state import BaseState, ComponentState
class ForEachState(BaseState):
"""A state for testing the ForEach component."""
colors_list: List[str] = ["red", "yellow"]
nested_colors_list: List[List[str]] = [["red", "yellow"], ["blue", "green"]]
colors_dict_list: List[Dict[str, str]] = [
{
"name": "red",
},
{"name": "yellow"},
]
colors_nested_dict_list: List[Dict[str, List[str]]] = [{"shades": ["light-red"]}]
primary_color: Dict[str, str] = {"category": "primary", "name": "red"}
color_with_shades: Dict[str, List[str]] = {
"red": ["orange", "yellow"],
"yellow": ["orange", "green"],
}
nested_colors_with_shades: Dict[str, Dict[str, List[Dict[str, str]]]] = {
"primary": {"red": [{"shade": "dark"}]}
}
color_tuple: Tuple[str, str] = (
"red",
"yellow",
)
colors_set: Set[str] = {"red", "green"}
bad_annotation_list: list = [["red", "orange"], ["yellow", "blue"]]
color_index_tuple: Tuple[int, str] = (0, "red")
class TestComponentState(ComponentState):
"""A test component state."""
foo: bool
@classmethod
def get_component(cls, *children, **props) -> Component:
"""Get the component.
Args:
children: The children components.
props: The component props.
Returns:
The component.
"""
return el.div(*children, **props)
def display_color(color):
assert color._var_type == str
return box(text(color))
def display_color_name(color):
assert color._var_type == Dict[str, str]
return box(text(color["name"]))
def display_shade(color):
assert color._var_type == Dict[str, List[str]]
return box(text(color["shades"][0]))
def display_primary_colors(color):
assert color._var_type == Tuple[str, str]
return box(text(color[0]), text(color[1]))
def display_color_with_shades(color):
assert color._var_type == Tuple[str, List[str]]
return box(text(color[0]), text(color[1][0]))
def display_nested_color_with_shades(color):
assert color._var_type == Tuple[str, Dict[str, List[Dict[str, str]]]]
return box(text(color[0]), text(color[1]["red"][0]["shade"]))
def show_shade(item):
return text(item[1][0]["shade"])
def display_nested_color_with_shades_v2(color):
assert color._var_type == Tuple[str, Dict[str, List[Dict[str, str]]]]
return box(text(foreach(color[1], show_shade)))
def display_color_tuple(color):
assert color._var_type == str
return box(text(color))
def display_colors_set(color):
assert color._var_type == str
return box(text(color))
def display_nested_list_element(element: ArrayVar[List[str]], index: NumberVar[int]):
assert element._var_type == List[str]
assert index._var_type == int
return box(text(element[index]))
def display_color_index_tuple(color):
assert color._var_type == Union[int, str]
return box(text(color))
seen_index_vars = set()
@pytest.mark.parametrize(
"state_var, render_fn, render_dict",
[
(
ForEachState.colors_list,
display_color,
{
"iterable_state": f"{ForEachState.get_full_name()}.colors_list",
"iterable_type": "list",
},
),
(
ForEachState.colors_dict_list,
display_color_name,
{
"iterable_state": f"{ForEachState.get_full_name()}.colors_dict_list",
"iterable_type": "list",
},
),
(
ForEachState.colors_nested_dict_list,
display_shade,
{
"iterable_state": f"{ForEachState.get_full_name()}.colors_nested_dict_list",
"iterable_type": "list",
},
),
(
ForEachState.primary_color,
display_primary_colors,
{
"iterable_state": f"{ForEachState.get_full_name()}.primary_color",
"iterable_type": "dict",
},
),
(
ForEachState.color_with_shades,
display_color_with_shades,
{
"iterable_state": f"{ForEachState.get_full_name()}.color_with_shades",
"iterable_type": "dict",
},
),
(
ForEachState.nested_colors_with_shades,
display_nested_color_with_shades,
{
"iterable_state": f"{ForEachState.get_full_name()}.nested_colors_with_shades",
"iterable_type": "dict",
},
),
(
ForEachState.nested_colors_with_shades,
display_nested_color_with_shades_v2,
{
"iterable_state": f"{ForEachState.get_full_name()}.nested_colors_with_shades",
"iterable_type": "dict",
},
),
(
ForEachState.color_tuple,
display_color_tuple,
{
"iterable_state": f"{ForEachState.get_full_name()}.color_tuple",
"iterable_type": "tuple",
},
),
(
ForEachState.colors_set,
display_colors_set,
{
"iterable_state": f"{ForEachState.get_full_name()}.colors_set",
"iterable_type": "set",
},
),
(
ForEachState.nested_colors_list,
lambda el, i: display_nested_list_element(el, i),
{
"iterable_state": f"{ForEachState.get_full_name()}.nested_colors_list",
"iterable_type": "list",
},
),
(
ForEachState.color_index_tuple,
display_color_index_tuple,
{
"iterable_state": f"{ForEachState.get_full_name()}.color_index_tuple",
"iterable_type": "tuple",
},
),
],
)
def test_foreach_render(state_var, render_fn, render_dict):
"""Test that the foreach component renders without error.
Args:
state_var: the state var.
render_fn: The render callable
render_dict: return dict on calling `component.render`
"""
component = Foreach.create(state_var, render_fn)
rend = component.render()
assert rend["iterable_state"] == render_dict["iterable_state"]
assert rend["iterable_type"] == render_dict["iterable_type"]
# Make sure the index vars are unique.
arg_index = rend["arg_index"]
assert isinstance(arg_index, ImmutableVar)
assert arg_index._var_name not in seen_index_vars
assert arg_index._var_type == int
seen_index_vars.add(arg_index._var_name)
def test_foreach_bad_annotations():
"""Test that the foreach component raises a ForeachVarError if the iterable is of type Any."""
with pytest.raises(ForeachVarError):
Foreach.create(
ForEachState.bad_annotation_list,
lambda sublist: Foreach.create(sublist, lambda color: text(color)),
)
def test_foreach_no_param_in_signature():
"""Test that the foreach component raises a ForeachRenderError if no parameters are passed."""
with pytest.raises(ForeachRenderError):
Foreach.create(
ForEachState.colors_list,
lambda: text("color"),
)
def test_foreach_too_many_params_in_signature():
"""Test that the foreach component raises a ForeachRenderError if too many parameters are passed."""
with pytest.raises(ForeachRenderError):
Foreach.create(
ForEachState.colors_list,
lambda color, index, extra: text(color),
)
def test_foreach_component_styles():
"""Test that the foreach component works with global component styles."""
component = el.div(
foreach(
ForEachState.colors_list,
display_color,
)
)
component._add_style_recursive({box: {"color": "red"}})
assert 'css={({ ["color"] : "red" })}' in str(component)
def test_foreach_component_state():
"""Test that using a component state to render in the foreach raises an error."""
with pytest.raises(TypeError):
Foreach.create(
ForEachState.colors_list,
TestComponentState.create,
)