mirror of
				https://github.com/Textualize/textual.git
				synced 2025-10-17 02:38:12 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			87 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			87 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import pytest
 | |
| 
 | |
| from textual.app import App, ComposeResult
 | |
| from textual.reactive import ReactiveError, reactive
 | |
| from textual.widgets import Label
 | |
| 
 | |
| 
 | |
| class FooLabel(Label):
 | |
|     foo = reactive("Foo")
 | |
| 
 | |
|     def render(self) -> str:
 | |
|         return self.foo
 | |
| 
 | |
| 
 | |
| class DataBindApp(App):
 | |
|     bar = reactive("Bar")
 | |
| 
 | |
|     def compose(self) -> ComposeResult:
 | |
|         yield FooLabel(id="label1").data_bind(foo=DataBindApp.bar)
 | |
|         yield FooLabel(id="label2")  # Not bound
 | |
| 
 | |
| 
 | |
| async def test_data_binding():
 | |
|     app = DataBindApp()
 | |
|     async with app.run_test() as pilot:
 | |
| 
 | |
|         # Check default
 | |
|         assert app.bar == "Bar"
 | |
| 
 | |
|         label1 = app.query_one("#label1", FooLabel)
 | |
|         label2 = app.query_one("#label2", FooLabel)
 | |
| 
 | |
|         # These are bound, so should have the same value as the App.foo
 | |
|         assert label1.foo == "Bar"
 | |
|         # Not yet bound, so should have its own default
 | |
|         assert label2.foo == "Foo"
 | |
| 
 | |
|         # Changing this reactive, should also change the bound widgets
 | |
|         app.bar = "Baz"
 | |
| 
 | |
|         # Sanity check
 | |
|         assert app.bar == "Baz"
 | |
| 
 | |
|         # Should also have updated bound labels
 | |
|         assert label1.foo == "Baz"
 | |
|         assert label2.foo == "Foo"
 | |
| 
 | |
|         with pytest.raises(ReactiveError):
 | |
|             # This should be an error because FooLabel.foo is not defined on the app
 | |
|             label2.data_bind(foo=FooLabel.foo)
 | |
| 
 | |
|         # Bind data outside of compose
 | |
|         label2.data_bind(foo=DataBindApp.bar)
 | |
|         await pilot.pause()
 | |
|         # Confirm new binding has propagated
 | |
|         assert label2.foo == "Baz"
 | |
| 
 | |
|         # Set reactive and check propagation
 | |
|         app.bar = "Egg"
 | |
|         assert label1.foo == "Egg"
 | |
|         assert label2.foo == "Egg"
 | |
| 
 | |
|         # Test nothing goes awry when removing widget with bound data
 | |
|         await label1.remove()
 | |
| 
 | |
|         # Try one last time
 | |
|         app.bar = "Spam"
 | |
| 
 | |
|         # Confirm remaining widgets still propagate
 | |
|         assert label2.foo == "Spam"
 | |
| 
 | |
| 
 | |
| async def test_data_binding_missing_reactive():
 | |
| 
 | |
|     class DataBindErrorApp(App):
 | |
|         foo = reactive("Bar")
 | |
| 
 | |
|         def compose(self) -> ComposeResult:
 | |
|             yield FooLabel(id="label1").data_bind(
 | |
|                 nofoo=DataBindErrorApp.foo
 | |
|             )  # Missing reactive
 | |
| 
 | |
|     app = DataBindErrorApp()
 | |
|     with pytest.raises(ReactiveError):
 | |
|         async with app.run_test():
 | |
|             pass
 | 
