项目作者: gavv

项目描述 :
End-to-end HTTP and REST API testing for Go.
高级语言: Go
项目地址: git://github.com/gavv/httpexpect.git
创建时间: 2016-04-29T17:05:20Z
项目社区:https://github.com/gavv/httpexpect

开源协议:MIT License

下载


httpexpect GoDev Build Coveralls GitHub release Discord

Concise, declarative, and easy to use end-to-end HTTP and REST API testing for Go (golang).

Basically, httpexpect is a set of chainable builders for HTTP requests and assertions for HTTP responses and payload, on top of net/http and several utility packages.

Workflow:

  • Incrementally build HTTP requests.
  • Inspect HTTP responses.
  • Inspect response payload recursively.

Features

Request builder
Response assertions
  • Response status, predefined status ranges.
  • Headers, cookies, payload: JSON, JSONP, forms, text.
  • Round-trip time.
  • Custom reusable response matchers.
Payload assertions
  • Type-specific assertions, supported types: object, array, string, number, boolean, null, datetime, duration, cookie.
  • Regular expressions.
  • Simple JSON queries (using subset of JSONPath), provided by jsonpath package.
  • JSON Schema validation, provided by gojsonschema package.
WebSocket support (thanks to @tyranron)
  • Upgrade an HTTP connection to a WebSocket connection (we use gorilla/websocket internally).
  • Interact with the WebSocket server.
  • Inspect WebSocket connection parameters and WebSocket messages.
Pretty printing
  • Verbose error messages.
  • JSON diff is produced on failure using gojsondiff package.
  • Failures are reported using testify (assert or require package) or standard testing package.
  • JSON values are pretty-printed using encoding/json, Go values are pretty-printed using litter.
  • Dumping requests and responses in various formats, using httputil, http2curl, or simple compact logger.
  • Printing stacktrace on failure in verbose or compact format.
  • Color support using fatih/color.
Tuning
  • Tests can communicate with server via real HTTP client or invoke net/http or fasthttp handler directly.
  • User can provide custom HTTP client, WebSocket dialer, HTTP request factory (e.g. from the Google App Engine testing).
  • User can configure redirect and retry policies and timeouts.
  • User can configure formatting options (what parts to display, how to format numbers, etc.) or provide custom templates based on text/template engine.
  • Custom handlers may be provided for logging, printing requests and responses, handling succeeded and failed assertions.

Versioning

The versions are selected according to the semantic versioning scheme. Every new major version gets its own stable branch with a backwards compatibility promise. Releases are tagged from stable branches.

Changelog file can be found here: changelog.

The current stable branch is v2:

  1. import "github.com/gavv/httpexpect/v2"

Documentation

Documentation is available on pkg.go.dev. It contains an overview and reference.

Community

Community forum and Q&A board is right on GitHub in discussions tab.

For more interactive discussion, you can join discord chat.

Contributing

Feel free to report bugs, suggest improvements, and send pull requests! Please add documentation and tests for new features.

This project highly depends on contributors. Thank you all for your amazing work!

If you would like to submit code, see HACKING.md.

Donating

If you would like to support my open-source work, you can do it here:

Thanks!

Examples

See _examples directory for complete standalone examples.

  • fruits_test.go

    Testing a simple CRUD server made with bare net/http.

  • iris_test.go

    Testing a server made with iris framework. Example includes JSON queries and validation, URL and form parameters, basic auth, sessions, and streaming. Tests invoke the http.Handler directly.

  • echo_test.go

    Testing a server with JWT authentication made with echo framework. Tests use either HTTP client or invoke the http.Handler directly.

  • gin_test.go

    Testing a server utilizing the gin web framework. Tests invoke the http.Handler directly.

  • fasthttp_test.go

    Testing a server made with fasthttp package. Tests invoke the fasthttp.RequestHandler directly.

  • websocket_test.go

    Testing a WebSocket server based on gorilla/websocket. Tests invoke the http.Handler or fasthttp.RequestHandler directly.

  • tls_test.go

    Testing a TLS server made with net/http and crypto/tls

  • oauth2_test.go

    Testing a OAuth2 server with oauth2.

  • gae_test.go

    Testing a server running under the Google App Engine.

  • formatter_test.go

    Testing with custom formatter for assertion messages.

Quick start

