123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- from typing import Dict, List, Type
- import pytest
- import pynecone as pc
- from pynecone.components.component import Component, CustomComponent, custom_component
- from pynecone.components.layout.box import Box
- from pynecone.event import EVENT_ARG, EVENT_TRIGGERS, EventHandler
- from pynecone.state import State
- from pynecone.style import Style
- from pynecone.utils import imports
- from pynecone.vars import ImportVar, Var
- @pytest.fixture
- def test_state():
- class TestState(State):
- num: int
- def do_something(self):
- pass
- def do_something_arg(self, arg):
- pass
- return TestState
- @pytest.fixture
- def component1() -> Type[Component]:
- """A test component.
- Returns:
- A test component.
- """
- class TestComponent1(Component):
- # A test string prop.
- text: Var[str]
- # A test number prop.
- number: Var[int]
- def _get_imports(self) -> imports.ImportDict:
- return {"react": {ImportVar(tag="Component")}}
- def _get_custom_code(self) -> str:
- return "console.log('component1')"
- return TestComponent1
- @pytest.fixture
- def component2() -> Type[Component]:
- """A test component.
- Returns:
- A test component.
- """
- class TestComponent2(Component):
- # A test list prop.
- arr: Var[List[str]]
- def get_controlled_triggers(self) -> Dict[str, Var]:
- """Test controlled triggers.
- Returns:
- Test controlled triggers.
- """
- return {
- "on_open": EVENT_ARG,
- "on_close": EVENT_ARG,
- }
- def _get_imports(self) -> imports.ImportDict:
- return {"react-redux": {ImportVar(tag="connect")}}
- def _get_custom_code(self) -> str:
- return "console.log('component2')"
- return TestComponent2
- @pytest.fixture
- def component3() -> Type[Component]:
- """A test component with hook defined.
- Returns:
- A test component.
- """
- class TestComponent3(Component):
- def _get_hooks(self) -> str:
- return "const a = () => true"
- return TestComponent3
- @pytest.fixture
- def component4() -> Type[Component]:
- """A test component with hook defined.
- Returns:
- A test component.
- """
- class TestComponent4(Component):
- def _get_hooks(self) -> str:
- return "const b = () => false"
- return TestComponent4
- @pytest.fixture
- def component5() -> Type[Component]:
- """A test component.
- Returns:
- A test component.
- """
- class TestComponent5(Component):
- tag = "Tag"
- invalid_children: List[str] = ["Text"]
- return TestComponent5
- @pytest.fixture
- def on_click1() -> EventHandler:
- """A sample on click function.
- Returns:
- A sample on click function.
- """
- def on_click1():
- pass
- return EventHandler(fn=on_click1)
- @pytest.fixture
- def on_click2() -> EventHandler:
- """A sample on click function.
- Returns:
- A sample on click function.
- """
- def on_click2():
- pass
- return EventHandler(fn=on_click2)
- @pytest.fixture
- def my_component():
- """A test component function.
- Returns:
- A test component function.
- """
- def my_component(prop1: Var[str], prop2: Var[int]):
- return Box.create(prop1, prop2)
- return my_component
- def test_set_style_attrs(component1):
- """Test that style attributes are set in the dict.
- Args:
- component1: A test component.
- """
- component = component1(color="white", text_align="center")
- assert component.style["color"] == "white"
- assert component.style["textAlign"] == "center"
- def test_create_component(component1):
- """Test that the component is created correctly.
- Args:
- component1: A test component.
- """
- children = [component1() for _ in range(3)]
- attrs = {"color": "white", "text_align": "center"}
- c = component1.create(*children, **attrs)
- assert isinstance(c, component1)
- assert c.children == children
- assert c.style == {"color": "white", "textAlign": "center"}
- def test_add_style(component1, component2):
- """Test adding a style to a component.
- Args:
- component1: A test component.
- component2: A test component.
- """
- style = {
- component1: Style({"color": "white"}),
- component2: Style({"color": "black"}),
- }
- c1 = component1().add_style(style) # type: ignore
- c2 = component2().add_style(style) # type: ignore
- assert c1.style["color"] == "white"
- assert c2.style["color"] == "black"
- def test_get_imports(component1, component2):
- """Test getting the imports of a component.
- Args:
- component1: A test component.
- component2: A test component.
- """
- c1 = component1.create()
- c2 = component2.create(c1)
- assert c1.get_imports() == {"react": {ImportVar(tag="Component")}}
- assert c2.get_imports() == {
- "react-redux": {ImportVar(tag="connect")},
- "react": {ImportVar(tag="Component")},
- }
- def test_get_custom_code(component1, component2):
- """Test getting the custom code of a component.
- Args:
- component1: A test component.
- component2: A test component.
- """
- # Check that the code gets compiled correctly.
- c1 = component1.create()
- c2 = component2.create()
- assert c1.get_custom_code() == {"console.log('component1')"}
- assert c2.get_custom_code() == {"console.log('component2')"}
- # Check that nesting components compiles both codes.
- c1 = component1.create(c2)
- assert c1.get_custom_code() == {
- "console.log('component1')",
- "console.log('component2')",
- }
- # Check that code is not duplicated.
- c1 = component1.create(c2, c2, c1, c1)
- assert c1.get_custom_code() == {
- "console.log('component1')",
- "console.log('component2')",
- }
- def test_get_props(component1, component2):
- """Test that the props are set correctly.
- Args:
- component1: A test component.
- component2: A test component.
- """
- assert component1.get_props() == {"text", "number"}
- assert component2.get_props() == {"arr"}
- @pytest.mark.parametrize(
- "text,number",
- [
- ("", 0),
- ("test", 1),
- ("hi", -13),
- ],
- )
- def test_valid_props(component1, text: str, number: int):
- """Test that we can construct a component with valid props.
- Args:
- component1: A test component.
- text: A test string.
- number: A test number.
- """
- c = component1.create(text=text, number=number)
- assert c.text == text
- assert c.number == number
- @pytest.mark.parametrize(
- "text,number", [("", "bad_string"), (13, 1), (None, 1), ("test", [1, 2, 3])]
- )
- def test_invalid_prop_type(component1, text: str, number: int):
- """Test that an invalid prop type raises an error.
- Args:
- component1: A test component.
- text: A test string.
- number: A test number.
- """
- # Check that
- with pytest.raises(TypeError):
- component1.create(text=text, number=number)
- def test_var_props(component1, test_state):
- """Test that we can set a Var prop.
- Args:
- component1: A test component.
- test_state: A test state.
- """
- c1 = component1.create(text="hello", number=test_state.num)
- assert c1.number == test_state.num
- def test_get_controlled_triggers(component1, component2):
- """Test that we can get the controlled triggers of a component.
- Args:
- component1: A test component.
- component2: A test component.
- """
- assert component1().get_controlled_triggers() == dict()
- assert set(component2().get_controlled_triggers()) == {"on_open", "on_close"}
- def test_get_triggers(component1, component2):
- """Test that we can get the triggers of a component.
- Args:
- component1: A test component.
- component2: A test component.
- """
- assert component1().get_triggers() == EVENT_TRIGGERS
- assert component2().get_triggers() == {"on_open", "on_close"} | EVENT_TRIGGERS
- def test_create_custom_component(my_component):
- """Test that we can create a custom component.
- Args:
- my_component: A test custom component.
- """
- component = CustomComponent(component_fn=my_component, prop1="test", prop2=1)
- assert component.tag == "MyComponent"
- assert component.get_props() == set()
- assert component.get_custom_components() == {component}
- def test_custom_component_hash(my_component):
- """Test that the hash of a custom component is correct.
- Args:
- my_component: A test custom component.
- """
- component1 = CustomComponent(component_fn=my_component, prop1="test", prop2=1)
- component2 = CustomComponent(component_fn=my_component, prop1="test", prop2=2)
- assert {component1, component2} == {component1}
- def test_custom_component_wrapper():
- """Test that the wrapper of a custom component is correct."""
- @custom_component
- def my_component(width: Var[int], color: Var[str]):
- return pc.box(
- width=width,
- color=color,
- )
- ccomponent = my_component(
- pc.text("child"), width=Var.create(1), color=Var.create("red")
- )
- assert isinstance(ccomponent, CustomComponent)
- assert len(ccomponent.children) == 1
- assert isinstance(ccomponent.children[0], pc.Text)
- component = ccomponent.get_component()
- assert isinstance(component, Box)
- def test_invalid_event_handler_args(component2, test_state):
- """Test that an invalid event handler raises an error.
- Args:
- component2: A test component.
- test_state: A test state.
- """
- # Uncontrolled event handlers should not take args.
- # This is okay.
- component2.create(on_click=test_state.do_something)
- # This is not okay.
- with pytest.raises(ValueError):
- component2.create(on_click=test_state.do_something_arg)
- # However lambdas are okay.
- component2.create(on_click=lambda: test_state.do_something_arg(1))
- component2.create(
- on_click=lambda: [test_state.do_something_arg(1), test_state.do_something]
- )
- component2.create(
- on_click=lambda: [test_state.do_something_arg(1), test_state.do_something()]
- )
- # Controlled event handlers should take args.
- # This is okay.
- component2.create(on_open=test_state.do_something_arg)
- # do_something is allowed and will simply run while ignoring the arg
- component2.create(on_open=test_state.do_something)
- component2.create(on_open=[test_state.do_something_arg, test_state.do_something])
- def test_get_hooks_nested(component1, component2, component3):
- """Test that a component returns hooks from child components.
- Args:
- component1: test component.
- component2: another component.
- component3: component with hooks defined.
- """
- c = component1.create(
- component2.create(arr=[]),
- component3.create(),
- component3.create(),
- component3.create(),
- text="a",
- number=1,
- )
- assert c.get_hooks() == component3().get_hooks()
- def test_get_hooks_nested2(component3, component4):
- """Test that a component returns both when parent and child have hooks.
- Args:
- component3: component with hooks defined.
- component4: component with different hooks defined.
- """
- exp_hooks = component3().get_hooks().union(component4().get_hooks())
- assert component3.create(component4.create()).get_hooks() == exp_hooks
- assert component4.create(component3.create()).get_hooks() == exp_hooks
- assert (
- component4.create(
- component3.create(),
- component4.create(),
- component3.create(),
- ).get_hooks()
- == exp_hooks
- )
- def test_unsupported_child_components(component5):
- """Test that a value error is raised when an unsupported component is provided as a child.
- Args:
- component5: the test component
- """
- with pytest.raises(ValueError) as err:
- comp = component5.create(pc.text("testing component"))
- comp.render()
- assert (
- err.value.args[0]
- == f"The component `tag` cannot have `text` as a child component"
- )
|