charmbracelet / charmbracelet/x
feat(teatest): add WaitForMsg function
- Dominant language
- Go
- Stars
- 314
- Forks
- 94
- Avg merge
- 3d 22h
- Merged PRs (30d)
- 2
Description
## **Is your feature request related to a problem? Please describe.**
I find it challenging to test individual child models in TUIs built using a tree of models, as described in [this blog post](https://leg100.github.io/en/posts/building-bubbletea-programs/). This issue arises because the `teatest` `TestModel` does not provide a way to access or interact with the underlying `tea.Model` before the `tea.Program` is "finished" (usually triggered by returning `tea.QuitCmd` from the model).
The current API supports inspecting string output mid-run via `TestModel.Output`, but this is insufficient when testing models that communicate state changes via custom `tea.Msg`s. This limitation forces testing of individual models through their parent/root models or requires forking `teatest` to expose this functionality.
## **Describe the solution you'd like**
A `TestModel.WaitForMsg` function that blocks until a specific `tea.Msg` is received. This would enable testing of `tea.Msg`-driven state transitions in models without relying solely on string output. The existing `TestModel.WaitFor` function, which blocks for specific output, should also be renamed to `WaitForOutput` for clarity.
This change would facilitate a more robust approach to testing TUIs by allowing:
1. **End-to-end tests**: Using `WaitForOutput` to verify user-facing outputs.
2. **Integration tests**: Using `WaitForMsg` to assert program state transitions and verify key `tea.Msg` emissions.
## **Describe alternatives you've considered**
1. Forking the `teatest` package to expose the inner `tea.Model` during execution, but this approach is complex since `tea.Program` does not natively support accessing the model mid-run.
2. Testing models through their root model, which introduces unnecessary complexity and reduces test isolation.
Ultimately, I shifted focus towards blackbox testing (`WaitForOutput`) and integration tests (`WaitForMsg`). This approach aligns with TUI testing being closer to integration testing than unit testing.
## **Additional context**
This feature request stems from testing a TUI structured with nested models in my open-source projects, such as [Tetrigo](https://github.com/Broderick-Westrope/tetrigo) (see [this](https://github.com/Broderick-Westrope/tetrigo/pull/35) PR). In these cases, child models emit custom `tea.Msg`s to signal state changes (e.g., transitioning from a menu to the game screen).
I have a working branch implementing `WaitForMsg` in `teatest` and an example in my project. I will create a PR to link my current code to this issue, but expect further discussions to take place before making changes to the API.
In addition to the mentioned changes, I have also proposed a rename of the existing function `WaitFor` to `WaitForOutput` in the hope that it will not get confused with the new method.
Contributor guide
Research direction
Start with the teatest TestModel API, especially the existing WaitFor function and the working branch mentioned in the issue. Review how TestModel observes output and how tea.Program runs its model before deciding how WaitForMsg and the WaitForOutput rename should fit. Done means the proposed API supports message-driven integration tests without requiring access to the underlying model.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- testing
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100