Hello, world!
  1. package example
  2. import (
  3. "net/http"
  4. "net/http/httptest"
  5. "testing"
  6. "github.com/gavv/httpexpect/v2"
  7. )
  8. func TestFruits(t *testing.T) {
  9. // create http.Handler
  10. handler := FruitsHandler()
  11. // run server using httptest
  12. server := httptest.NewServer(handler)
  13. defer server.Close()
  14. // create httpexpect instance
  15. e := httpexpect.Default(t, server.URL)
  16. // is it working?
  17. e.GET("/fruits").
  18. Expect().
  19. Status(http.StatusOK).JSON().Array().IsEmpty()
  20. }
JSON
  1. orange := map[string]interface{}{
  2. "weight": 100,
  3. }
  4. e.PUT("/fruits/orange").WithJSON(orange).
  5. Expect().
  6. Status(http.StatusNoContent).NoContent()
  7. e.GET("/fruits/orange").
  8. Expect().
  9. Status(http.StatusOK).
  10. JSON().Object().ContainsKey("weight").HasValue("weight", 100)
  11. apple := map[string]interface{}{
  12. "colors": []interface{}{"green", "red"},
  13. "weight": 200,
  14. }
  15. e.PUT("/fruits/apple").WithJSON(apple).
  16. Expect().
  17. Status(http.StatusNoContent).NoContent()
  18. obj := e.GET("/fruits/apple").
  19. Expect().
  20. Status(http.StatusOK).JSON().Object()
  21. obj.Keys().ContainsOnly("colors", "weight")
  22. obj.Value("colors").Array().ConsistsOf("green", "red")
  23. obj.Value("colors").Array().Value(0).String().IsEqual("green")
  24. obj.Value("colors").Array().Value(1).String().IsEqual("red")
  25. obj.Value("colors").Array().First().String().IsEqual("green")
  26. obj.Value("colors").Array().Last().String().IsEqual("red")
JSON Schema and JSON Path
  1. schema := `{
  2. "type": "array",
  3. "items": {
  4. "type": "object",
  5. "properties": {
  6. ...
  7. "private": {
  8. "type": "boolean"
  9. }
  10. }
  11. }
  12. }`
  13. repos := e.GET("/repos/octocat").
  14. Expect().
  15. Status(http.StatusOK).JSON()
  16. // validate JSON schema
  17. repos.Schema(schema)
  18. // run JSONPath query and iterate results
  19. for _, private := range repos.Path("$..private").Array().Iter() {
  20. private.Boolean().IsFalse()
  21. }
JSON decoding
  1. type User struct {
  2. Name string `json:"name"`
  3. Age int `json:"age"`
  4. Gender string `json:"gender"`
  5. }
  6. var user User
  7. e.GET("/user").
  8. Expect().
  9. Status(http.StatusOK).
  10. JSON().
  11. Decode(&user)
  12. if user.Name != "octocat" {
  13. t.Fail()
  14. }
Forms
  1. // post form encoded from struct or map
  2. e.POST("/form").WithForm(structOrMap).
  3. Expect().
  4. Status(http.StatusOK)
  5. // set individual fields
  6. e.POST("/form").WithFormField("foo", "hello").WithFormField("bar", 123).
  7. Expect().
  8. Status(http.StatusOK)
  9. // multipart form
  10. e.POST("/form").WithMultipart().
  11. WithFile("avatar", "./john.png").WithFormField("username", "john").
  12. Expect().
  13. Status(http.StatusOK)
URL construction
  1. // construct path using ordered parameters
  2. e.GET("/repos/{user}/{repo}", "octocat", "hello-world").
  3. Expect().
  4. Status(http.StatusOK)
  5. // construct path using named parameters
  6. e.GET("/repos/{user}/{repo}").
  7. WithPath("user", "octocat").WithPath("repo", "hello-world").
  8. Expect().
  9. Status(http.StatusOK)
  10. // set query parameters
  11. e.GET("/repos/{user}", "octocat").WithQuery("sort", "asc").
  12. Expect().
  13. Status(http.StatusOK) // "/repos/octocat?sort=asc"
Headers
  1. // set If-Match
  2. e.POST("/users/john").WithHeader("If-Match", etag).WithJSON(john).
  3. Expect().
  4. Status(http.StatusOK)
  5. // check ETag
  6. e.GET("/users/john").
  7. Expect().
  8. Status(http.StatusOK).Header("ETag").NotEmpty()
  9. // check Date
  10. t := time.Now()
  11. e.GET("/users/john").
  12. Expect().
  13. Status(http.StatusOK).Header("Date").AsDateTime().InRange(t, time.Now())
