{"id":35800030,"url":"https://github.com/xescugc/ebitest","last_synced_at":"2026-01-07T11:01:08.482Z","repository":{"id":328564204,"uuid":"1115482508","full_name":"xescugc/ebitest","owner":"xescugc","description":"Library to test Ebitenentine through the UI","archived":false,"fork":false,"pushed_at":"2025-12-14T01:49:38.000Z","size":100,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-12-16T00:12:55.562Z","etag":null,"topics":["ebiten","ebitengine","game","gamedev","go","golang","test","testing"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xescugc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-13T00:10:08.000Z","updated_at":"2025-12-14T01:49:42.000Z","dependencies_parsed_at":"2025-12-16T05:08:37.480Z","dependency_job_id":null,"html_url":"https://github.com/xescugc/ebitest","commit_stats":null,"previous_names":["xescugc/ebitest"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/xescugc/ebitest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xescugc%2Febitest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xescugc%2Febitest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xescugc%2Febitest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xescugc%2Febitest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xescugc","download_url":"https://codeload.github.com/xescugc/ebitest/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xescugc%2Febitest/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28234796,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2026-01-07T02:00:05.975Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ebiten","ebitengine","game","gamedev","go","golang","test","testing"],"created_at":"2026-01-07T11:00:18.010Z","updated_at":"2026-01-07T11:01:08.462Z","avatar_url":"https://github.com/xescugc.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"docs/ebitest.png\" width=25% height=25%\u003e\n\u003c/p\u003e\n\n# Ebitest\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/xescugc/ebitest.svg)](https://pkg.go.dev/github.com/xescugc/ebitest)\n\nEbitest is a lib to test Ebiten UI through inputs and asserting on what should be on the screen.\n\n## Requirements\n\nThere are a few dependencies/requirements in order to run Ebitest at it's fullest.\n\nTo run the test **headless**, meaning without having the game open on a screen, you need to install [Xvfb](https://www.x.org/archive/X11R7.7/doc/man/man1/Xvfb.1.xhtml), but\nthat is only available on Linux (and X server) for others I did not investigate yet. With this then you can just do `xvfb-run go test ./...` (check the [Makefile](./Makefile)).\n\nTo fake all the events (click, scroll, mouse move) I use [robotgo](https://github.com/go-vgo/robotgo) so I would recommend checking their README for specific\nrequirements depending on the OS you have.\n\n## Installation\n\nWith Go module support (Go 1.11+), just import:\n\n```golang\nimport \"github.com/xescugc/ebitest\"\n```\n\nOtherwise, to install the ebiteset package, run the command:\n\n```\ngo get github.com/xescugc/ebitest\n```\n\n## Usage\n\nSimple API that has:\n* `Should(s)` and `ShouldNot(s)`: Not stop execution when fails\n* `Must(s)` and `MustNot(s)`: Stop execution if assertion fails\n\nWhen asserting the `s` can be many things:\n* `string`: To search that string on the screen (the Color and Face have to be provided on the initialization of Ebitest.Run)\n* `image.Image`: Search that specific image on the screen\n* `*ebiten.Image`: Searches that specific image\n* `*ebitest.Selector`: Searches for the selector internal image\n\nWhen using a positive assertion (`Should` or `Must`) they return also the `*ebitest.Selector` so then you can interact with it\nlike doing a `.Click()`.\n\nInitialize Ebitest with `ebitest.Run(t, g)` with `t *testing.Test` and `g ebiten.Game`. A few extra options are available like:\n* `WithFace|Color`: To set the default values when the using the assertions with a text value.\n* `WithDumpErrorImages`: Which will generate an image when a test fail with the failed assertion on the folder `_ebitest_dump/`\n\nIf you need some extra interactions that are not implemented (yet) you can directly use [robotgo](https://github.com/go-vgo/robotgo),\nbut those may fail as they are not synchronized internally so I would recommend opening an issue and I'll add it.\n\n## Example\n\nThis is a simple test in which there is a Game with just a button that when clicked switches the test in it from `Click Me` to `Clicked Me`\n\n```golang\npackage ebitest_test\n\nimport (\n\t\"image/color\"\n\t\"testing\"\n\n\t\"github.com/go-vgo/robotgo\"\n\t\"github.com/hajimehoshi/ebiten/v2\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/xescugc/ebitest\"\n\t\"github.com/xescugc/ebitest/testdata\"\n)\n\nfunc TestGameButton(t *testing.T) {\n\tface, _ := testdata.LoadFont(20)\n\tg := testdata.NewGame()\n\tet := ebitest.Run(g,\n\t\tebitest.WithFace(face),\n\t\tebitest.WithColor(color.White),\n\t\tebitest.WithDumpErrorImages(),\n\t)\n\tdefer et.Close()\n\n\trobotgo.Move(0, 0)\n\trobotgo.Click(\"left\", true)\n\n\tassert.True(t, g.Clicked)\n\n\tet.PingPong.Ping()\n\n\ttext1 := \"Click Me\"\n\ttext1_2 := \"Click Me 2\"\n\ttext2 := \"Clicked Me\"\n\n\tt1s := et.Must(t, text1)\n\tt1_2s, _ := et.Should(t, text1_2)\n\n\t// Fails\n\tet.ShouldNot(t, text1_2)\n\tet.ShouldNot(t, text2)\n\n\t// Fails\n\tet.Should(t, text2)\n\n\tt1s.Click()\n\n\tet.Should(t, text1_2)\n\tet.Should(t, text2)\n\n\tt1_2s.Click()\n\n\tet.ShouldNot(t, text1)\n\tet.ShouldNot(t, text1_2)\n\tassert.Len(t, et.GetAll(text2), 2)\n\n\tet.KeyTap(ebiten.KeyI, ebiten.KeyShift)\n\tassert.True(t, g.ClickedShiftI)\n}\n```\n\nAn output could be for example\n\n```\n--- FAIL: TestGameButton (9.34s)\n    ebitest_test.go:39: \n                Error Trace:    ebitest.go:150\n                                                        ebitest_test.go:39\n                Error:          selector found\n                                image at: ebitest/_ebitest_dump/019b2e81-8d9a-7bb7-ba16-639756d11e58.png\n                Test:           TestGameButton\n    ebitest_test.go:43: \n                Error Trace:    ebitest.go:126\n                                                        ebitest_test.go:43\n                Error:          selector not found\n                                image at: ebitest/_ebitest_dump/019b2e81-966e-7715-8c5f-6a6acc92720c.png\n                Test:           TestGameButton\nFAIL\nFAIL    github.com/xescugc/ebitest      9.393s\nFAIL\nmake: *** [Makefile:7: test] Error \n```\n\nAnd then you have the `image at: ebitest/_ebitest_dump/019b2e81-8d9a-7bb7-ba16-639756d11e58.png` that expects to not find something, and it finds\nit and reports the image with the highlight of what was found. At the top right you can see what was looking for.\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"docs/should_not.png\" width=50% height=50%\u003e\n\u003c/p\u003e\n\nAnd the `image at: ebitest/_ebitest_dump/019b2e81-966e-7715-8c5f-6a6acc92720c.png` that expect to find something that was not found. At the top right\nyou can see what was looking for.\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"docs/should.png\" width=50% height=50%\u003e\n\u003c/p\u003e\n\n\n## Run it on a CI\n\nIf the CI has low resources (like GitHub Actions) it'll most likely fail (check `Known issues#2`) but you\ncan check what I install for it to run on the [`go.yml`](.github/workflows/go.yml)\n\n## Known issues and Limitations\n\n1/ You cannot have more than 1 test case\n\nBasically you cannot run more than one test as even calling `Ebitest.Close()` there are some resources missing and you may get an error like\n\n\u003e panic: ebiten: NewImage cannot be called after RunGame finishes [recovered, repanicked]\n\n2/ Some false positive/negative\n\nDue to the nature of this test (the game is running on a goroutine) there may be the case in which an input is not registered by the game\nso an expectation may randomly fail.\n\nI kind of fixed it (100 consecutive test pass) using a custom [PingPong](./ping_pong.go) and [TicTacToe](./tic_tac_toe.go) that basically forces a context switch and synchronizes Input+Game.Update+Game.Draw but it still fails in low resource like GitHub [Actions](https://github.com/xescugc/ebitest/actions) for example.\n\n3/ Size of the screen\n\nBy default the screen is of `640x480` if using `xvfb`. To make it bigger you can directly increase the size with `ebiten.SetWindowSize(750, 750)`, though setting big sizes\nI've seen it causes some issues that the click are not where they are expected to be and are a bit off and miss which causes errors.\n\n## Plans\n\n* Add more helpers for assertions (like animations)\n* Add more inputs (potentially just port all the [robotgo](https://github.com/go-vgo/robotgo) lib) synchronized\n* Others\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxescugc%2Febitest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxescugc%2Febitest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxescugc%2Febitest/lists"}