{"id":51562927,"url":"https://github.com/cookiengineer/gotestwasm","last_synced_at":"2026-07-10T12:01:42.225Z","repository":{"id":368273850,"uuid":"1284306233","full_name":"cookiengineer/gotestwasm","owner":"cookiengineer","description":":sparkles: Slopcoding towards testable Go WASM builds :construction:","archived":false,"fork":false,"pushed_at":"2026-06-29T20:03:53.000Z","size":49,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-29T21:26:37.497Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cookiengineer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2026-06-29T18:22:00.000Z","updated_at":"2026-06-29T20:03:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cookiengineer/gotestwasm","commit_stats":null,"previous_names":["cookiengineer/gotestwasm"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/cookiengineer/gotestwasm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cookiengineer%2Fgotestwasm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cookiengineer%2Fgotestwasm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cookiengineer%2Fgotestwasm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cookiengineer%2Fgotestwasm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cookiengineer","download_url":"https://codeload.github.com/cookiengineer/gotestwasm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cookiengineer%2Fgotestwasm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35330738,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-10T02:00:06.465Z","response_time":60,"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":[],"created_at":"2026-07-10T12:01:41.456Z","updated_at":"2026-07-10T12:01:42.219Z","avatar_url":"https://github.com/cookiengineer.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gotestwasm\n\n`gotestwasm` compiles Go tests into standalone WebAssembly binaries and executes them in a headless browser. It reuses the Go toolchain for compilation and linking while adding the browser runtime layer that `go test` does not provide for the `GOOS=js GOARCH=wasm` target.\n\n## Problem\n\n`go test -c` compiles a test binary for the host platform. When cross-compiling with `GOOS=js GOARCH=wasm`, the resulting `.wasm` file has no runtime environment — the Go standard library's `js` port expects a JavaScript host (Node.js or a browser) with `wasm_exec.js` to provide system call implementations. Running `go test` directly for this target is not supported because `go test` attempts to execute the binary natively.\n\n`gotestwasm` bridges this gap: it compiles the test binary with `go test -c`, assembles a browser environment around it (`wasm_exec.js` + `index.html`), and runs it through a headless Chromium instance. The result is a test workflow that behaves like `go test -v` but executes inside a real browser's JavaScript runtime.\n\n## Comparison with upstream\n\n| Capability                     | `go test`       | `gotestwasm`                      |\n|--------------------------------|-----------------|-----------------------------------|\n| Compiles test binary           | yes             | yes (delegates to `go test -c`)   |\n| Executes binary natively       | yes             | no                                |\n| Executes binary in browser     | no              | yes                               |\n| `//go:build wasm` test files   | skipped on host | compiled and run                  |\n| `syscall/js` in tests          | not available   | fully supported                   |\n| Headless CI execution          | yes             | yes (via Chromium)                |\n| Locates `wasm_exec.js`         | N/A             | automatic from `GOROOT`           |\n| Generates `_testmain.go`       | internal only   | written to disk for inspection    |\n\n## How it works\n\n```\ngotestwasm ./mypackage\n```\n\n1. **Package discovery** — Uses `golang.org/x/tools/go/packages` to resolve module-aware import paths, build tags, and identify all `*_test.go` files. Internal test files (`package pkg`) and external test files (`package pkg_test`) are tracked separately.\n\n2. **Test function scanning** — Parses each `*_test.go` file with `go/parser` and `go/ast` to extract `Test*`, `Benchmark*`, `Fuzz*`, `TestMain`, and `Example*` functions. Function signatures are validated against Go's test function conventions.\n\n3. **Testmain generation** — Produces a `_testmain.go` file using the same template structure as `cmd/go`. This file imports all test packages, registers discovered test functions with `testing.MainStart`, and provides the `main()` entry point. The generated file is written alongside the output for inspection and customization.\n\n4. **Compilation** — Shells out to `go test -c -o tests.wasm` with `GOOS=js GOARCH=wasm`. This reuses the full Go toolchain: compiler, linker, module resolution, build cache, and PGO. The `go test -c` path is required because it handles the `testing/internal/testdeps` import that `go build` rejects.\n\n5. **Runtime assembly** — Creates a `/tmp/go-test-*` directory containing:\n   - `tests.wasm` — the compiled test binary\n   - `wasm_exec.js` — the Go JS runtime, located automatically from `GOROOT/lib/wasm/`\n   - `index.html` — a bootstrap page that loads the WASM, intercepts console output, and reports results\n\n6. **Headless execution** (with `--run`) — Starts an embedded HTTP server serving the test directory, launches Chromium in headless mode with `--window-size=1280,1024`, waits for the test binary to report its exit status via `fetch(\"/report\")`, captures the full console output, and returns a matching exit code.\n\n## Installation\n\n```\ngo install github.com/cookiengineer/gotestwasm/cmds/gotestwasm@latest\n```\n\nRequires Go 1.21 or later. The headless execution feature (`--run`) requires Chromium in `$PATH`.\n\n## Usage\n\n```\ngotestwasm [flags] [packages]\n```\n\n### Flags\n\n| Flag           | Default        | Description                                          |\n|----------------|----------------|------------------------------------------------------|\n| `-o`           | `tests.wasm`   | Output WASM file name                                |\n| `-outputdir`   | `.`            | Output directory for WASM and `_testmain.go`         |\n| `-tags`        |                | Build tags, comma-separated                          |\n| `-gen`         | `false`        | Generate `_testmain.go` only, skip build             |\n| `-printmain`   | `false`        | Print generated `_testmain.go` to stdout             |\n| `-v`           | `false`        | Verbose output                                       |\n| `-ldflags`     |                | Extra linker flags                                   |\n| `-gcflags`     |                | Extra compiler flags                                 |\n| `-run`         | `false`        | Build and execute in headless Chromium               |\n| `-chromium`    | `chromium`     | Path to Chromium binary                              |\n| `-timeout`     | `30000`        | Test timeout in milliseconds (headless mode)         |\n\n### Examples\n\nBuild a test binary:\n\n```\n$ gotestwasm ./mypackage\nOK  mypackage -\u003e /home/user/mypackage/tests.wasm\n```\n\nBuild with build tags:\n\n```\n$ gotestwasm -tags=integration ./...\n```\n\nGenerate `_testmain.go` for inspection:\n\n```\n$ gotestwasm -gen -printmain ./mypackage\n// Code generated by gotestwasm. DO NOT EDIT.\n\npackage main\n\nimport (\n    \"os\"\n    \"reflect\"\n    \"testing\"\n    \"testing/internal/testdeps\"\n    _test \"mypackage\"\n)\n\nvar tests = []testing.InternalTest{\n    {\"TestFoo\", _test.TestFoo},\n    ...\n```\n\nBuild and run in headless Chromium:\n\n```\n$ gotestwasm -run -tags=wasm ./example\n=== RUN   TestWindow\n    window_test.go:19: window innerWidth=1280, innerHeight=885\n--- PASS: TestWindow (0.00s)\nPASS\n```\n\n## Example project\n\nThe `./example` directory contains a browser-targeted test:\n\n```go\n//go:build wasm\n\npackage example\n\nimport (\n    \"syscall/js\"\n    \"testing\"\n)\n\nfunc TestWindow(t *testing.T) {\n    window := js.Global().Get(\"window\")\n    if window.IsUndefined() || window.IsNull() {\n        t.Skip(\"window is not available in this runtime\")\n    }\n\n    width := window.Get(\"innerWidth\").Int()\n    height := window.Get(\"innerHeight\").Int()\n\n    t.Logf(\"window innerWidth=%d, innerHeight=%d\", width, height)\n\n    if width \u003c= 1024 {\n        t.Errorf(\"expected window.innerWidth \u003e 1024, got %d\", width)\n    }\n    if height \u003c= 768 {\n        t.Errorf(\"expected window.innerHeight \u003e 768, got %d\", height)\n    }\n}\n```\n\nThe `//go:build wasm` constraint ensures this test is skipped on the host platform and only compiled when targeting WASM. The test accesses browser DOM APIs through `syscall/js` to assert viewport dimensions. Build and run it with:\n\n```\ngotestwasm -run -tags=wasm ./example\n```\n\n## Generated testmain\n\nThe generated `_testmain.go` follows the same structure as the test harness produced internally by `go test`. It imports each test package with a distinct alias (`_test` for internal tests, `_xtest` for external `package pkg_test` tests), registers discovered functions into the `testing.InternalTest` / `InternalBenchmark` / `InternalFuzzTarget` / `InternalExample` slices, and calls `testing.MainStart` in `main()`. TestMain is supported: if a `TestMain` function is detected, the generated code calls it with the `*testing.M` value and reads the exit code from its reflected `exitCode` field, matching the behavior of `go test`.\n\n## License\n\nWTFPL\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcookiengineer%2Fgotestwasm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcookiengineer%2Fgotestwasm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcookiengineer%2Fgotestwasm/lists"}