Cookies
  1. // set cookie
  2. t := time.Now()
  3. e.POST("/users/john").WithCookie("session", sessionID).WithJSON(john).
  4. Expect().
  5. Status(http.StatusOK)
  6. // check cookies
  7. c := e.GET("/users/john").
  8. Expect().
  9. Status(http.StatusOK).Cookie("session")
  10. c.Value().IsEqual(sessionID)
  11. c.Domain().IsEqual("example.com")
  12. c.Path().IsEqual("/")
  13. c.Expires().InRange(t, t.Add(time.Hour * 24))
Regular expressions
  1. // simple match
  2. e.GET("/users/john").
  3. Expect().
  4. Header("Location").
  5. Match("http://(.+)/users/(.+)").Values("example.com", "john")
  6. // check capture groups by index or name
  7. m := e.GET("/users/john").
  8. Expect().
  9. Header("Location").Match("http://(?P<host>.+)/users/(?P<user>.+)")
  10. m.Submatch(0).IsEqual("http://example.com/users/john")
  11. m.Submatch(1).IsEqual("example.com")
  12. m.Submatch(2).IsEqual("john")
  13. m.NamedSubmatch("host").IsEqual("example.com")
  14. m.NamedSubmatch("user").IsEqual("john")
Redirection support
  1. e.POST("/path").
  2. WithRedirectPolicy(httpexpect.FollowAllRedirects).
  3. WithMaxRedirects(5).
  4. Expect().
  5. Status(http.StatusOK)
  6. e.POST("/path").
  7. WithRedirectPolicy(httpexpect.DontFollowRedirects).
  8. Expect().
  9. Status(http.StatusPermanentRedirect)
Retry support
  1. // default retry policy
  2. e.POST("/path").
  3. WithMaxRetries(5).
  4. Expect().
  5. Status(http.StatusOK)
  6. // custom built-in retry policy
  7. e.POST("/path").
  8. WithMaxRetries(5).
  9. WithRetryPolicy(httpexpect.RetryAllErrors).
  10. Expect().
  11. Status(http.StatusOK)
  12. // custom retry delays
  13. e.POST("/path").
  14. WithMaxRetries(5).
  15. WithRetryDelay(time.Second, time.Minute).
  16. Expect().
  17. Status(http.StatusOK)
  18. // custom user-defined retry policy
  19. e.POST("/path").
  20. WithMaxRetries(5).
  21. WithRetryPolicyFunc(func(resp *http.Response, err error) bool {
  22. return resp.StatusCode == http.StatusTeapot
  23. }).
  24. Expect().
  25. Status(http.StatusOK)
Subdomains and per-request URL
  1. e.GET("/path").WithURL("http://example.com").
  2. Expect().
  3. Status(http.StatusOK)
  4. e.GET("/path").WithURL("http://subdomain.example.com").
  5. Expect().
  6. Status(http.StatusOK)
WebSocket support
  1. ws := e.GET("/mysocket").WithWebsocketUpgrade().
  2. Expect().
  3. Status(http.StatusSwitchingProtocols).
  4. Websocket()
  5. defer ws.Disconnect()
  6. ws.WriteText("some request").
  7. Expect().
  8. TextMessage().Body().IsEqual("some response")
  9. ws.CloseWithText("bye").
  10. Expect().
  11. CloseMessage().NoContent()
Reusable builders
  1. e := httpexpect.Default(t, "http://example.com")
  2. r := e.POST("/login").WithForm(Login{"ford", "betelgeuse7"}).
  3. Expect().
  4. Status(http.StatusOK).JSON().Object()
  5. token := r.Value("token").String().Raw()
  6. auth := e.Builder(func (req *httpexpect.Request) {
  7. req.WithHeader("Authorization", "Bearer "+token)
  8. })
  9. auth.GET("/restricted").
  10. Expect().
  11. Status(http.StatusOK)
  12. e.GET("/restricted").
  13. Expect().
  14. Status(http.StatusUnauthorized)
Reusable matchers
  1. e := httpexpect.Default(t, "http://example.com")
  2. // every response should have this header
  3. m := e.Matcher(func (resp *httpexpect.Response) {
  4. resp.Header("API-Version").NotEmpty()
  5. })
  6. m.GET("/some-path").
  7. Expect().
  8. Status(http.StatusOK)
  9. m.GET("/bad-path").
  10. Expect().
  11. Status(http.StatusNotFound)
Request transformers
  1. e := httpexpect.Default(t, "http://example.com")
  2. myTranform := func(r* http.Request) {
  3. // modify the underlying http.Request
  4. }
  5. // apply transformer to a single request
  6. e.POST("/some-path").
  7. WithTransformer(myTranform).
  8. Expect().
  9. Status(http.StatusOK)
  10. // create a builder that applies transfromer to every request
  11. myBuilder := e.Builder(func (req *httpexpect.Request) {
  12. req.WithTransformer(myTranform)
  13. })
  14. myBuilder.POST("/some-path").
  15. Expect().
  16. Status(http.StatusOK)
Shared environment
  1. e := httpexpect.Default(t, "http://example.com")
  2. t.Run("/users", func(t *testing.T) {
  3. obj := e.GET("/users").
  4. Expect().
  5. Status(http.StatusOK).JSON().Object()
  6. // store user id for next tests
  7. userID := obj.Path("$.users[1].id").String().Raw()
  8. e.Env().Put("user1.id", userID)
  9. })
  10. t.Run("/user/{userId}", func(t *testing.T) {
  11. // read user id from previous tests
  12. userID := e.Env().GetString("user1.id")
  13. e.GET("/user/{userId}").
  14. WithPath("userId", userID)
  15. Expect().
  16. Status(http.StatusOK)
  17. })
Custom config
  1. e := httpexpect.WithConfig(httpexpect.Config{
  2. // include test name in failures (optional)
  3. TestName: t.Name(),
  4. // prepend this url to all requests
  5. BaseURL: "http://example.com",
  6. // use http.Client with a cookie jar and timeout
  7. Client: &http.Client{
  8. Jar: httpexpect.NewCookieJar(),
  9. Timeout: time.Second * 30,
  10. },
  11. // use fatal failures
  12. Reporter: httpexpect.NewRequireReporter(t),
  13. // print all requests and responses
  14. Printers: []httpexpect.Printer{
  15. httpexpect.NewDebugPrinter(t, true),
  16. },
  17. })
Use HTTP handler directly
  1. // invoke http.Handler directly using httpexpect.Binder
  2. var handler http.Handler = myHandler()
  3. e := httpexpect.WithConfig(httpexpect.Config{
  4. // prepend this url to all requests, required for cookies
  5. // to be handled correctly
  6. BaseURL: "http://example.com",
  7. Reporter: httpexpect.NewAssertReporter(t),
  8. Client: &http.Client{
  9. Transport: httpexpect.NewBinder(handler),
  10. Jar: httpexpect.NewCookieJar(),
  11. },
  12. })
  13. // invoke fasthttp.RequestHandler directly using httpexpect.FastBinder
  14. var handler fasthttp.RequestHandler = myHandler()
  15. e := httpexpect.WithConfig(httpexpect.Config{
  16. // prepend this url to all requests, required for cookies
  17. // to be handled correctly
  18. BaseURL: "http://example.com",
  19. Reporter: httpexpect.NewAssertReporter(t),
  20. Client: &http.Client{
  21. Transport: httpexpect.NewFastBinder(handler),
  22. Jar: httpexpect.NewCookieJar(),
  23. },
  24. })
Per-request client or handler
  1. e := httpexpect.Default(t, server.URL)
  2. client := &http.Client{
  3. Transport: &http.Transport{
  4. DisableCompression: true,
  5. },
  6. }
  7. // overwrite client
  8. e.GET("/path").WithClient(client).
  9. Expect().
  10. Status(http.StatusOK)
  11. // construct client that invokes a handler directly and overwrite client
  12. e.GET("/path").WithHandler(handler).
  13. Expect().
  14. Status(http.StatusOK)
WebSocket dialer
  1. // invoke http.Handler directly using websocket.Dialer
  2. var handler http.Handler = myHandler()
  3. e := httpexpect.WithConfig(httpexpect.Config{
  4. BaseURL: "http://example.com",
  5. Reporter: httpexpect.NewAssertReporter(t),
  6. WebsocketDialer: httpexpect.NewWebsocketDialer(handler),
  7. })
  8. // invoke fasthttp.RequestHandler directly using websocket.Dialer
  9. var handler fasthttp.RequestHandler = myHandler()
  10. e := httpexpect.WithConfig(httpexpect.Config{
  11. BaseURL: "http://example.com",
  12. Reporter: httpexpect.NewAssertReporter(t),
  13. WebsocketDialer: httpexpect.NewFastWebsocketDialer(handler),
  14. })
Session support
  1. // cookie jar is used to store cookies from server
  2. e := httpexpect.WithConfig(httpexpect.Config{
  3. Reporter: httpexpect.NewAssertReporter(t),
  4. Client: &http.Client{
  5. Jar: httpexpect.NewCookieJar(), // used by default if Client is nil
  6. },
  7. })
  8. // cookies are disabled
  9. e := httpexpect.WithConfig(httpexpect.Config{
  10. Reporter: httpexpect.NewAssertReporter(t),
  11. Client: &http.Client{
  12. Jar: nil,
  13. },
  14. })
TLS support
  1. // use TLS with http.Transport
  2. e := httpexpect.WithConfig(httpexpect.Config{
  3. Reporter: httpexpect.NewAssertReporter(t),
  4. Client: &http.Client{
  5. Transport: &http.Transport{
  6. TLSClientConfig: &tls.Config{
  7. // accept any certificate; for testing only!
  8. InsecureSkipVerify: true,
  9. },
  10. },
  11. },
  12. })
  13. // use TLS with http.Handler
  14. e := httpexpect.WithConfig(httpexpect.Config{
  15. Reporter: httpexpect.NewAssertReporter(t),
  16. Client: &http.Client{
  17. Transport: &httpexpect.Binder{
  18. Handler: myHandler,
  19. TLS: &tls.ConnectionState{},
  20. },
  21. },
  22. })
Proxy support
  1. e := httpexpect.WithConfig(httpexpect.Config{
  2. Reporter: httpexpect.NewAssertReporter(t),
  3. Client: &http.Client{
  4. Transport: &http.Transport{
  5. Proxy: http.ProxyURL("http://proxy.example.com"),
  6. },
  7. },
  8. })
Global timeout/cancellation
  1. handler := FruitsHandler()
  2. server := httptest.NewServer(handler)
  3. defer server.Close()
  4. ctx, cancel := context.WithCancel(context.Background())
  5. e := WithConfig(Config{
  6. BaseURL: server.URL,
  7. Reporter: httpexpect.NewAssertReporter(t),
  8. Context: ctx,
  9. })
  10. go func() {
  11. time.Sleep(time.Duration(5)*time.Second)
  12. cancel()
  13. }()
  14. e.GET("/fruits").
  15. Expect().
  16. Status(http.StatusOK)
Per-request timeout/cancellation
  1. // per-request context
  2. e.GET("/fruits").
  3. WithContext(context.TODO()).
  4. Expect().
  5. Status(http.StatusOK)
  6. // per-request timeout
  7. e.GET("/fruits").
  8. WithTimeout(time.Duration(5)*time.Second).
  9. Expect().
  10. Status(http.StatusOK)
  11. // timeout combined with retries (timeout applies to each try)
  12. e.POST("/fruits").
  13. WithMaxRetries(5).
  14. WithTimeout(time.Duration(10)*time.Second).
  15. Expect().
  16. Status(http.StatusOK)
Choosing failure reporter
  1. // default reporter, uses testify/assert
  2. // failures don't terminate test immediately, but mark test as failed
  3. e := httpexpect.WithConfig(httpexpect.Config{
  4. Reporter: httpexpect.NewAssertReporter(t),
  5. })
  6. // uses testify/require
  7. // failures terminate test immediately
  8. e := httpexpect.WithConfig(httpexpect.Config{
  9. Reporter: httpexpect.NewRequireReporter(t),
  10. })
  11. // if you're using bare testing.T without testify
  12. e := httpexpect.WithConfig(httpexpect.Config{
  13. Reporter: t,
  14. })
  15. // if you're using bare testing.T and want failures to terminate test immediately
  16. e := httpexpect.WithConfig(httpexpect.Config{
  17. Reporter: httpexpect.NewFatalReporter(t),
  18. })
  19. // if you want fatal failures triggered from other goroutines
  20. e := httpexpect.WithConfig(httpexpect.Config{
  21. Reporter: httpexpect.NewPanicReporter(t),
  22. })
Assigning names to requests
  1. // when the tests fails, assertion message will mention request name:
  2. // request name: Get Fruits
  3. e.GET("/fruits").
  4. WithName("Get Fruits")
  5. Expect().
  6. Status(http.StatusOK).JSON().Array().IsEmpty()
Assigning aliases to values
  1. // when the tests fails, assertion path in the failure message is:
  2. // assertion: Request("GET").Expect().JSON().Array().IsEmpty()
  3. e.GET("/fruits").
  4. Expect().
  5. Status(http.StatusOK).JSON().Array().IsEmpty()
  6. // assign alias "fruits" to the Array variable
  7. fruits := e.GET("/fruits").
  8. Expect().
  9. Status(http.StatusOK).JSON().Array().Alias("fruits")
  10. // assertion path in the failure message is now:
  11. // assertion: fruits.IsEmpty()
  12. fruits.IsEmpty()
Printing requests and responses
  1. // print requests in short form, don't print responses
  2. e := httpexpect.WithConfig(httpexpect.Config{
  3. Reporter: httpexpect.NewAssertReporter(t),
  4. Printers: []httpexpect.Printer{
  5. httpexpect.NewCompactPrinter(t),
  6. },
  7. })
  8. // print requests as curl commands that can be inserted into terminal
  9. e := httpexpect.WithConfig(httpexpect.Config{
  10. Reporter: httpexpect.NewAssertReporter(t),
  11. Printers: []httpexpect.Printer{
  12. httpexpect.NewCurlPrinter(t),
  13. },
  14. })
  15. // print requests and responses in verbose form
  16. // also print all incoming and outgoing websocket messages
  17. e := httpexpect.WithConfig(httpexpect.Config{
  18. Reporter: httpexpect.NewAssertReporter(t),
  19. Printers: []httpexpect.Printer{
  20. httpexpect.NewDebugPrinter(t, true),
  21. },
  22. })
Customize failure formatting
  1. // change formatting options
  2. e := httpexpect.WithConfig(httpexpect.Config{
  3. Reporter: httpexpect.NewAssertReporter(t),
  4. Formatter: &httpexpect.DefaultFormatter{
  5. DisablePaths: true,
  6. DisableDiffs: true,
  7. FloatFormat: httpexpect.FloatFormatScientific,
  8. ColorMode: httpexpect.ColorModeNever,
  9. LineWidth: 80,
  10. },
  11. })
  12. // provide custom templates
  13. e := httpexpect.WithConfig(httpexpect.Config{
  14. Reporter: httpexpect.NewAssertReporter(t),
  15. Formatter: &httpexpect.DefaultFormatter{
  16. SuccessTemplate: "...",
  17. FailureTemplate: "...",
  18. TemplateFuncs: template.FuncMap{ ... },
  19. },
  20. })
  21. // provide custom formatter
  22. e := httpexpect.WithConfig(httpexpect.Config{
  23. Reporter: httpexpect.NewAssertReporter(t),
  24. Formatter: &MyFormatter{},
  25. })
Customize assertion handling
  1. // enable printing of succeeded assertions
  2. e := httpexpect.WithConfig(httpexpect.Config{
  3. AssertionHandler: &httpexpect.DefaultAssertionHandler{
  4. Formatter: &httpexpect.DefaultFormatter{},
  5. Reporter: httpexpect.NewAssertReporter(t),
  6. Logger: t, // specify logger to enable printing of succeeded assertions
  7. },
  8. })
  9. // provide custom assertion handler
  10. // here you can implement custom handling of succeeded and failed assertions
  11. // this may be useful for integrating httpexpect with other testing libs
  12. // if desired, you can completely ignore builtin Formatter, Reporter, and Logger
  13. e := httpexpect.WithConfig(httpexpect.Config{
  14. AssertionHandler: &MyAssertionHandler{},
  15. })

Environment variables

The following environment variables are checked when ColorModeAuto is used:

  • FORCE_COLOR - if set to a positive integers, colors are enabled
  • NO_COLOR - if set to non-empty string, colors are disabled (see also)
  • TERM - if starts with dumb, colors are disabled

Similar packages

Authors

List of contributors can be found here.

If your name is missing or you want to change its appearance, feel free to submit PR!

License

MIT