diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index b6a5d93..0fc5312 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -15,7 +15,7 @@
"--security-opt",
"seccomp=unconfined"
],
- "postCreateCommand": "cargo install cross typos-cli cargo-expand",
+ "postCreateCommand": "cargo install cross typos-cli cargo-expand cargo-insta",
"customizations": {
"vscode": {
// Add the IDs of extensions you want installed when the container is created.
diff --git a/.github/release-body.md b/.github/release-body.md
index 5b199a1..cae1958 100644
--- a/.github/release-body.md
+++ b/.github/release-body.md
@@ -1,18 +1,21 @@
-### 2025-02-23
+### 2025-04-18
### Chores
-+ dependencies updated, [e5f355a1928f78abdb64e4c5617d6fac06340016], [4539d8ad0705b46d7c89c51c7be482b696d26e5f], [6aee6181136235a1a4f79af9b9748c1801be8bf8], [64d1bdf2bf88407e02f0eded1e03fcfc5ee2d8e3]
-+ .devcontainer dependencies updated, [5c8e76e7bb4d7aab8543c9be09fdbc4ffa446b10]
-+ example docker-compose.yml updated, [2354b0b9be1ab3795a421512594b2650b9cbdd74]
-+ Rust 1.84 linting, [3065265e26c30d78ba738cfe731d3901ec1948d0]
++ dependencies updated, [8f959c5408995527485e817514c3f4a10bca31bd], [69d1801ea1e71e7d84c00fb2142ba27577e3bb73], [188490e13fd1255eecb305ac0a99a7a1913e0294]
++ Rust 1.86.0 linting, [9acf60334c5224faa9ee4ecf7030b2e9b13b7d67]
-### Features
-+ Config file introduced, including customizing color scheme of application, closes #47, [f4d54e1ba8ea1516394aef19511a63e6271f27bf]
-+ Enable log timestamps to be set to any given timezone, plus custom timestamp format via the config file, closes #56, [7a5e7a25873d2c270e5808730721ebb5427a051]
-+ update Rust edition to 2024, [7e4a960b888f1dab524d6045504162cea1171d20]
+### Docs
++ comment typo, [723b220c6aa6393b8eebd84a6ddba69f35dc18b8]
### Fixes
-+ Only draw screen if data or layout has changed, drastically reduces CPU usage, [bfc295c50e982886ccaa5e60b57f10d3690b3f09]
-
++ github workflow update, [997eebca20a2883dcfcb35009dd4d7b0d438dec6]
++ config merging, [a468827f02c5243b9bd4e0183fed16854ae6c851]
+
+### Refactors
++ rename ChartType to ChartVariant, [bca67116f3b71451156a39a7b0957568b26fa183], [d0caa9271b6f92f52ccfe3dec69708efe54e5170]
++ rename FileType to FileFormat, [848f64d0da429e547a2f6c8de62e4da5f5c9a187]
+
+### Tests
++ Use insta, closes #57, [9362d7b481ea22eab6f902dc7f3c10150c7ddf22]
see CHANGELOG.md for more details
diff --git a/.github/workflows/create_release_and_build.yml b/.github/workflows/create_release_and_build.yml
index ce686d8..67e569a 100644
--- a/.github/workflows/create_release_and_build.yml
+++ b/.github/workflows/create_release_and_build.yml
@@ -88,29 +88,12 @@ jobs:
artifacts: |
**/oxker_*.zip
**/oxker_*.tar.gz
-
- ##################
- ## Cargo publish #
- ##################
-
- cargo_publish:
- needs: [create_release]
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: publish to crates.io
- uses: katyo/publish-crates@v2
- with:
- registry-token: ${{ secrets.CRATES_IO_TOKEN }}
-
#########################################
## Build images for Dockerhub & ghcr.io #
#########################################
- image_build:
- needs: [cargo_publish]
+ container_image_build:
+ needs: [create_release]
runs-on: ubuntu-latest
steps:
- name: Checkout code
@@ -148,3 +131,31 @@ jobs:
--provenance=false --sbom=false \
--push \
-f containerised/Dockerfile .
+ ########################
+ # Publish to crates.io #
+ ########################
+
+ # This could be moved to before a github release is made
+ dry_run_cargo_publish:
+ runs-on: ubuntu-latest
+ needs: [container_image_build]
+ steps:
+ - name: update rust stable
+ run: rustup update stable
+ - uses: actions/checkout@v4
+ - name: Publish Dry Run
+ run: cargo publish --dry-run
+
+ cargo_publish:
+ environment: crates.io
+ runs-on: ubuntu-latest
+ needs: [dry_run_cargo_publish]
+ env:
+ CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
+ steps:
+ - name: update rust stable
+ run: rustup update stable
+ - uses: actions/checkout@v4
+ - name: Publish
+ run: cargo publish
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6de7d30..73cf283 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,24 @@
+# v0.10.1
+### 2025-04-18
+
+### Chores
++ dependencies updated, [8f959c54](https://github.com/mrjackwills/oxker/commit/8f959c5408995527485e817514c3f4a10bca31bd), [69d1801e](https://github.com/mrjackwills/oxker/commit/69d1801ea1e71e7d84c00fb2142ba27577e3bb73), [188490e1](https://github.com/mrjackwills/oxker/commit/188490e13fd1255eecb305ac0a99a7a1913e0294)
++ Rust 1.86.0 linting, [9acf6033](https://github.com/mrjackwills/oxker/commit/9acf60334c5224faa9ee4ecf7030b2e9b13b7d67)
+
+### Docs
++ comment typo, [723b220c](https://github.com/mrjackwills/oxker/commit/723b220c6aa6393b8eebd84a6ddba69f35dc18b8)
+
+### Fixes
++ github workflow update, [997eebca](https://github.com/mrjackwills/oxker/commit/997eebca20a2883dcfcb35009dd4d7b0d438dec6)
++ config merging, [a468827f](https://github.com/mrjackwills/oxker/commit/a468827f02c5243b9bd4e0183fed16854ae6c851)
+
+### Refactors
++ rename ChartType to ChartVariant, [bca67116](https://github.com/mrjackwills/oxker/commit/bca67116f3b71451156a39a7b0957568b26fa183), [d0caa927](https://github.com/mrjackwills/oxker/commit/d0caa9271b6f92f52ccfe3dec69708efe54e5170)
++ rename FileType to FileFormat, [848f64d0](https://github.com/mrjackwills/oxker/commit/848f64d0da429e547a2f6c8de62e4da5f5c9a187)
+
+### Tests
++ Use insta, closes [#57](https://github.com/mrjackwills/oxker/issues/57), [9362d7b4](https://github.com/mrjackwills/oxker/commit/9362d7b481ea22eab6f902dc7f3c10150c7ddf22)
+
# v0.10.0
### 2025-02-23
diff --git a/Cargo.lock b/Cargo.lock
index 223b54e..5d1571f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -90,9 +90,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.96"
+version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "autocfg"
@@ -123,9 +123,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
-version = "2.8.0"
+version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
+checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "bollard"
@@ -177,17 +177,11 @@ version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
-[[package]]
-name = "byteorder"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
-
[[package]]
name = "bytes"
-version = "1.10.0"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cansi"
@@ -212,9 +206,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.2.15"
+version = "1.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
+checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
dependencies = [
"shlex",
]
@@ -227,22 +221,22 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
-version = "0.4.39"
+version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
+checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
dependencies = [
"android-tzdata",
"iana-time-zone",
"num-traits",
"serde",
- "windows-targets",
+ "windows-link",
]
[[package]]
name = "clap"
-version = "4.5.30"
+version = "4.5.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d"
+checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04"
dependencies = [
"clap_builder",
"clap_derive",
@@ -250,9 +244,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.30"
+version = "4.5.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c"
+checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5"
dependencies = [
"anstream",
"anstyle",
@@ -264,9 +258,9 @@ dependencies = [
[[package]]
name = "clap_derive"
-version = "4.5.28"
+version = "4.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
+checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
dependencies = [
"heck",
"proc-macro2",
@@ -300,6 +294,27 @@ dependencies = [
"static_assertions",
]
+[[package]]
+name = "console"
+version = "0.15.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
+dependencies = [
+ "encode_unicode",
+ "libc",
+ "once_cell",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "convert_case"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
+dependencies = [
+ "unicode-segmentation",
+]
+
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
@@ -316,7 +331,25 @@ dependencies = [
"crossterm_winapi",
"mio",
"parking_lot",
- "rustix",
+ "rustix 0.38.44",
+ "signal-hook",
+ "signal-hook-mio",
+ "winapi",
+]
+
+[[package]]
+name = "crossterm"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
+dependencies = [
+ "bitflags",
+ "crossterm_winapi",
+ "derive_more",
+ "document-features",
+ "mio",
+ "parking_lot",
+ "rustix 1.0.5",
"signal-hook",
"signal-hook-mio",
"winapi",
@@ -333,9 +366,9 @@ dependencies = [
[[package]]
name = "darling"
-version = "0.20.10"
+version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
+checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
@@ -343,9 +376,9 @@ dependencies = [
[[package]]
name = "darling_core"
-version = "0.20.10"
+version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
+checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
@@ -357,9 +390,9 @@ dependencies = [
[[package]]
name = "darling_macro"
-version = "0.20.10"
+version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
+checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
@@ -368,14 +401,35 @@ dependencies = [
[[package]]
name = "deranged"
-version = "0.3.11"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
dependencies = [
"powerfmt",
"serde",
]
+[[package]]
+name = "derive_more"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
+dependencies = [
+ "derive_more-impl",
+]
+
+[[package]]
+name = "derive_more-impl"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "directories"
version = "6.0.0"
@@ -409,10 +463,25 @@ dependencies = [
]
[[package]]
-name = "either"
-version = "1.13.0"
+name = "document-features"
+version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
+dependencies = [
+ "litrs",
+]
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "encode_unicode"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
name = "equivalent"
@@ -422,9 +491,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
-version = "0.3.10"
+version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
+checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [
"libc",
"windows-sys 0.59.0",
@@ -438,9 +507,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
-version = "0.1.4"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "form_urlencoded"
@@ -516,14 +585,14 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.3.1"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
+checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
dependencies = [
"cfg-if",
"libc",
- "wasi 0.13.3+wasi-0.2.2",
- "windows-targets",
+ "r-efi",
+ "wasi 0.14.2+wasi-0.2.4",
]
[[package]]
@@ -563,9 +632,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "http"
-version = "1.2.0"
+version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
+checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
dependencies = [
"bytes",
"fnv",
@@ -584,12 +653,12 @@ dependencies = [
[[package]]
name = "http-body-util"
-version = "0.1.2"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
+checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [
"bytes",
- "futures-util",
+ "futures-core",
"http",
"http-body",
"pin-project-lite",
@@ -597,9 +666,9 @@ dependencies = [
[[package]]
name = "httparse"
-version = "1.10.0"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "httpdate"
@@ -644,9 +713,9 @@ dependencies = [
[[package]]
name = "hyper-util"
-version = "0.1.10"
+version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
+checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2"
dependencies = [
"bytes",
"futures-channel",
@@ -654,6 +723,7 @@ dependencies = [
"http",
"http-body",
"hyper",
+ "libc",
"pin-project-lite",
"socket2",
"tokio",
@@ -678,14 +748,15 @@ dependencies = [
[[package]]
name = "iana-time-zone"
-version = "0.1.61"
+version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
+checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
+ "log",
"wasm-bindgen",
"windows-core",
]
@@ -740,9 +811,9 @@ dependencies = [
[[package]]
name = "icu_locid_transform_data"
-version = "1.5.0"
+version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
[[package]]
name = "icu_normalizer"
@@ -764,9 +835,9 @@ dependencies = [
[[package]]
name = "icu_normalizer_data"
-version = "1.5.0"
+version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
[[package]]
name = "icu_properties"
@@ -785,9 +856,9 @@ dependencies = [
[[package]]
name = "icu_properties_data"
-version = "1.5.0"
+version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
[[package]]
name = "icu_provider"
@@ -857,9 +928,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.7.1"
+version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
+checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown 0.15.2",
@@ -868,9 +939,22 @@ dependencies = [
[[package]]
name = "indoc"
-version = "2.0.5"
+version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
+checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
+
+[[package]]
+name = "insta"
+version = "1.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50259abbaa67d11d2bcafc7ba1d094ed7a0c70e3ce893f0d0997f73558cb3084"
+dependencies = [
+ "console",
+ "linked-hash-map",
+ "once_cell",
+ "pin-project",
+ "similar",
+]
[[package]]
name = "instability"
@@ -902,16 +986,17 @@ dependencies = [
[[package]]
name = "itoa"
-version = "1.0.14"
+version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jiff"
-version = "0.2.1"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3590fea8e9e22d449600c9bbd481a8163bef223e4ff938e5f55899f8cf1adb93"
+checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0"
dependencies = [
+ "jiff-static",
"jiff-tzdb",
"jiff-tzdb-platform",
"log",
@@ -922,16 +1007,27 @@ dependencies = [
]
[[package]]
-name = "jiff-tzdb"
-version = "0.1.2"
+name = "jiff-static"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf2cec2f5d266af45a071ece48b1fb89f3b00b2421ac3a5fe10285a6caaa60d3"
+checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "jiff-tzdb"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524"
[[package]]
name = "jiff-tzdb-platform"
-version = "0.1.2"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a63c62e404e7b92979d2792352d885a7f8f83fd1d0d31eea582d77b2ceca697e"
+checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
dependencies = [
"jiff-tzdb",
]
@@ -954,9 +1050,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
-version = "0.2.169"
+version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
+checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libredox"
@@ -968,6 +1064,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "linked-hash-map"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
+
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
@@ -975,10 +1077,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
-name = "litemap"
-version = "0.7.4"
+name = "linux-raw-sys"
+version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
+checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
+
+[[package]]
+name = "litemap"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
+
+[[package]]
+name = "litrs"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
[[package]]
name = "lock_api"
@@ -992,9 +1106,9 @@ dependencies = [
[[package]]
name = "log"
-version = "0.4.26"
+version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "lru"
@@ -1013,9 +1127,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
-version = "0.8.5"
+version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
+checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]
@@ -1068,9 +1182,9 @@ dependencies = [
[[package]]
name = "once_cell"
-version = "1.20.3"
+version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "option-ext"
@@ -1086,15 +1200,16 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "oxker"
-version = "0.10.0"
+version = "0.10.1"
dependencies = [
"anyhow",
"bollard",
"cansi",
"clap",
- "crossterm",
+ "crossterm 0.29.0",
"directories",
"futures-util",
+ "insta",
"jiff",
"parking_lot",
"ratatui",
@@ -1144,6 +1259,26 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+[[package]]
+name = "pin-project"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "pin-project-lite"
version = "0.2.16"
@@ -1158,9 +1293,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "portable-atomic"
-version = "1.10.0"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
+checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
[[package]]
name = "portable-atomic-util"
@@ -1179,40 +1314,45 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
-version = "0.2.20"
+version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
- "zerocopy 0.7.35",
+ "zerocopy",
]
[[package]]
name = "proc-macro2"
-version = "1.0.93"
+version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.38"
+version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
-name = "rand"
-version = "0.9.0"
+name = "r-efi"
+version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
+checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+
+[[package]]
+name = "rand"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"rand_chacha",
"rand_core",
- "zerocopy 0.8.20",
]
[[package]]
@@ -1227,12 +1367,11 @@ dependencies = [
[[package]]
name = "rand_core"
-version = "0.9.1"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a88e0da7a2c97baa202165137c158d0a2e824ac465d13d81046727b34cb247d3"
+checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
- "getrandom 0.3.1",
- "zerocopy 0.8.20",
+ "getrandom 0.3.2",
]
[[package]]
@@ -1244,7 +1383,7 @@ dependencies = [
"bitflags",
"cassowary",
"compact_str",
- "crossterm",
+ "crossterm 0.28.1",
"indoc",
"instability",
"itertools",
@@ -1258,9 +1397,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.5.9"
+version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
+checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
dependencies = [
"bitflags",
]
@@ -1291,21 +1430,34 @@ dependencies = [
"bitflags",
"errno",
"libc",
- "linux-raw-sys",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustix"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.9.4",
"windows-sys 0.59.0",
]
[[package]]
name = "rustversion"
-version = "1.0.19"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
+checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
[[package]]
name = "ryu"
-version = "1.0.19"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "scopeguard"
@@ -1315,18 +1467,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
-version = "1.0.218"
+version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
+checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.218"
+version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
+checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
@@ -1335,9 +1487,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.139"
+version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
+checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
@@ -1358,9 +1510,9 @@ dependencies = [
[[package]]
name = "serde_repr"
-version = "0.1.19"
+version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
+checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
dependencies = [
"proc-macro2",
"quote",
@@ -1398,7 +1550,7 @@ dependencies = [
"chrono",
"hex",
"indexmap 1.9.3",
- "indexmap 2.7.1",
+ "indexmap 2.9.0",
"serde",
"serde_derive",
"serde_json",
@@ -1450,6 +1602,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "similar"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
+
[[package]]
name = "slab"
version = "0.4.9"
@@ -1461,15 +1619,15 @@ dependencies = [
[[package]]
name = "smallvec"
-version = "1.14.0"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
+checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "socket2"
-version = "0.5.8"
+version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
+checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
dependencies = [
"libc",
"windows-sys 0.52.0",
@@ -1517,9 +1675,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.98"
+version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
+checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
dependencies = [
"proc-macro2",
"quote",
@@ -1539,18 +1697,18 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "2.0.11"
+version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "2.0.11"
+version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
@@ -1569,9 +1727,9 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.37"
+version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
+checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
dependencies = [
"deranged",
"itoa",
@@ -1584,15 +1742,15 @@ dependencies = [
[[package]]
name = "time-core"
-version = "0.1.2"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
[[package]]
name = "time-macros"
-version = "0.2.19"
+version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
+checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
dependencies = [
"num-conv",
"time-core",
@@ -1610,9 +1768,9 @@ dependencies = [
[[package]]
name = "tokio"
-version = "1.43.0"
+version = "1.44.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
+checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
dependencies = [
"backtrace",
"bytes",
@@ -1639,9 +1797,9 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.7.13"
+version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
+checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034"
dependencies = [
"bytes",
"futures-core",
@@ -1677,7 +1835,7 @@ version = "0.22.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
dependencies = [
- "indexmap 2.7.1",
+ "indexmap 2.9.0",
"serde",
"serde_spanned",
"toml_datetime",
@@ -1761,9 +1919,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]]
name = "unicode-ident"
-version = "1.0.17"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-segmentation"
@@ -1825,11 +1983,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
-version = "1.14.0"
+version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93d59ca99a559661b96bf898d8fce28ed87935fd2bea9f05983c1464dd6c71b1"
+checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
dependencies = [
- "getrandom 0.3.1",
+ "getrandom 0.3.2",
"rand",
]
@@ -1856,9 +2014,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
-version = "0.13.3+wasi-0.2.2"
+version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
+checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
@@ -1945,11 +2103,61 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
-version = "0.52.0"
+version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
- "windows-targets",
+ "windows-implement",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+
+[[package]]
+name = "windows-result"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
+dependencies = [
+ "windows-link",
]
[[package]]
@@ -2036,18 +2244,18 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
-version = "0.7.3"
+version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1"
+checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
-version = "0.33.0"
+version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
+checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]
@@ -2090,39 +2298,18 @@ dependencies = [
[[package]]
name = "zerocopy"
-version = "0.7.35"
+version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
dependencies = [
- "byteorder",
- "zerocopy-derive 0.7.35",
-]
-
-[[package]]
-name = "zerocopy"
-version = "0.8.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dde3bb8c68a8f3f1ed4ac9221aad6b10cece3e60a8e2ea54a6a2dec806d0084c"
-dependencies = [
- "zerocopy-derive 0.8.20",
+ "zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.7.35"
+version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.8.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eea57037071898bf96a6da35fd626f4f27e9cee3ead2a6c703cf09d472b2e700"
+checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
dependencies = [
"proc-macro2",
"quote",
@@ -2131,18 +2318,18 @@ dependencies = [
[[package]]
name = "zerofrom"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
diff --git a/Cargo.toml b/Cargo.toml
index b62e7c1..699e9f1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "oxker"
-version = "0.10.0"
+version = "0.10.1"
edition = "2024"
authors = ["Jack Wills "]
description = "A simple tui to view & control docker containers"
@@ -30,7 +30,7 @@ anyhow = "1.0"
bollard = "0.18"
cansi = "2.2"
clap = { version = "4.5", features = ["color", "derive", "unicode"] }
-crossterm = "0.28"
+crossterm = "0.29"
directories = "6.0"
futures-util = "0.3"
jiff = { version = "0.2", features = ["tzdb-bundle-always"] }
@@ -39,13 +39,12 @@ ratatui = "0.29"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_jsonc = "1.0"
-tokio = { version = "1.43", features = ["full"] }
+tokio = { version = "1.44", features = ["full"] }
tokio-util = "0.7"
toml = { version = "0.8", default-features = false, features = ["parse"] }
tracing = "0.1"
tracing-subscriber = "0.3"
-uuid = { version = "1.14", features = ["fast-rng", "v4"] }
-
+uuid = { version = "1.16", features = ["fast-rng", "v4"] }
[profile.release]
lto = true
@@ -53,3 +52,6 @@ codegen-units = 1
panic = 'abort'
strip = true
debug = false
+
+[dev-dependencies]
+insta = "1.42.2"
diff --git a/example_config/example.config.jsonc b/example_config/example.config.jsonc
index e3eeef1..f62f643 100644
--- a/example_config/example.config.jsonc
+++ b/example_config/example.config.jsonc
@@ -13,7 +13,7 @@
// Show self (the oxker container) when running as a docker container
"show_self": false,
// Show std_err in logs
- "show_std_err": false,
+ "show_std_err": true,
// Show a timestamp for every log entry
"show_timestamp": true,
// Don't draw gui - for debugging - mostly pointless
diff --git a/example_config/example.config.toml b/example_config/example.config.toml
index 8d9180a..b8d4534 100644
--- a/example_config/example.config.toml
+++ b/example_config/example.config.toml
@@ -16,7 +16,7 @@ raw_logs = false
show_self = false
# Show std_err in logs
-show_std_err = false
+show_std_err = true
# Show a timestamp for every log entry
show_timestamp = true
diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs
index d1d3786..fa9587f 100644
--- a/src/app_data/container_state.rs
+++ b/src/app_data/container_state.rs
@@ -35,7 +35,7 @@ impl ContainerId {
}
/// Only return first 8 chars of id, is usually more than enough for uniqueness
- /// TODO container id is a hex string, so can assume that 0..=8 will always return a 8 char ascii &str - need to update tests to use real ids, or atleast strings of the correct-ish length
+ /// need to update tests to use real ids, or atleast strings of the correct-ish length
pub fn get_short(&self) -> String {
self.0.chars().take(8).collect::()
}
@@ -481,7 +481,7 @@ impl ByteStats {
pub const fn new(value: u64) -> Self {
Self(value)
}
- pub fn update(&mut self, value: u64) {
+ pub const fn update(&mut self, value: u64) {
self.0 = value;
}
}
@@ -567,7 +567,7 @@ impl Logs {
pub fn insert(&mut self, line: ListItem<'static>, tz: LogsTz) {
if self.tz.insert(tz) {
self.logs.items.push(line);
- };
+ }
}
pub fn to_vec(&self) -> Vec> {
@@ -598,7 +598,7 @@ impl Logs {
self.logs.items.len()
}
- pub fn state(&mut self) -> &mut ListState {
+ pub const fn state(&mut self) -> &mut ListState {
&mut self.logs.state
}
}
diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs
index b852d33..cbbc0e3 100644
--- a/src/app_data/mod.rs
+++ b/src/app_data/mod.rs
@@ -237,7 +237,7 @@ impl AppData {
term.push(c);
} else {
self.filter.term = Some(format!("{c}"));
- };
+ }
self.filter_containers();
}
@@ -275,7 +275,7 @@ impl AppData {
while let Some(i) = self.hidden_containers.pop() {
if self.get_container_by_id(&i.id).is_none() {
self.containers.items.push(i);
- };
+ }
}
self.sort_containers();
}
@@ -420,6 +420,8 @@ impl AppData {
}
/// Get all the ContainerItems
+ /// Thnk this allow block can be removed with the 1.87 release of Clippy
+ #[allow(clippy::missing_const_for_fn)]
pub fn get_container_items(&self) -> &[ContainerItem] {
&self.containers.items
}
@@ -459,7 +461,7 @@ impl AppData {
}
/// Get ListState of containers
- pub fn get_container_state(&mut self) -> &mut ListState {
+ pub const fn get_container_state(&mut self) -> &mut ListState {
&mut self.containers.state
}
@@ -886,10 +888,10 @@ impl AppData {
if let Some(item) = self.get_any_container_by_id(&id) {
if item.name.get() != name {
item.name.set(name);
- };
+ }
if item.status != status {
item.status = status;
- };
+ }
if item.state != state {
item.docker_controls.items = DockerCommand::gen_vec(state);
// Update the list state, needs to be None if the gen_vec returns an empty vec
@@ -898,15 +900,15 @@ impl AppData {
item.docker_controls.state.select(None);
}
_ => item.docker_controls.start(),
- };
+ }
item.state = state;
- };
+ }
item.ports = ports;
if item.image.get() != image {
item.image.set(image);
- };
+ }
} else {
// container not known, so make new ContainerItem and push into containers Ve
let container = ContainerItem::new(
diff --git a/src/config/config.toml b/src/config/config.toml
index 8d9180a..b8d4534 100644
--- a/src/config/config.toml
+++ b/src/config/config.toml
@@ -16,7 +16,7 @@ raw_logs = false
show_self = false
# Show std_err in logs
-show_std_err = false
+show_std_err = true
# Show a timestamp for every log entry
show_timestamp = true
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 7a61bd2..9a31823 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -126,19 +126,44 @@ impl Config {
}
/// Combine config from CLI into config file, the cli take priority
- /// make sure color_logs and raw_logs can't clash
+ /// and also make sure color_logs and raw_logs can't clash
fn merge_args(mut self, config_from_cli: Self) -> Self {
- self.color_logs = config_from_cli.color_logs;
- self.docker_interval_ms = config_from_cli.docker_interval_ms;
- self.gui = config_from_cli.gui;
- self.raw_logs = config_from_cli.raw_logs;
- self.show_self = config_from_cli.show_self;
- self.show_std_err = config_from_cli.show_std_err;
- self.show_timestamp = config_from_cli.show_timestamp;
- self.use_cli = config_from_cli.use_cli;
+ let default_args = Args::default();
+
+ if config_from_cli.color_logs != default_args.color {
+ self.color_logs = config_from_cli.color_logs;
+ }
+
+ if config_from_cli.gui != default_args.gui {
+ self.gui = config_from_cli.gui;
+ }
+
+ if config_from_cli.docker_interval_ms != default_args.docker_interval {
+ self.docker_interval_ms = config_from_cli.docker_interval_ms;
+ }
if config_from_cli.docker_interval_ms < 1000 {
- self.docker_interval_ms = 1000;
+ self.docker_interval_ms = default_args.docker_interval;
+ }
+
+ if config_from_cli.raw_logs != default_args.raw {
+ self.raw_logs = config_from_cli.raw_logs;
+ }
+
+ if config_from_cli.show_self != default_args.show_self {
+ self.show_self = config_from_cli.show_self;
+ }
+
+ if config_from_cli.show_std_err != default_args.no_std_err {
+ self.show_std_err = config_from_cli.show_std_err;
+ }
+
+ if config_from_cli.show_timestamp != default_args.timestamp {
+ self.show_timestamp = config_from_cli.show_timestamp;
+ }
+
+ if config_from_cli.use_cli != default_args.use_cli {
+ self.use_cli = config_from_cli.use_cli;
}
if let Some(host) = config_from_cli.host {
@@ -153,10 +178,7 @@ impl Config {
self.timezone = Some(tz);
}
- if config_from_cli.raw_logs {
- self.color_logs = false;
- }
- if config_from_cli.color_logs {
+ if self.color_logs && self.raw_logs {
self.raw_logs = false;
}
self
@@ -232,11 +254,9 @@ mod tests {
#[test]
/// Test various timezones get parsed correctly
fn test_config_parse_timezone() {
- assert!(super::Config::parse_timezone(None).is_none());
-
// Timezone with no offset just return None
- for i in ["Europe/London", "Africa/Accra"] {
- assert!(super::Config::parse_timezone(Some(i.to_owned())).is_none());
+ for i in [None, Some("UTC".to_owned())] {
+ assert!(super::Config::parse_timezone(i).is_none());
}
let expected = Some(TimeZone::get("Asia/Tokyo").unwrap());
diff --git a/src/config/parse_args.rs b/src/config/parse_args.rs
index c8cedc2..c79434f 100644
--- a/src/config/parse_args.rs
+++ b/src/config/parse_args.rs
@@ -53,3 +53,22 @@ pub struct Args {
#[clap(long="use-cli", short = None)]
pub use_cli: bool,
}
+
+impl Default for Args {
+ fn default() -> Self {
+ Self {
+ docker_interval: 1000,
+ timestamp: true,
+ color: false,
+ raw: false,
+ show_self: false,
+ gui: true,
+ host: None,
+ no_std_err: true,
+ timezone: None,
+ save_dir: None,
+ config_file: None,
+ use_cli: false,
+ }
+ }
+}
diff --git a/src/config/parse_config_file.rs b/src/config/parse_config_file.rs
index 2af4a46..b198f1b 100644
--- a/src/config/parse_config_file.rs
+++ b/src/config/parse_config_file.rs
@@ -10,14 +10,14 @@ use crate::app_error::AppError;
use super::{color_parser::ConfigColors, keymap_parser::ConfigKeymap};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum ConfigFileType {
+enum ConfigFileFormat {
Toml,
Jsonc,
Json,
JsoncAsJson,
}
-impl TryFrom<&PathBuf> for ConfigFileType {
+impl TryFrom<&PathBuf> for ConfigFileFormat {
type Error = AppError;
/// Only allow toml, json, or jsonc files
@@ -38,7 +38,7 @@ impl TryFrom<&PathBuf> for ConfigFileType {
}
}
-impl ConfigFileType {
+impl ConfigFileFormat {
/// Get the local config directory, to be used by default config parser
fn get_config_dir(in_container: bool) -> Option {
if in_container {
@@ -48,7 +48,7 @@ impl ConfigFileType {
.map(|base_dirs| base_dirs.config_local_dir().join(env!("CARGO_PKG_NAME")))
}
}
- /// Return the default filename + path for a given filetype
+ /// Return the default filename + path for a given fileformat
fn get_default_path_name(self, in_container: bool) -> PathBuf {
let suffix = match self {
Self::Json | Self::JsoncAsJson => "config.json",
@@ -84,7 +84,7 @@ impl ConfigFile {
return Ok(());
}
- let config_dir = ConfigFileType::get_config_dir(in_container)
+ let config_dir = ConfigFileFormat::get_config_dir(in_container)
.ok_or_else(|| AppError::IO("config_dir".to_owned()))?;
let file_name = config_dir.join("config.toml");
@@ -105,22 +105,22 @@ impl ConfigFile {
}
/// parse a given &str (read from the configfile) into Self
- fn parse(file_type: ConfigFileType, input: &str) -> Result {
- match file_type {
- ConfigFileType::Json => {
+ fn parse(file_format: ConfigFileFormat, input: &str) -> Result {
+ match file_format {
+ ConfigFileFormat::Json => {
serde_json::from_str::(input).map_err(|i| AppError::Parse(i.to_string()))
}
- ConfigFileType::Jsonc | ConfigFileType::JsoncAsJson => {
+ ConfigFileFormat::Jsonc | ConfigFileFormat::JsoncAsJson => {
serde_jsonc::from_str::(input).map_err(|i| AppError::Parse(i.to_string()))
}
- ConfigFileType::Toml => {
+ ConfigFileFormat::Toml => {
toml::from_str::(input).map_err(|i| AppError::Parse(i.message().to_owned()))
}
}
}
/// Read the config file path to string, then attempt to parse
- fn parse_config_file(file_type: ConfigFileType, path: &PathBuf) -> Result {
+ fn parse_config_file(file_format: ConfigFileFormat, path: &PathBuf) -> Result {
let mut file = std::fs::File::open(path).map_err(|_| {
AppError::IO(
path.to_str()
@@ -130,53 +130,32 @@ impl ConfigFile {
let mut input = String::new();
file.read_to_string(&mut input)
.map_err(|i| AppError::IO(i.to_string()))?;
- Self::parse(file_type, &input)
- }
-
- /// Resolve conflict in the args, this is handled automatically by Clap, basically just by rejecting it
- /// But here we can just change the options - although maybe should be also reject to follow the same behaviour as Clap?
- /// TODO I think this is duplicated with the merge_args fn
- fn resolve_conflict(&mut self) {
- if let Some(color) = self.color_logs.as_ref() {
- if *color {
- self.raw_logs = Some(false);
- }
- }
- if let Some(interval) = self.docker_interval.as_ref() {
- if interval < &1000 {
- self.docker_interval = Some(1000);
- }
- }
+ Self::parse(file_format, &input)
}
/// Try to parse the config file when the path is user supplied via cliargs
pub fn try_parse_from_file(path: &str) -> Option {
let path = PathBuf::from(path);
- let Ok(file_type) = ConfigFileType::try_from(&path) else {
+ let Ok(file_format) = ConfigFileFormat::try_from(&path) else {
return None;
};
-
- Self::parse_config_file(file_type, &path).map_or(None, |mut config_file| {
- config_file.resolve_conflict();
- Some(config_file)
- })
+ Self::parse_config_file(file_format, &path).ok()
}
/// Parse a config file using default config_file location
/// This is executed first, then the CLI args are read, and if they contain a "--config-file" entry, then Self::try_parse_from_file() is executed
pub fn try_parse(in_container: bool) -> Option {
let mut config = None;
- for file_type in [
- ConfigFileType::Toml,
- ConfigFileType::Jsonc,
- ConfigFileType::JsoncAsJson,
- ConfigFileType::Json,
+ for file_format in [
+ ConfigFileFormat::Toml,
+ ConfigFileFormat::Jsonc,
+ ConfigFileFormat::JsoncAsJson,
+ ConfigFileFormat::Json,
] {
- if let Ok(mut config_file) =
- Self::parse_config_file(file_type, &file_type.get_default_path_name(in_container))
- {
- Self::resolve_conflict(&mut config_file);
-
+ if let Ok(config_file) = Self::parse_config_file(
+ file_format,
+ &file_format.get_default_path_name(in_container),
+ ) {
config = Some(config_file);
break;
}
@@ -202,7 +181,7 @@ mod tests {
/// ./config.toml parses fine - as this is used to write a file on disk, it's vital that this is always valid
fn test_parse_config_toml_valid() {
let example_toml = include_str!("./config.toml");
- let result = ConfigFile::parse(super::ConfigFileType::Toml, example_toml);
+ let result = ConfigFile::parse(super::ConfigFileFormat::Toml, example_toml);
assert!(result.is_ok());
}
@@ -210,7 +189,7 @@ mod tests {
/// make sure config.toml matches the default keymap
fn test_parse_config_keymap_toml() {
let example_toml = include_str!("./config.toml");
- let result = ConfigFile::parse(super::ConfigFileType::Toml, example_toml).unwrap();
+ let result = ConfigFile::parse(super::ConfigFileFormat::Toml, example_toml).unwrap();
assert!(result.keymap.is_some());
assert_eq!(Keymap::from(result.keymap), Keymap::new());
}
@@ -219,7 +198,7 @@ mod tests {
/// make sure example.config.jsonc matches the default keymap
fn test_parse_config_keymap_jsonc() {
let example_jsonc = include_str!("../../example_config/example.config.jsonc");
- let result = ConfigFile::parse(super::ConfigFileType::Jsonc, example_jsonc).unwrap();
+ let result = ConfigFile::parse(super::ConfigFileFormat::Jsonc, example_jsonc).unwrap();
assert!(result.keymap.is_some());
assert_eq!(Keymap::from(result.keymap), Keymap::new());
}
@@ -228,12 +207,13 @@ mod tests {
/// All configs parsed and are equal
fn test_parse_config_keymap_all() {
let example_jsonc = include_str!("../../example_config/example.config.jsonc");
- let result_jsonc = ConfigFile::parse(super::ConfigFileType::Jsonc, example_jsonc).unwrap();
+ let result_jsonc =
+ ConfigFile::parse(super::ConfigFileFormat::Jsonc, example_jsonc).unwrap();
assert!(result_jsonc.keymap.is_some());
let result_jsonc = result_jsonc.keymap.unwrap();
let example_toml = include_str!("./config.toml");
- let result_toml = ConfigFile::parse(super::ConfigFileType::Toml, example_toml).unwrap();
+ let result_toml = ConfigFile::parse(super::ConfigFileFormat::Toml, example_toml).unwrap();
assert!(result_toml.keymap.is_some());
let result_toml = result_toml.keymap.unwrap();
@@ -245,7 +225,7 @@ mod tests {
/// make sure config.toml matches the default app colors
fn test_parse_config_colors_toml() {
let example_toml = include_str!("./config.toml");
- let result = ConfigFile::parse(super::ConfigFileType::Toml, example_toml).unwrap();
+ let result = ConfigFile::parse(super::ConfigFileFormat::Toml, example_toml).unwrap();
assert!(result.colors.is_some());
assert_eq!(AppColors::from(result.colors), AppColors::new());
}
@@ -254,7 +234,7 @@ mod tests {
/// make sure config.toml matches the default app colors
fn test_parse_config_colors_jsonc() {
let example_jsonc = include_str!("../../example_config/example.config.jsonc");
- let result = ConfigFile::parse(super::ConfigFileType::Jsonc, example_jsonc).unwrap();
+ let result = ConfigFile::parse(super::ConfigFileFormat::Jsonc, example_jsonc).unwrap();
assert!(result.colors.is_some());
assert_eq!(AppColors::from(result.colors), AppColors::new());
}
@@ -263,12 +243,13 @@ mod tests {
/// All configs parsed and are equal
fn test_parse_config_colors_all() {
let example_jsonc = include_str!("../../example_config/example.config.jsonc");
- let result_jsonc = ConfigFile::parse(super::ConfigFileType::Jsonc, example_jsonc).unwrap();
+ let result_jsonc =
+ ConfigFile::parse(super::ConfigFileFormat::Jsonc, example_jsonc).unwrap();
assert!(result_jsonc.colors.is_some());
let result_jsonc = result_jsonc.colors.unwrap();
let example_toml = include_str!("./config.toml");
- let result_toml = ConfigFile::parse(super::ConfigFileType::Toml, example_toml).unwrap();
+ let result_toml = ConfigFile::parse(super::ConfigFileFormat::Toml, example_toml).unwrap();
assert!(result_toml.colors.is_some());
let result_toml = result_toml.colors.unwrap();
diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs
index 2295b83..499a647 100644
--- a/src/docker_data/mod.rs
+++ b/src/docker_data/mod.rs
@@ -322,7 +322,7 @@ impl DockerData {
self.config.show_std_err,
)));
}
- };
+ }
self.update_all_container_stats();
self.app_data.lock().sort_containers();
}
diff --git a/src/exec.rs b/src/exec.rs
index 0a49edd..a1604b4 100644
--- a/src/exec.rs
+++ b/src/exec.rs
@@ -330,7 +330,7 @@ impl ExecMode {
if byte_sequence_valid(&bytes) {
waiting_thread.store(false, std::sync::atomic::Ordering::SeqCst);
}
- };
+ }
}
});
diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs
index 7ba06b0..a52c8dd 100644
--- a/src/input_handler/mod.rs
+++ b/src/input_handler/mod.rs
@@ -167,7 +167,7 @@ impl InputHandler {
.set_info_box("✓ mouse capture enabled");
} else {
err();
- };
+ }
self.mouse_capture = !self.mouse_capture;
}
@@ -264,7 +264,7 @@ impl InputHandler {
// Poor way of disallowing commands to be sent to a containerised okxer
if self.app_data.lock().is_oxker_in_container() {
return;
- };
+ }
let option_id = self.app_data.lock().get_selected_container_id();
if let Some(id) = option_id {
match command {
@@ -639,7 +639,7 @@ impl InputHandler {
SelectablePanel::Containers => self.app_data.lock().containers_next(),
SelectablePanel::Logs => self.app_data.lock().log_next(),
SelectablePanel::Commands => self.app_data.lock().docker_controls_next(),
- };
+ }
}
/// Change state to previous, depending which panel is currently in focus
diff --git a/src/ui/draw_blocks/charts.rs b/src/ui/draw_blocks/charts.rs
index 0de3f48..6926213 100644
--- a/src/ui/draw_blocks/charts.rs
+++ b/src/ui/draw_blocks/charts.rs
@@ -16,12 +16,12 @@ use crate::{
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum ChartType {
+enum ChartVariant {
Cpu,
Memory,
}
-impl ChartType {
+impl ChartVariant {
const fn name(self) -> &'static str {
match self {
Self::Cpu => "cpu",
@@ -75,30 +75,30 @@ impl ChartType {
/// Create charts
fn make_chart<'a, T: Stats + Display>(
- chart_type: ChartType,
+ chart_variant: ChartVariant,
colors: AppColors,
current: &'a T,
dataset: Vec>,
max: &'a T,
state: State,
) -> Chart<'a> {
- let max_color = chart_type.get_max_color(colors, state);
+ let max_color = chart_variant.get_max_color(colors, state);
Chart::new(dataset)
- .bg(chart_type.get_bg_color(colors))
+ .bg(chart_variant.get_bg_color(colors))
.block(
Block::default()
- .style(Style::default().bg(chart_type.get_bg_color(colors)))
+ .style(Style::default().bg(chart_variant.get_bg_color(colors)))
.title_alignment(Alignment::Center)
.title(Span::styled(
- format!(" {} {current} ", chart_type.name()),
+ format!(" {} {current} ", chart_variant.name()),
Style::default()
- .fg(chart_type.get_title_color(colors, state))
+ .fg(chart_variant.get_title_color(colors, state))
.add_modifier(Modifier::BOLD),
))
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
- .border_style(Style::default().fg(chart_type.get_border_color(colors))),
+ .border_style(Style::default().fg(chart_variant.get_border_color(colors))),
)
.x_axis(Axis::default().bounds([0.00, 60.0]))
.y_axis(
@@ -110,7 +110,7 @@ fn make_chart<'a, T: Stats + Display>(
Style::default().add_modifier(Modifier::BOLD).fg(max_color),
),
])
- .style(Style::new().fg(chart_type.get_y_axis_color(colors)))
+ .style(Style::new().fg(chart_variant.get_y_axis_color(colors)))
// Add 0.01, so that max point is always visible?
.bounds([0.0, max.get_value() + 0.01]),
)
@@ -143,7 +143,7 @@ pub fn draw(area: Rect, colors: AppColors, f: &mut Frame, fd: &FrameData) {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let mem_stats = ByteStats::new(mem.0.last().map_or(0, |f| f.1 as u64));
let cpu_chart = make_chart(
- ChartType::Cpu,
+ ChartVariant::Cpu,
colors,
&cpu_stats,
cpu_dataset,
@@ -151,7 +151,7 @@ pub fn draw(area: Rect, colors: AppColors, f: &mut Frame, fd: &FrameData) {
cpu.2,
);
let mem_chart = make_chart(
- ChartType::Memory,
+ ChartVariant::Memory,
colors,
&mem_stats,
mem_dataset,
@@ -167,6 +167,7 @@ pub fn draw(area: Rect, colors: AppColors, f: &mut Frame, fd: &FrameData) {
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
+ use insta::assert_snapshot;
use ratatui::style::{Color, Modifier};
use crate::{
@@ -174,14 +175,12 @@ mod tests {
config::AppColors,
ui::{
FrameData,
- draw_blocks::tests::{
- COLOR_ORANGE, expected_to_vec, get_result, insert_chart_data, test_setup,
- },
+ draw_blocks::tests::{COLOR_ORANGE, get_result, insert_chart_data, test_setup},
},
};
/// CPU and Memory charts used in multiple tests, based on data from above insert_chart_data()
- const EXPECTED: [&str; 10] = [
+ const _EXPECTED: [&str; 10] = [
"╭───────────── cpu 03.00% ─────────────╮╭────────── memory 30.00 kB ───────────╮",
"│10.00%│ • ││100.00 kB│ •• │",
"│ │ •• ││ │ •• │",
@@ -236,8 +235,7 @@ mod tests {
#[test]
/// When status is Running, but not data, charts drawn without dots etc, colours correct
fn test_draw_blocks_charts_running_none() {
- let (w, h) = (80, 10);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(80, 10, true, true);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup
@@ -246,25 +244,10 @@ mod tests {
super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd);
})
.unwrap();
+ assert_snapshot!(setup.terminal.backend());
- let expected = [
- "╭───────────── cpu 00.00% ─────────────╮╭─────────── memory 0.00 kB ───────────╮",
- "│00.00%│ ││0.00 kB│ │",
- "│ │ ││ │ │",
- "│ │ ││ │ │",
- "│ │ ││ │ │",
- "│ │ ││ │ │",
- "│ │ ││ │ │",
- "│ │ ││ │ │",
- "│ │ ││ │ │",
- "╰──────────────────────────────────────╯╰──────────────────────────────────────╯",
- ];
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
match (row_index, result_cell_index) {
(0, 14..=25 | 52..=67) => {
assert_eq!(result_cell.fg, Color::Green);
@@ -290,8 +273,7 @@ mod tests {
#[test]
/// When status is Running, charts correctly drawn
fn test_draw_blocks_charts_running_some() {
- let (w, h) = (80, 10);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(80, 10, true, true);
insert_chart_data(&setup);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
@@ -303,11 +285,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&EXPECTED, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
+ for (result_cell_index, result_cell) in result_row.iter().enumerate() {
match (row_index, result_cell_index) {
(0, 14..=25 | 51..=67) => {
assert_eq!(result_cell.fg, Color::Green);
@@ -341,8 +322,7 @@ mod tests {
#[test]
/// Whens status paused, some text is now Yellow
fn test_draw_blocks_charts_paused() {
- let (w, h) = (80, 10);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(80, 10, true, true);
insert_chart_data(&setup);
setup.app_data.lock().containers.items[0].state = State::Paused;
@@ -355,11 +335,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&EXPECTED, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
+ for (result_cell_index, result_cell) in result_row.iter().enumerate() {
match (row_index, result_cell_index) {
(0, 14..=25 | 51..=67) | (1, 1..=6 | 41..=49) => {
assert_eq!(result_cell.fg, Color::Yellow);
@@ -389,8 +368,7 @@ mod tests {
#[test]
/// When dead, text is red
fn test_draw_blocks_charts_dead() {
- let (w, h) = (80, 10);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(80, 10, true, true);
insert_chart_data(&setup);
setup.app_data.lock().containers.items[0].state = State::Dead;
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
@@ -401,12 +379,9 @@ mod tests {
super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd);
})
.unwrap();
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&EXPECTED, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
match (row_index, result_cell_index) {
(0, 14..=25 | 51..=67) | (1, 1..=6 | 41..=49) => {
assert_eq!(result_cell.fg, Color::Red);
@@ -452,8 +427,7 @@ mod tests {
colors.chart_memory.points = Color::Black;
colors.chart_memory.y_axis = Color::Blue;
- let (w, h) = (80, 10);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(80, 10, true, true);
insert_chart_data(&setup);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
@@ -465,10 +439,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&EXPECTED, row_index);
+ assert_snapshot!(setup.terminal.backend());
+
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::White);
match (row_index, result_cell_index) {
diff --git a/src/ui/draw_blocks/commands.rs b/src/ui/draw_blocks/commands.rs
index 25dc74f..146313d 100644
--- a/src/ui/draw_blocks/commands.rs
+++ b/src/ui/draw_blocks/commands.rs
@@ -55,6 +55,7 @@ pub fn draw(
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
+ use insta::assert_snapshot;
use ratatui::style::{Color, Modifier};
use crate::{
@@ -62,16 +63,16 @@ mod tests {
tests::gen_container_summary,
ui::{
FrameData,
- draw_blocks::tests::{BORDER_CHARS, expected_to_vec, get_result, test_setup},
+ draw_blocks::tests::{BORDER_CHARS, get_result, test_setup},
},
};
// cusomt border colors
#[test]
/// Test that when DockerCommands are available, they are drawn correctly, dependant on container state
+ /// In this case, no commands are drawn
fn test_draw_blocks_commands_none() {
- let (w, h) = (12, 6);
- let mut setup = test_setup(w, h, false, false);
+ let mut setup = test_setup(12, 6, false, false);
let colors = setup.app_data.lock().config.app_colors;
setup
@@ -88,28 +89,14 @@ mod tests {
})
.unwrap();
- let expected = [
- "╭──────────╮",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "╰──────────╯",
- ];
-
- for (row_index, row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (cell_index, cell) in row.iter().enumerate() {
- assert_eq!(cell.symbol(), expected_row[cell_index]);
- }
- }
+ assert_snapshot!(setup.terminal.backend());
}
#[test]
/// Test that when DockerCommands are available, they are drawn correctly, dependant on container state
+ /// In this test, container is running
fn test_draw_blocks_commands_some() {
- let (w, h) = (12, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(12, 6, true, true);
let colors = setup.app_data.lock().config.app_colors;
setup
@@ -126,19 +113,10 @@ mod tests {
})
.unwrap();
- let expected = [
- "╭──────────╮",
- "│▶ pause │",
- "│ restart │",
- "│ stop │",
- "│ delete │",
- "╰──────────╯",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Reset);
match (row_index, result_cell_index) {
// Borders & delete
@@ -163,22 +141,36 @@ mod tests {
}
}
}
- // Change the controls state
+ }
+
+ #[test]
+ /// Test that when DockerCommands are available, they are drawn correctly, dependant on container state
+ /// In this test, container is paused
+ fn test_draw_blocks_commands_some_paused() {
+ let mut setup = test_setup(12, 6, true, true);
+
+ let colors = setup.app_data.lock().config.app_colors;
+ setup
+ .terminal
+ .draw(|f| {
+ super::draw(
+ &setup.app_data,
+ setup.area,
+ colors,
+ f,
+ &setup.fd,
+ &setup.gui_state,
+ );
+ })
+ .unwrap();
+
+ // Set the container state to paused
setup
.app_data
.lock()
.update_containers(vec![gen_container_summary(1, "paused")]);
setup.app_data.lock().docker_controls_next();
- let expected = [
- "╭──────────╮",
- "│ resume │",
- "│▶ stop │",
- "│ delete │",
- "│ │",
- "╰──────────╯",
- ];
-
setup
.terminal
.draw(|f| {
@@ -193,10 +185,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Reset);
match (row_index, result_cell_index) {
// resume
@@ -222,16 +214,7 @@ mod tests {
#[test]
/// When control panel is selected, the border is blue, if not then white, selected text is highlighted
fn test_draw_blocks_commands_panel_selected_color() {
- let (w, h) = (12, 6);
- let mut setup = test_setup(w, h, true, true);
- let expected = [
- "╭──────────╮",
- "│▶ pause │",
- "│ restart │",
- "│ stop │",
- "│ delete │",
- "╰──────────╯",
- ];
+ let mut setup = test_setup(12, 6, true, true);
let colors = setup.app_data.lock().config.app_colors;
// Unselected, has a grey border
setup
@@ -248,10 +231,9 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ assert_snapshot!(setup.terminal.backend());
+ for (_, result_row) in get_result(&setup) {
+ for result_cell in result_row {
if BORDER_CHARS.contains(&result_cell.symbol()) {
assert_eq!(result_cell.fg, Color::Gray);
}
@@ -275,10 +257,8 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
if row_index == 0
|| row_index == 5
|| result_cell_index == 0
@@ -296,10 +276,9 @@ mod tests {
}
#[test]
- /// Custom colors are rendered correctlty
- fn test_draw_blocks_commands_custom_colors() {
- let (w, h) = (12, 6);
- let mut setup = test_setup(w, h, true, true);
+ /// Custom colors are rendered correctly
+ fn test_draw_blocks_commands_custom_colors_running() {
+ let mut setup = test_setup(12, 6, true, true);
let mut colors = AppColors::new();
colors.commands.background = Color::White;
colors.commands.pause = Color::Black;
@@ -323,19 +302,9 @@ mod tests {
})
.unwrap();
- let expected = [
- "╭──────────╮",
- "│▶ pause │",
- "│ restart │",
- "│ stop │",
- "│ delete │",
- "╰──────────╯",
- ];
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::White);
match (row_index, result_cell_index) {
// pause
@@ -358,21 +327,19 @@ mod tests {
}
}
}
- // Change the controls state
- setup
- .app_data
- .lock()
- .update_containers(vec![gen_container_summary(1, "paused")]);
- setup.app_data.lock().docker_controls_next();
-
- let expected = [
- "╭──────────╮",
- "│ resume │",
- "│▶ stop │",
- "│ delete │",
- "│ │",
- "╰──────────╯",
- ];
+ }
+ #[test]
+ /// Custom colors are rendered correctly
+ fn test_draw_blocks_commands_custom_colors_paused() {
+ let mut setup = test_setup(12, 6, true, true);
+ let mut colors = AppColors::new();
+ colors.commands.background = Color::White;
+ colors.commands.pause = Color::Black;
+ colors.commands.restart = Color::Green;
+ colors.commands.stop = Color::Blue;
+ colors.commands.delete = Color::Magenta;
+ colors.commands.resume = Color::Yellow;
+ colors.commands.start = Color::Cyan;
setup
.terminal
@@ -388,10 +355,31 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ // Set the controls state
+ setup
+ .app_data
+ .lock()
+ .update_containers(vec![gen_container_summary(1, "paused")]);
+ setup.app_data.lock().docker_controls_next();
+
+ setup
+ .terminal
+ .draw(|f| {
+ super::draw(
+ &setup.app_data,
+ setup.area,
+ colors,
+ f,
+ &setup.fd,
+ &setup.gui_state,
+ );
+ })
+ .unwrap();
+
+ assert_snapshot!(setup.terminal.backend());
+
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::White);
match (row_index, result_cell_index) {
diff --git a/src/ui/draw_blocks/containers.rs b/src/ui/draw_blocks/containers.rs
index b817785..a5c7395 100644
--- a/src/ui/draw_blocks/containers.rs
+++ b/src/ui/draw_blocks/containers.rs
@@ -136,6 +136,7 @@ pub fn draw(
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
+ use insta::assert_snapshot;
use ratatui::style::{Color, Modifier};
use crate::{
@@ -144,8 +145,8 @@ mod tests {
ui::{
FrameData,
draw_blocks::tests::{
- BORDER_CHARS, COLOR_ORANGE, COLOR_RX, COLOR_TX, TuiTestSetup, expected_to_vec,
- get_result, test_setup,
+ BORDER_CHARS, COLOR_ORANGE, COLOR_RX, COLOR_TX, TuiTestSetup, get_result,
+ test_setup,
},
},
};
@@ -153,19 +154,9 @@ mod tests {
#[test]
/// No containers, panel unselected, then selected, border color changes correctly
fn test_draw_blocks_containers_none() {
- let (w, h) = (25, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(40, 6, true, true);
setup.app_data.lock().containers = StatefulList::new(vec![]);
- let expected = [
- "╭ Containers ───────────╮",
- "│ no containers running │",
- "│ │",
- "│ │",
- "│ │",
- "╰───────────────────────╯",
- ];
-
setup.gui_state.lock().next_panel();
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors;
@@ -184,10 +175,9 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ assert_snapshot!(setup.terminal.backend());
+ for (_, result_row) in get_result(&setup) {
+ for result_cell in result_row {
if BORDER_CHARS.contains(&result_cell.symbol()) {
assert_eq!(result_cell.fg, Color::Gray);
}
@@ -211,10 +201,8 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ for (_, result_row) in get_result(&setup) {
+ for result_cell in result_row {
if BORDER_CHARS.contains(&result_cell.symbol()) {
assert_eq!(result_cell.fg, Color::LightCyan);
}
@@ -225,17 +213,8 @@ mod tests {
#[test]
/// Containers panel drawn, selected line is bold, border is blue
fn test_draw_blocks_containers_selected_bold() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(130, 6, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ✓ running Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
let colors = setup.app_data.lock().config.app_colors;
setup
@@ -252,11 +231,9 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
if BORDER_CHARS.contains(&result_cell.symbol()) {
assert_eq!(result_cell.fg, Color::LightCyan);
}
@@ -294,11 +271,8 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
+ for (_, result_row) in get_result(&setup) {
+ for result_cell in result_row {
if BORDER_CHARS.contains(&result_cell.symbol()) {
assert_eq!(result_cell.fg, Color::Gray);
}
@@ -309,17 +283,8 @@ mod tests {
#[test]
/// Columns on all rows are coloured correctly
fn test_draw_blocks_containers_colors() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(130, 6, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ✓ running Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors;
@@ -337,12 +302,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
match (row_index, result_cell_index) {
//border
(0 | 5, _) | (1..=4, 0 | 129) => {
@@ -373,21 +336,12 @@ mod tests {
#[test]
/// Long container + image name is truncated correctly
fn test_draw_blocks_containers_long_name_image() {
- let (w, h) = (170, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(170, 6, true, true);
setup.app_data.lock().containers.items[0].name =
ContainerName::from("a_long_container_name_for_the_purposes_of_this_test");
setup.app_data.lock().containers.items[0].image =
ContainerImage::from("a_long_image_name_for_the_purposes_of_this_test");
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ a_long_container_name_for_the… ॥ paused Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 a_long_image_name_for_the_pur… 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors;
setup.app_data.lock().containers.items[0].state = State::Paused;
@@ -405,22 +359,14 @@ mod tests {
);
})
.unwrap();
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
+ assert_snapshot!(setup.terminal.backend());
}
// Check that the correct colour is applied to the state/status/cpu/memory section
- fn check_expected(expected: [&str; 6], w: u16, _h: u16, setup: &TuiTestSetup, color: Color) {
- for (row_index, result_row) in get_result(setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ fn check_colour(setup: &TuiTestSetup, color: Color) {
+ for (row_index, result_row) in get_result(setup) {
+ for (result_cell_index, result_cell) in result_row.iter().enumerate() {
match (row_index, result_cell_index) {
// border
(0 | 5, _) | (1..=4, 0 | 129) => {
@@ -455,17 +401,8 @@ mod tests {
#[test]
/// When container is paused, correct colors displayed
fn test_draw_blocks_containers_paused() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(130, 6, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ॥ paused Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors;
setup.app_data.lock().containers.items[0].state = State::Paused;
@@ -484,23 +421,14 @@ mod tests {
})
.unwrap();
- check_expected(expected, w, h, &setup, Color::Yellow);
+ check_colour(&setup, Color::Yellow);
+ assert_snapshot!(setup.terminal.backend());
}
#[test]
/// When container is dead, correct colors displayed
fn test_draw_blocks_containers_dead() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
-
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ✖ dead Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
+ let mut setup = test_setup(130, 6, true, true);
setup.app_data.lock().containers.items[0].state = State::Dead;
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors;
@@ -519,23 +447,15 @@ mod tests {
})
.unwrap();
- check_expected(expected, w, h, &setup, Color::Red);
+ check_colour(&setup, Color::Red);
+ assert_snapshot!(setup.terminal.backend());
}
#[test]
/// When container is exited, correct colors displayed
fn test_draw_blocks_containers_exited() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(130, 6, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ✖ exited Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
setup.app_data.lock().containers.items[0].state = State::Exited;
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors;
@@ -554,22 +474,14 @@ mod tests {
})
.unwrap();
- check_expected(expected, w, h, &setup, Color::Red);
+ check_colour(&setup, Color::Red);
+ assert_snapshot!(setup.terminal.backend());
}
#[test]
/// When container is paused, correct colors displayed
fn test_draw_blocks_containers_removing() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(130, 6, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 removing Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
setup.app_data.lock().containers.items[0].state = State::Removing;
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors;
@@ -588,23 +500,15 @@ mod tests {
})
.unwrap();
- check_expected(expected, w, h, &setup, Color::LightRed);
+ check_colour(&setup, Color::LightRed);
+ assert_snapshot!(setup.terminal.backend());
}
#[test]
/// When container state is restarting, correct colors displayed
fn test_draw_blocks_containers_restarting() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(130, 6, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ↻ restarting Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
setup.app_data.lock().containers.items[0].state = State::Restarting;
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors;
@@ -623,11 +527,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
+ for (result_cell_index, result_cell) in result_row.iter().enumerate() {
match (row_index, result_cell_index) {
// border
(0 | 5, _) | (1..=4, 0 | 129) => {
@@ -664,21 +567,12 @@ mod tests {
#[test]
/// When container state is unhealthy, correct colors displayed
fn test_draw_blocks_containers_unhealthy() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(130, 6, true, true);
let status = ContainerStatus::from("Up 1 hour (unhealthy)".to_owned());
setup.app_data.lock().containers.items[0].state = State::from(("running", &status));
setup.app_data.lock().containers.items[0].status = status;
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ! running Up 1 hour (unhealthy) 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors;
@@ -696,10 +590,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
match (row_index, result_cell_index) {
// border
(0 | 5, _) | (1..=4, 0 | 129) => {
@@ -734,17 +628,8 @@ mod tests {
#[test]
/// When container state is unknown, correct colors displayed
fn test_draw_blocks_containers_unknown() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(130, 6, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ? unknown Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
setup.app_data.lock().containers.items[0].state = State::Unknown;
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors;
@@ -763,23 +648,15 @@ mod tests {
})
.unwrap();
- check_expected(expected, w, h, &setup, Color::Red);
+ check_colour(&setup, Color::Red);
+ assert_snapshot!(setup.terminal.backend());
}
#[test]
/// Custom colors applied correctly
fn test_draw_blocks_containers_custom_colors() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(130, 6, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ✓ running Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let mut colors = AppColors::new();
colors.borders.selected = Color::Green;
@@ -804,11 +681,9 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
-
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
// The highlight symbol can't correctly be colored
if (row_index, result_cell_index) != (1, 2) {
assert_eq!(result_cell.bg, Color::Black);
@@ -843,17 +718,8 @@ mod tests {
#[test]
/// Make sure that the state has the correctly color applied to it
fn test_draw_blocks_containers_custom_colors_state_healthy() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(130, 6, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ✓ running Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
// Healthy
@@ -874,11 +740,9 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
-
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
if let (1..=3, 18..=70) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Magenta);
}
@@ -888,18 +752,8 @@ mod tests {
#[test]
/// Make sure that the state has the correctly color applied to it
fn test_draw_blocks_containers_custom_colors_state_unhealthy() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(130, 6, true, true);
- // Unhealthy
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ! running Up 1 hour (unhealthy) 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let mut colors = AppColors::new();
@@ -922,11 +776,9 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
-
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
if let (1, 18..=70) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Red);
}
@@ -937,16 +789,7 @@ mod tests {
#[test]
/// Make sure that the state has the correctly color applied to it
fn test_draw_blocks_containers_custom_colors_state_dead() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ✖ dead Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
+ let mut setup = test_setup(130, 6, true, true);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
@@ -967,12 +810,9 @@ mod tests {
);
})
.unwrap();
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
-
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
if let (1, 18..=70) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Magenta);
}
@@ -983,16 +823,7 @@ mod tests {
#[test]
/// Make sure that the state has the correctly color applied to it
fn test_draw_blocks_containers_custom_colors_state_exited() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ✖ exited Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
+ let mut setup = test_setup(130, 6, true, true);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
@@ -1014,11 +845,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
if let (1, 18..=70) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Gray);
}
@@ -1029,16 +859,7 @@ mod tests {
#[test]
/// Make sure that the state has the correctly color applied to it
fn test_draw_blocks_containers_custom_colors_state_paused() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ॥ paused Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
+ let mut setup = test_setup(130, 6, true, true);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
@@ -1059,12 +880,10 @@ mod tests {
);
})
.unwrap();
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
-
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
if let (1, 18..=70) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Cyan);
}
@@ -1075,16 +894,7 @@ mod tests {
#[test]
/// Make sure that the state has the correctly color applied to it
fn test_draw_blocks_containers_custom_colors_state_removing() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 removing Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
+ let mut setup = test_setup(130, 6, true, true);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
@@ -1105,12 +915,10 @@ mod tests {
);
})
.unwrap();
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
-
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
if let (1, 18..=70) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::White);
}
@@ -1121,16 +929,7 @@ mod tests {
#[test]
/// Make sure that the state has the correctly color applied to it
fn test_draw_blocks_containers_custom_colors_state_restarting() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ↻ restarting Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
+ let mut setup = test_setup(130, 6, true, true);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
@@ -1152,11 +951,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
if let (1, 18..=70) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::LightYellow);
}
@@ -1167,16 +965,7 @@ mod tests {
#[test]
/// Make sure that the state has the correctly color applied to it
fn test_draw_blocks_containers_custom_colors_state_unknown() {
- let (w, h) = (130, 6);
- let mut setup = test_setup(w, h, true, true);
- let expected = [
- "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│⚪ container_1 ? unknown Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- ];
+ let mut setup = test_setup(130, 6, true, true);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
@@ -1198,11 +987,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
if let (1, 18..=70) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, COLOR_ORANGE);
}
diff --git a/src/ui/draw_blocks/delete_confirm.rs b/src/ui/draw_blocks/delete_confirm.rs
index efa1345..d64fb7d 100644
--- a/src/ui/draw_blocks/delete_confirm.rs
+++ b/src/ui/draw_blocks/delete_confirm.rs
@@ -128,32 +128,20 @@ pub fn draw(
#[allow(clippy::unwrap_used)]
mod tests {
use crossterm::event::KeyCode;
+ use insta::assert_snapshot;
use ratatui::style::{Color, Modifier};
use crate::{
app_data::ContainerName,
config::{AppColors, Keymap},
- ui::draw_blocks::tests::{expected_to_vec, get_result, test_setup},
+ ui::draw_blocks::tests::{get_result, test_setup},
};
#[test]
/// Delete container popup is drawn correctly
fn test_draw_blocks_delete() {
- let (w, h) = (82, 10);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(82, 10, true, true);
- let expected = [
- " ",
- " ╭──────────────────────── Confirm Delete ────────────────────────╮ ",
- " │ │ ",
- " │ Are you sure you want to delete container: container_1 │ ",
- " │ │ ",
- " │ ╭─────────────────────╮ ╭─────────────────────╮ │ ",
- " │ │ ( n ) no │ │ ( y ) yes │ │ ",
- " │ ╰─────────────────────╯ ╰─────────────────────╯ │ ",
- " ╰────────────────────────────────────────────────────────────────╯ ",
- " ",
- ];
let colors = setup.app_data.lock().config.app_colors;
let keymap = &setup.app_data.lock().config.keymap;
@@ -170,11 +158,9 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
match (row_index, result_cell_index) {
(0 | 9, _) | (1..=8, 0..=7 | 74..=81) => {
assert_eq!(result_cell.bg, Color::Reset);
@@ -197,23 +183,10 @@ mod tests {
#[test]
/// Delete container popup is drawn correctly
fn test_draw_blocks_delete_long_name() {
- let (w, h) = (106, 10);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(106, 10, true, true);
let name = ContainerName::from("container_1_container_1_container_1");
setup.app_data.lock().containers.items[0].name = name.clone();
- let expected = [
- " ",
- " ╭──────────────────────────────────── Confirm Delete ────────────────────────────────────╮ ",
- " │ │ ",
- " │ Are you sure you want to delete container: container_1_container_1_container_1 │ ",
- " │ │ ",
- " │ ╭──────────────────────────────╮ ╭─────────────────────────────╮ │ ",
- " │ │ ( n ) no │ │ ( y ) yes │ │ ",
- " │ ╰──────────────────────────────╯ ╰─────────────────────────────╯ │ ",
- " ╰────────────────────────────────────────────────────────────────────────────────────────╯ ",
- " ",
- ];
let colors = setup.app_data.lock().config.app_colors;
let keymap = &setup.app_data.lock().config.keymap;
@@ -223,12 +196,9 @@ mod tests {
super::draw(colors, f, &setup.gui_state, keymap, &name);
})
.unwrap();
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
match (row_index, result_cell_index) {
(0 | 9, _) | (1..=8, 0..=7 | 98..=106) => {
assert_eq!(result_cell.bg, Color::Reset);
@@ -251,21 +221,7 @@ mod tests {
#[test]
/// Custom colors applied correctly to delete popup
fn test_draw_blocks_delete_custom_colors() {
- let (w, h) = (82, 10);
- let mut setup = test_setup(w, h, true, true);
-
- let expected = [
- " ",
- " ╭──────────────────────── Confirm Delete ────────────────────────╮ ",
- " │ │ ",
- " │ Are you sure you want to delete container: container_1 │ ",
- " │ │ ",
- " │ ╭─────────────────────╮ ╭─────────────────────╮ │ ",
- " │ │ ( n ) no │ │ ( y ) yes │ │ ",
- " │ ╰─────────────────────╯ ╰─────────────────────╯ │ ",
- " ╰────────────────────────────────────────────────────────────────╯ ",
- " ",
- ];
+ let mut setup = test_setup(82, 10, true, true);
let mut colors = AppColors::new();
colors.popup_delete.background = Color::Black;
colors.popup_delete.text = Color::Yellow;
@@ -284,11 +240,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
+ for (result_cell_index, result_cell) in result_row.iter().enumerate() {
match (row_index, result_cell_index) {
(0 | 9, _) | (1..=8, 0..=7 | 74..=81) => {
assert_eq!(result_cell.bg, Color::Reset);
@@ -310,22 +265,8 @@ mod tests {
#[test]
/// Custom keymap, with multiple definitions for each button, applied correctly to delete popup
- #[allow(clippy::too_many_lines)]
- fn test_draw_blocks_delete_custom_keymap() {
- let (w, h) = (82, 10);
- let mut setup = test_setup(w, h, true, true);
- let expected = [
- " ",
- " ╭──────────────────────── Confirm Delete ────────────────────────╮ ",
- " │ │ ",
- " │ Are you sure you want to delete container: container_1 │ ",
- " │ │ ",
- " │ ╭─────────────────────╮ ╭─────────────────────╮ │ ",
- " │ │ ( End ) no │ │ ( F10 ) yes │ │ ",
- " │ ╰─────────────────────╯ ╰─────────────────────╯ │ ",
- " ╰────────────────────────────────────────────────────────────────╯ ",
- " ",
- ];
+ fn test_draw_blocks_delete_custom_keymap_one_definition() {
+ let mut setup = test_setup(82, 10, true, true);
let mut keymap = Keymap::new();
keymap.delete_confirm = (KeyCode::F(10), None);
keymap.delete_deny = (KeyCode::End, None);
@@ -341,25 +282,12 @@ mod tests {
);
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
-
- let expected = [
- " ",
- " ╭──────────────────────── Confirm Delete ────────────────────────╮ ",
- " │ │ ",
- " │ Are you sure you want to delete container: container_1 │ ",
- " │ │ ",
- " │ ╭─────────────────────╮ ╭─────────────────────╮ │ ",
- " │ │ ( End | Up ) no │ │ ( F10 | L ) yes │ │ ",
- " │ ╰─────────────────────╯ ╰─────────────────────╯ │ ",
- " ╰────────────────────────────────────────────────────────────────╯ ",
- " ",
- ];
+ assert_snapshot!(setup.terminal.backend());
+ }
+ #[test]
+ /// Custom keymap, with multiple definitions for each button, applied correctly to delete popup
+ fn test_draw_blocks_delete_custom_keymap_two_definition() {
+ let mut setup = test_setup(82, 10, true, true);
let mut keymap = Keymap::new();
keymap.delete_confirm = (KeyCode::F(10), Some(KeyCode::Char('L')));
keymap.delete_deny = (KeyCode::End, Some(KeyCode::Up));
@@ -375,26 +303,12 @@ mod tests {
);
})
.unwrap();
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
-
- let expected = [
- " ",
- " ╭──────────────────────── Confirm Delete ────────────────────────╮ ",
- " │ │ ",
- " │ Are you sure you want to delete container: container_1 │ ",
- " │ │ ",
- " │ ╭─────────────────────╮ ╭─────────────────────╮ │ ",
- " │ │ ( End | Up ) no │ │ ( F10 ) yes │ │ ",
- " │ ╰─────────────────────╯ ╰─────────────────────╯ │ ",
- " ╰────────────────────────────────────────────────────────────────╯ ",
- " ",
- ];
+ assert_snapshot!(setup.terminal.backend());
+ }
+ #[test]
+ /// Custom keymap, with multiple definitions for each button, applied correctly to delete popup
+ fn test_draw_blocks_delete_custom_keymap_one_two_definition() {
+ let mut setup = test_setup(82, 10, true, true);
let mut keymap = Keymap::new();
keymap.delete_confirm = (KeyCode::F(10), None);
keymap.delete_deny = (KeyCode::End, Some(KeyCode::Up));
@@ -410,12 +324,6 @@ mod tests {
);
})
.unwrap();
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
+ assert_snapshot!(setup.terminal.backend());
}
}
diff --git a/src/ui/draw_blocks/error.rs b/src/ui/draw_blocks/error.rs
index 551b023..0d69f12 100644
--- a/src/ui/draw_blocks/error.rs
+++ b/src/ui/draw_blocks/error.rs
@@ -14,6 +14,9 @@ use crate::{
use super::popup;
+const SUFFIX_CLEAR: &str = "clear error";
+const SUFFIX_QUIT: &str = "quit oxker";
+
/// Draw an error popup over whole screen
pub fn draw(
colors: AppColors,
@@ -36,24 +39,21 @@ pub fn draw(
seconds.unwrap_or(5)
)
} else {
- let clear_suffix = "clear error";
let clear_text = if keymap.clear == Keymap::new().clear {
- format!("( {} ) {clear_suffix}", keymap.clear.0)
+ format!("( {} ) {SUFFIX_CLEAR}", keymap.clear.0)
} else if let Some(secondary) = keymap.clear.1 {
- format!(" ( {} | {secondary} ) {clear_suffix}", keymap.clear.0)
+ format!(" ( {} | {secondary} ) {SUFFIX_CLEAR}", keymap.clear.0)
} else {
- format!(" ( {} ) {clear_suffix}", keymap.clear.0)
+ format!(" ( {} ) {SUFFIX_CLEAR}", keymap.clear.0)
};
- let quit_suffix = "quit oxker";
let quit_text = if keymap.quit == Keymap::new().quit {
- format!("( {} ) {quit_suffix}", keymap.quit.0)
+ format!("( {} ) {SUFFIX_QUIT}", keymap.quit.0)
} else if let Some(secondary) = keymap.quit.1 {
- format!(" ( {} | {secondary} ) {quit_suffix}", keymap.quit.0)
+ format!(" ( {} | {secondary} ) {SUFFIX_QUIT}", keymap.quit.0)
} else {
- format!(" ( {} ) {quit_suffix}", keymap.quit.0)
+ format!(" ( {} ) {SUFFIX_QUIT}", keymap.quit.0)
};
-
format!("\n\n{clear_text}\n\n{quit_text}")
};
@@ -94,21 +94,19 @@ pub fn draw(
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
-
- use super::VERSION;
use crate::{
app_error::AppError,
config::{AppColors, Keymap},
- ui::draw_blocks::tests::{expected_to_vec, get_result, test_setup},
+ ui::draw_blocks::tests::{get_result, test_setup},
};
use crossterm::event::KeyCode;
+ use insta::assert_snapshot;
use ratatui::style::Color;
#[test]
/// Test that the error popup is centered, red background, white border, white text, and displays the correct text
fn test_draw_blocks_error_docker_connect_error() {
- let (w, h) = (46, 9);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(46, 9, true, true);
setup
.terminal
@@ -122,34 +120,15 @@ mod tests {
);
})
.unwrap();
-
- let version_row = format!(" │ oxker::v{VERSION} closing in 04 seconds │ ");
- let expected = [
- " ",
- " ╭───────────────── Error ──────────────────╮ ",
- " │ │ ",
- " │ Unable to access docker daemon │ ",
- " │ │ ",
- version_row.as_str(),
- " │ │ ",
- " ╰──────────────────────────────────────────╯ ",
- " ",
- ];
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
- match (row_index, result_cell_index) {
- (0 | 8, _) | (1..=7, 0 | 45) => {
- assert_eq!(result_cell.bg, Color::Reset);
- assert_eq!(result_cell.fg, Color::Reset);
- }
- _ => {
- assert_eq!(result_cell.bg, Color::Red);
- assert_eq!(result_cell.fg, Color::White);
- }
+ if let (0 | 8, _) = (row_index, result_cell_index) {
+ assert_eq!(result_cell.bg, Color::Reset);
+ assert_eq!(result_cell.fg, Color::Reset);
+ } else {
+ assert_eq!(result_cell.bg, Color::Red);
+ assert_eq!(result_cell.fg, Color::White);
}
}
}
@@ -158,8 +137,7 @@ mod tests {
#[test]
/// Test that the clearable error popup is centered, red background, white border, white text, and displays the correct text
fn test_draw_blocks_error_clearable_error() {
- let (w, h) = (39, 11);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(39, 11, true, true);
setup
.terminal
@@ -174,25 +152,10 @@ mod tests {
})
.unwrap();
- let expected = [
- " ",
- " ╭────────────── Error ──────────────╮ ",
- " │ │ ",
- " │ Unable to exec into container │ ",
- " │ │ ",
- " │ ( c ) clear error │ ",
- " │ │ ",
- " │ ( q ) quit oxker │ ",
- " │ │ ",
- " ╰───────────────────────────────────╯ ",
- " ",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
match (row_index, result_cell_index) {
(0 | 10, _) | (1..=9, 0 | 38) => {
assert_eq!(result_cell.bg, Color::Reset);
@@ -211,8 +174,7 @@ mod tests {
#[test]
/// Custom colors applied to the error popup correctly
fn test_draw_blocks_error_custom_colors() {
- let (w, h) = (39, 11);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(39, 11, true, true);
let mut colors = AppColors::new();
colors.popup_error.background = Color::Yellow;
@@ -225,25 +187,10 @@ mod tests {
})
.unwrap();
- let expected = [
- " ",
- " ╭────────────── Error ──────────────╮ ",
- " │ │ ",
- " │ Unable to exec into container │ ",
- " │ │ ",
- " │ ( c ) clear error │ ",
- " │ │ ",
- " │ ( q ) quit oxker │ ",
- " │ │ ",
- " ╰───────────────────────────────────╯ ",
- " ",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
match (row_index, result_cell_index) {
(0 | 10, _) | (1..=9, 0 | 38) => {
assert_eq!(result_cell.bg, Color::Reset);
@@ -260,10 +207,9 @@ mod tests {
}
#[test]
- /// Custom keymap applied correct with both 1 and 2 definitions
+ /// Custom keymap applied correctly
fn test_draw_blocks_error_custom_keymap() {
- let (w, h) = (39, 11);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(39, 11, true, true);
let mut keymap = Keymap::new();
keymap.clear = (KeyCode::BackTab, None);
@@ -275,27 +221,12 @@ mod tests {
super::draw(AppColors::new(), &AppError::DockerExec, f, &keymap, None);
})
.unwrap();
-
- let expected = [
- " ",
- " ╭────────────── Error ──────────────╮ ",
- " │ │ ",
- " │ Unable to exec into container │ ",
- " │ │ ",
- " │ ( Back Tab ) clear error │ ",
- " │ │ ",
- " │ ( F4 ) quit oxker │ ",
- " │ │ ",
- " ╰───────────────────────────────────╯ ",
- " ",
- ];
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
+ assert_snapshot!(setup.terminal.backend());
+ }
+ #[test]
+ /// Custom keymap applied with two definitions for each option
+ fn test_draw_blocks_error_custom_keymap_two_definitions() {
+ let mut setup = test_setup(39, 11, true, true);
let mut keymap = Keymap::new();
keymap.clear = (KeyCode::BackTab, Some(KeyCode::Char('m')));
@@ -307,27 +238,13 @@ mod tests {
super::draw(AppColors::new(), &AppError::DockerExec, f, &keymap, None);
})
.unwrap();
+ assert_snapshot!(setup.terminal.backend());
+ }
- let expected = [
- " ",
- " ╭────────────── Error ──────────────╮ ",
- " │ │ ",
- " │ Unable to exec into container │ ",
- " │ │ ",
- " │ ( Back Tab | m ) clear error │ ",
- " │ │ ",
- " │ ( F4 | End ) quit oxker │ ",
- " │ │ ",
- " ╰───────────────────────────────────╯ ",
- " ",
- ];
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
+ #[test]
+ /// Custom keymap applied correctly, with 1 definition for the first option, and 2 definitions for the other
+ fn test_draw_blocks_error_custom_keymap_one_two_definitions() {
+ let mut setup = test_setup(39, 11, true, true);
let mut keymap = Keymap::new();
keymap.quit = (KeyCode::F(4), Some(KeyCode::End));
@@ -338,26 +255,6 @@ mod tests {
super::draw(AppColors::new(), &AppError::DockerExec, f, &keymap, None);
})
.unwrap();
-
- let expected = [
- " ",
- " ╭────────────── Error ──────────────╮ ",
- " │ │ ",
- " │ Unable to exec into container │ ",
- " │ │ ",
- " │ ( c ) clear error │ ",
- " │ │ ",
- " │ ( F4 | End ) quit oxker │ ",
- " │ │ ",
- " ╰───────────────────────────────────╯ ",
- " ",
- ];
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
+ assert_snapshot!(setup.terminal.backend());
}
}
diff --git a/src/ui/draw_blocks/filter.rs b/src/ui/draw_blocks/filter.rs
index 5e39cb4..cf62973 100644
--- a/src/ui/draw_blocks/filter.rs
+++ b/src/ui/draw_blocks/filter.rs
@@ -71,23 +71,22 @@ pub fn draw(area: Rect, colors: AppColors, frame: &mut Frame, fd: &FrameData) {
#[allow(clippy::unwrap_used)]
mod tests {
+ use insta::assert_snapshot;
use ratatui::style::{Color, Modifier};
use crate::{
config::AppColors,
ui::{
FrameData,
- draw_blocks::tests::{expected_to_vec, get_result, test_setup},
+ draw_blocks::tests::{get_result, test_setup},
},
};
#[test]
- #[allow(clippy::cognitive_complexity, clippy::too_many_lines)]
/// Filter row is drawn correctly & colors are correct
/// Colours change when filter_by option is changed
fn test_draw_blocks_filter_row() {
- let (w, h) = (140, 1);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(140, 1, true, true);
setup
.gui_state
@@ -100,15 +99,10 @@ mod tests {
})
.unwrap();
- let expected = [
- " Esc clear ← by → Name Image Status All term: ",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (_, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
match result_cell_index {
0..=4 | 12..=19 => {
assert_eq!(result_cell.bg, Color::Magenta);
@@ -134,6 +128,22 @@ mod tests {
}
}
}
+ }
+ #[test]
+ /// Colours change when filter_by option is changed
+ fn test_draw_blocks_filter_row_text() {
+ let mut setup = test_setup(140, 1, true, true);
+
+ setup
+ .gui_state
+ .lock()
+ .status_push(crate::ui::Status::Filter);
+ setup
+ .terminal
+ .draw(|f| {
+ super::draw(setup.area, AppColors::new(), f, &setup.fd);
+ })
+ .unwrap();
// Test when char added to search term
setup.app_data.lock().filter_term_push('c');
@@ -147,15 +157,10 @@ mod tests {
})
.unwrap();
- let expected = [
- " Esc clear ← by → Name Image Status All term: cd ",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (_, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
match result_cell_index {
0..=4 | 12..=19 => {
assert_eq!(result_cell.bg, Color::Magenta);
@@ -181,35 +186,39 @@ mod tests {
}
}
}
+ }
- // Test when filter_by changes
+ #[test]
+ /// Colours change when filter_by option is changed
+ fn test_draw_blocks_filter_row_filter_by() {
+ let mut setup = test_setup(140, 1, true, true);
+
+ setup
+ .gui_state
+ .lock()
+ .status_push(crate::ui::Status::Filter);
setup.app_data.lock().filter_by_next();
- let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup
.terminal
.draw(|f| {
- super::draw(setup.area, AppColors::new(), f, &fd);
+ super::draw(setup.area, AppColors::new(), f, &setup.fd);
})
.unwrap();
- let expected = [
- " Esc clear ← by → Name Image Status All term: cd ",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (_, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
match result_cell_index {
0..=4 | 12..=19 => {
assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::Black);
}
- 5..=11 | 21..=26 | 34..=46 | 54..=55 => {
+ 5..=11 | 27..=46 => {
assert_eq!(result_cell.bg, Color::Reset);
assert_eq!(result_cell.fg, Color::Gray);
}
- 27..=33 => {
+ 21..=26 => {
assert_eq!(result_cell.bg, Color::Gray);
assert_eq!(result_cell.fg, Color::Black);
}
@@ -230,8 +239,7 @@ mod tests {
#[test]
/// Make sure custom colors are applied
fn test_draw_blocks_filter_row_custom_colors() {
- let (w, h) = (140, 1);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(140, 1, true, true);
setup
.gui_state
@@ -256,14 +264,10 @@ mod tests {
})
.unwrap();
- let expected = [
- " Esc clear ← by → Name Image Status All term: cd ",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (_, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
match result_cell_index {
0..=4 | 12..=19 => {
assert_eq!(result_cell.bg, Color::Blue);
diff --git a/src/ui/draw_blocks/headers.rs b/src/ui/draw_blocks/headers.rs
index 33ec2f3..792ccf4 100644
--- a/src/ui/draw_blocks/headers.rs
+++ b/src/ui/draw_blocks/headers.rs
@@ -46,8 +46,8 @@ fn gen_header_block<'a>(colors: AppColors, fd: &FrameData, header: Header) -> (C
SortedOrder::Desc => suffix = " ▼",
}
color = colors.headers_bar.text_selected;
- };
- };
+ }
+ }
(color, suffix)
}
@@ -222,6 +222,7 @@ mod tests {
use std::ops::RangeInclusive;
use crossterm::event::KeyCode;
+ use insta::assert_snapshot;
use ratatui::style::Color;
use uuid::Uuid;
@@ -230,22 +231,17 @@ mod tests {
config::{AppColors, Keymap},
ui::{
FrameData, Status,
- draw_blocks::tests::{expected_to_vec, get_result, test_setup},
+ draw_blocks::tests::{TuiTestSetup, get_result, test_setup},
},
};
#[test]
/// Heading back only has show/exit help when no containers, correctly coloured
- fn test_draw_blocks_headers_no_containers() {
- let (w, h) = (140, 1);
- let mut setup = test_setup(w, h, true, true);
+ fn test_draw_blocks_headers_no_containers_show_help() {
+ let mut setup = test_setup(140, 1, true, true);
setup.app_data.lock().containers = StatefulList::new(vec![]);
- let mut fd = FrameData::from((&setup.app_data, &setup.gui_state));
-
- let expected = [
- " ( h ) show help ",
- ];
+ let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup
.terminal
@@ -261,19 +257,24 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
+ assert_snapshot!(setup.terminal.backend());
+
+ for (_, result_row) in get_result(&setup) {
+ for result_cell in result_row {
assert_eq!(result_cell.bg, Color::Magenta);
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.fg, Color::Gray,);
}
}
+ }
+ #[test]
+ /// Heading back only has show/exit help when no containers, correctly coloured
+ fn test_draw_blocks_headers_no_containers_exit_help() {
+ let mut setup = test_setup(140, 1, true, true);
+ setup.app_data.lock().containers = StatefulList::new(vec![]);
+
+ let mut fd = FrameData::from((&setup.app_data, &setup.gui_state));
fd.status.insert(Status::Help);
- let expected = [
- " ( h ) exit help ",
- ];
setup
.terminal
.draw(|f| {
@@ -287,11 +288,10 @@ mod tests {
);
})
.unwrap();
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ for (_, result_row) in get_result(&setup) {
+ for result_cell in result_row {
assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::Black);
}
@@ -301,13 +301,8 @@ mod tests {
#[test]
/// Show all headings when containers present, colors valid
fn test_draw_blocks_headers_some_containers() {
- let (w, h) = (140, 1);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(140, 1, true, true);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
-
- let expected = [
- " name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- ];
setup
.terminal
.draw(|f| {
@@ -321,11 +316,10 @@ mod tests {
);
})
.unwrap();
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (_, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(
result_cell.fg,
@@ -343,12 +337,9 @@ mod tests {
#[test]
/// Only show the headings that fit the reduced-in-size header section
fn test_draw_blocks_headers_some_containers_reduced_width() {
- let (w, h) = (80, 1);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(80, 1, true, true);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
- let expected =
- [" name state status cpu ( h ) show help "];
setup
.terminal
.draw(|f| {
@@ -363,10 +354,9 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (_, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(
result_cell.fg,
@@ -381,204 +371,14 @@ mod tests {
}
}
- #[test]
- /// Test all combination of headers & sort by
- #[allow(clippy::too_many_lines)]
- fn test_draw_blocks_headers_sort_containers() {
- let (w, h) = (140, 1);
- let mut setup = test_setup(w, h, true, true);
- let mut fd = FrameData::from((&setup.app_data, &setup.gui_state));
-
- // Actual test, used for each header and sorted type
- let mut test =
- |expected: &[&str], range: RangeInclusive, x: (Header, SortedOrder)| {
- fd.sorted_by = Some(x);
-
- setup
- .terminal
- .draw(|f| {
- super::draw(
- setup.area,
- AppColors::new(),
- f,
- &fd,
- &setup.gui_state,
- &Keymap::new(),
- );
- })
- .unwrap();
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
- assert_eq!(result_cell.bg, Color::Magenta);
- assert_eq!(
- result_cell.fg,
- match result_cell_index {
- 0..=3 => Color::White,
- 122..=139 => Color::Gray,
- // given range | help section
- x if range.contains(&x) => Color::Gray,
- 112..=121 => Color::Reset,
- _ => Color::Black,
- }
- );
- }
- }
- };
-
- // Name
- test(
- &[
- " name ▲ state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- ],
- 1..=17,
- (Header::Name, SortedOrder::Asc),
- );
- test(
- &[
- " name ▼ state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- ],
- 1..=17,
- (Header::Name, SortedOrder::Desc),
- );
- // state
- test(
- &[
- " name state ▲ status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- ],
- 18..=29,
- (Header::State, SortedOrder::Asc),
- );
- test(
- &[
- " name state ▼ status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- ],
- 18..=29,
- (Header::State, SortedOrder::Desc),
- );
- // status
- test(
- &[
- " name state status ▲ cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- ],
- 30..=41,
- (Header::Status, SortedOrder::Asc),
- );
- test(
- &[
- " name state status ▼ cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- ],
- 30..=41,
- (Header::Status, SortedOrder::Desc),
- );
- // cpu
- test(
- &[
- " name state status cpu ▲ memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- ],
- 42..=50,
- (Header::Cpu, SortedOrder::Asc),
- );
- test(
- &[
- " name state status cpu ▼ memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- ],
- 42..=50,
- (Header::Cpu, SortedOrder::Desc),
- );
- // memory
- test(
- &[
- " name state status cpu memory/limit ▲ id image ↓ rx ↑ tx ( h ) show help ",
- ],
- 51..=70,
- (Header::Memory, SortedOrder::Asc),
- );
- test(
- &[
- " name state status cpu memory/limit ▼ id image ↓ rx ↑ tx ( h ) show help ",
- ],
- 51..=70,
- (Header::Memory, SortedOrder::Desc),
- );
- //id
- test(
- &[
- " name state status cpu memory/limit id ▲ image ↓ rx ↑ tx ( h ) show help ",
- ],
- 71..=81,
- (Header::Id, SortedOrder::Asc),
- );
- test(
- &[
- " name state status cpu memory/limit id ▼ image ↓ rx ↑ tx ( h ) show help ",
- ],
- 71..=81,
- (Header::Id, SortedOrder::Desc),
- );
- // image
- test(
- &[
- " name state status cpu memory/limit id image ▲ ↓ rx ↑ tx ( h ) show help ",
- ],
- 82..=91,
- (Header::Image, SortedOrder::Asc),
- );
- test(
- &[
- " name state status cpu memory/limit id image ▼ ↓ rx ↑ tx ( h ) show help ",
- ],
- 82..=91,
- (Header::Image, SortedOrder::Desc),
- );
- // rx
- test(
- &[
- " name state status cpu memory/limit id image ↓ rx ▲ ↑ tx ( h ) show help ",
- ],
- 92..=101,
- (Header::Rx, SortedOrder::Asc),
- );
- test(
- &[
- " name state status cpu memory/limit id image ↓ rx ▼ ↑ tx ( h ) show help ",
- ],
- 92..=101,
- (Header::Rx, SortedOrder::Desc),
- );
- // tx
- test(
- &[
- " name state status cpu memory/limit id image ↓ rx ↑ tx ▲ ( h ) show help ",
- ],
- 102..=111,
- (Header::Tx, SortedOrder::Asc),
- );
- test(
- &[
- " name state status cpu memory/limit id image ↓ rx ↑ tx ▼ ( h ) show help ",
- ],
- 102..=111,
- (Header::Tx, SortedOrder::Desc),
- );
- }
-
#[test]
/// Show animation
fn test_draw_blocks_headers_animation() {
- let (w, h) = (140, 1);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(140, 1, true, true);
let uuid = Uuid::new_v4();
setup.gui_state.lock().next_loading(uuid);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
- let expected = [
- " ⠙ name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- ];
-
setup
.terminal
.draw(|f| {
@@ -593,10 +393,9 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (_, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(
result_cell.fg,
@@ -614,8 +413,7 @@ mod tests {
#[test]
/// Custom colors are applied correctly
fn test_draw_blocks_headers_custom_colors() {
- let (w, h) = (140, 1);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(140, 1, true, true);
let uuid = Uuid::new_v4();
setup.gui_state.lock().next_loading(uuid);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
@@ -627,10 +425,6 @@ mod tests {
colors.headers_bar.text = Color::Blue;
colors.headers_bar.text_selected = Color::Yellow;
- let expected = [
- " ⠙ name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- ];
-
setup
.terminal
.draw(|f| {
@@ -638,10 +432,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+
+ for (_, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Black);
assert_eq!(
result_cell.fg,
@@ -657,18 +451,14 @@ mod tests {
}
#[test]
- /// Custom keymap for help panel is correctly display, with one and two definitions
- fn test_draw_blocks_headers_custom_keymap() {
- let (w, h) = (140, 1);
- let mut setup = test_setup(w, h, true, true);
+ /// Custom keymap for help panel is correctly display, with one definitions
+ fn test_draw_blocks_headers_custom_keymap_one_definition() {
+ let mut setup = test_setup(140, 1, true, true);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let mut keymap = Keymap::new();
keymap.toggle_help = (KeyCode::Char('T'), None);
- let expected = [
- " name state status cpu memory/limit id image ↓ rx ↑ tx ( T ) show help ",
- ];
setup
.terminal
.draw(|f| {
@@ -683,17 +473,17 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
+ assert_snapshot!(setup.terminal.backend());
+ }
+
+ #[test]
+ /// Custom keymap for help panel is correctly display, two definitions
+ fn test_draw_blocks_headers_custom_keymap_two_definitions() {
+ let mut setup = test_setup(140, 1, true, true);
+ let fd = FrameData::from((&setup.app_data, &setup.gui_state));
+ let mut keymap = Keymap::new();
keymap.toggle_help = (KeyCode::Char('T'), Some(KeyCode::Tab));
- let expected = [
- " name state status cpu memory/limit id image ↓ rx ↑ tx ( T | Tab ) show help ",
- ];
setup
.terminal
.draw(|f| {
@@ -708,11 +498,178 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ }
+
+ fn check_color(setup: &TuiTestSetup, range: RangeInclusive) {
+ for (_, result_row) in get_result(setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ assert_eq!(result_cell.bg, Color::Magenta);
+ assert_eq!(
+ result_cell.fg,
+ match result_cell_index {
+ 0..=3 => Color::White,
+ 122..=139 => Color::Gray,
+ // given range | help section
+ x if range.contains(&x) => Color::Gray,
+ 112..=121 => Color::Reset,
+ _ => Color::Black,
+ }
+ );
}
}
}
+
+ /// As a macro - headers test, check for asc/desc icon and colors
+ macro_rules! test_draw_blocks_headers_sort {
+ ($name:ident, $header:expr, $order:expr, $color_range:expr) => {
+ #[test]
+ fn $name() {
+ let mut setup = test_setup(140, 1, true, true);
+ let mut fd = FrameData::from((&setup.app_data, &setup.gui_state));
+ fd.sorted_by = Some(($header, $order));
+ setup
+ .terminal
+ .draw(|f| {
+ super::draw(
+ setup.area,
+ AppColors::new(),
+ f,
+ &fd,
+ &setup.gui_state,
+ &Keymap::new(),
+ );
+ })
+ .unwrap();
+ assert_snapshot!(setup.terminal.backend());
+ check_color(&setup, $color_range);
+ }
+ };
+ }
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_name_asc,
+ Header::Name,
+ SortedOrder::Asc,
+ 1..=17
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_name_desc,
+ Header::Name,
+ SortedOrder::Desc,
+ 1..=17
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_state_asc,
+ Header::State,
+ SortedOrder::Asc,
+ 18..=29
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_state_desc,
+ Header::State,
+ SortedOrder::Desc,
+ 18..=29
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_status_asc,
+ Header::Status,
+ SortedOrder::Asc,
+ 30..=41
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_status_desc,
+ Header::Status,
+ SortedOrder::Desc,
+ 30..=41
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_cpu_asc,
+ Header::Cpu,
+ SortedOrder::Asc,
+ 42..=50
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_cpu_desc,
+ Header::Cpu,
+ SortedOrder::Desc,
+ 42..=50
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_memory_asc,
+ Header::Memory,
+ SortedOrder::Asc,
+ 51..=70
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_memory_desc,
+ Header::Memory,
+ SortedOrder::Desc,
+ 51..=70
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_id_asc,
+ Header::Id,
+ SortedOrder::Asc,
+ 71..=81
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_id_desc,
+ Header::Id,
+ SortedOrder::Desc,
+ 71..=81
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_image_asc,
+ Header::Image,
+ SortedOrder::Asc,
+ 82..=91
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_image_desc,
+ Header::Image,
+ SortedOrder::Desc,
+ 82..=91
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_rx_asc,
+ Header::Rx,
+ SortedOrder::Asc,
+ 92..=101
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_rx_desc,
+ Header::Rx,
+ SortedOrder::Desc,
+ 92..=101
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_tx_asc,
+ Header::Tx,
+ SortedOrder::Asc,
+ 102..=111
+ );
+
+ test_draw_blocks_headers_sort!(
+ test_draw_blocks_headers_sort_containers_tx_desc,
+ Header::Tx,
+ SortedOrder::Desc,
+ 102..=111
+ );
}
diff --git a/src/ui/draw_blocks/help.rs b/src/ui/draw_blocks/help.rs
index a7dd43c..49b1d0e 100644
--- a/src/ui/draw_blocks/help.rs
+++ b/src/ui/draw_blocks/help.rs
@@ -279,7 +279,7 @@ impl HelpInfo {
km.toggle_help,
"toggle this help information - or click heading",
),
- or_secondary(km.toggle_help, "save logs to file"),
+ or_secondary(km.save_logs, "save logs to file"),
or_secondary(
km.toggle_mouse_capture,
"toggle mouse capture - if disabled, text on screen can be selected & copied",
@@ -415,21 +415,18 @@ pub fn draw(
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::too_many_lines)]
mod tests {
- use crate::{
- config::{AppColors, Keymap},
- ui::draw_blocks::VERSION,
- };
+ use crate::config::{AppColors, Keymap};
use crossterm::event::KeyCode;
+ use insta::assert_snapshot;
use jiff::tz::TimeZone;
use ratatui::style::{Color, Modifier};
- use crate::ui::draw_blocks::tests::{expected_to_vec, get_result, test_setup};
+ use crate::ui::draw_blocks::tests::{get_result, test_setup};
#[test]
/// This will cause issues once the version has more than the current 5 chars (0.5.0)
fn test_draw_blocks_help() {
- let (w, h) = (87, 33);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(87, 33, true, true);
let tz = setup.app_data.lock().config.timezone.clone();
setup
@@ -445,50 +442,10 @@ mod tests {
})
.unwrap();
- let version_row = format!(
- " ╭ {VERSION} ────────────────────────────────────────────────────────────────────────────╮ "
- );
- let expected = [
- " ",
- version_row.as_str(),
- " │ │ ",
- " │ 88 │ ",
- " │ 88 │ ",
- " │ 88 │ ",
- " │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ ",
- r#" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "#,
- r#" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "#,
- r#" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "#,
- r#" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "#,
- " │ │ ",
- " │ A simple tui to view & control docker containers │ ",
- " │ │ ",
- " │ ( tab ) or ( shift+tab ) change panels │ ",
- " │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ ",
- " │ ( enter ) send docker container command │ ",
- " │ ( e ) exec into a container │ ",
- " │ ( h ) toggle this help information - or click heading │ ",
- " │ ( s ) save logs to file │ ",
- " │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ ",
- " │ ( F1 ) or ( / ) enter filter mode │ ",
- " │ ( 0 ) stop sort │ ",
- " │ ( 1 - 9 ) sort by header - or click header │ ",
- " │ ( esc ) close dialog │ ",
- " │ ( q ) quit at any time │ ",
- " │ │ ",
- " │ currently an early work in progress, all and any input appreciated │ ",
- " │ https://github.com/mrjackwills/oxker │ ",
- " │ │ ",
- " │ │ ",
- " ╰───────────────────────────────────────────────────────────────────────────────────╯ ",
- " ",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
match (row_index, result_cell_index) {
// first & last row, and first & last char on each row, is reset/reset, making sure that the help info is centered in the given area
(0 | 32, _) | (0..=33, 0 | 86) => {
@@ -531,8 +488,7 @@ mod tests {
#[test]
/// Test that the help panel gets drawn with custom colors
fn test_draw_blocks_help_custom_colors() {
- let (w, h) = (87, 33);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(87, 33, true, true);
let mut colors = AppColors::new();
let tz = setup.app_data.lock().config.timezone.clone();
@@ -553,50 +509,10 @@ mod tests {
})
.unwrap();
- let version_row = format!(
- " ╭ {VERSION} ────────────────────────────────────────────────────────────────────────────╮ "
- );
- let expected = [
- " ",
- version_row.as_str(),
- " │ │ ",
- " │ 88 │ ",
- " │ 88 │ ",
- " │ 88 │ ",
- " │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ ",
- r#" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "#,
- r#" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "#,
- r#" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "#,
- r#" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "#,
- " │ │ ",
- " │ A simple tui to view & control docker containers │ ",
- " │ │ ",
- " │ ( tab ) or ( shift+tab ) change panels │ ",
- " │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ ",
- " │ ( enter ) send docker container command │ ",
- " │ ( e ) exec into a container │ ",
- " │ ( h ) toggle this help information - or click heading │ ",
- " │ ( s ) save logs to file │ ",
- " │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ ",
- " │ ( F1 ) or ( / ) enter filter mode │ ",
- " │ ( 0 ) stop sort │ ",
- " │ ( 1 - 9 ) sort by header - or click header │ ",
- " │ ( esc ) close dialog │ ",
- " │ ( q ) quit at any time │ ",
- " │ │ ",
- " │ currently an early work in progress, all and any input appreciated │ ",
- " │ https://github.com/mrjackwills/oxker │ ",
- " │ │ ",
- " │ │ ",
- " ╰───────────────────────────────────────────────────────────────────────────────────╯ ",
- " ",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
match (row_index, result_cell_index) {
// first & last row, and first & last char on each row, is reset/reset, making sure that the help info is centered in the given area
(0 | 32, _) | (0..=33, 0 | 86) => {
@@ -639,8 +555,7 @@ mod tests {
#[test]
/// Help panel will show custom keymap if in use, with one definition for each entry
fn test_draw_blocks_help_custom_keymap_one_definition() {
- let (w, h) = (98, 47);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(98, 47, true, true);
let input = Keymap {
clear: (KeyCode::Char('a'), None),
@@ -679,72 +594,13 @@ mod tests {
})
.unwrap();
- let version_row = format!(
- " ╭ {VERSION} ─────────────────────────────────────────────────────────────────────────────────────╮ "
- );
- let expected = [
- " ",
- version_row.as_str(),
- " │ │ ",
- " │ 88 │ ",
- " │ 88 │ ",
- " │ 88 │ ",
- " │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ ",
- r#" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "#,
- r#" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "#,
- r#" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "#,
- r#" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "#,
- " │ │ ",
- " │ A simple tui to view & control docker containers │ ",
- " │ │ ",
- " │ ( 0 ) select next panel │ ",
- " │ ( 2 ) select previous panel │ ",
- " │ ( q ) scroll list down by one │ ",
- " │ ( y ) scroll list up by one │ ",
- " │ ( o ) scroll list down by many │ ",
- " │ ( w ) scroll list by up many │ ",
- " │ ( s ) scroll list to end │ ",
- " │ ( u ) scroll list to start │ ",
- " │ ( enter ) send docker container command │ ",
- " │ ( g ) exec into a container │ ",
- " │ ( Home ) toggle this help information - or click heading │ ",
- " │ ( Home ) save logs to file │ ",
- " │ ( Page Down ) toggle mouse capture - if disabled, text on screen can be selected & copied │ ",
- " │ ( i ) enter filter mode │ ",
- " │ ( Up ) reset container sorting │ ",
- " │ ( 4 ) sort containers by name │ ",
- " │ ( 6 ) sort containers by state │ ",
- " │ ( 8 ) sort containers by status │ ",
- " │ ( F1 ) sort containers by cpu │ ",
- " │ ( # ) sort containers by memory │ ",
- " │ ( / ) sort containers by id │ ",
- " │ ( , ) sort containers by image │ ",
- " │ ( . ) sort containers by rx │ ",
- " │ ( Backspace ) sort containers by tx │ ",
- " │ ( a ) close dialog │ ",
- " │ ( k ) quit at any time │ ",
- " │ │ ",
- " │ currently an early work in progress, all and any input appreciated │ ",
- " │ https://github.com/mrjackwills/oxker │ ",
- " │ │ ",
- " │ │ ",
- " ╰────────────────────────────────────────────────────────────────────────────────────────────╯ ",
- " ",
- ];
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
+ assert_snapshot!(setup.terminal.backend());
}
#[test]
/// Help panel will show custom keymap if in use, with two definition for each entry
fn test_draw_blocks_help_custom_keymap_two_definitions() {
- let (w, h) = (110, 47);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(110, 47, true, true);
let keymap = Keymap {
clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))),
@@ -783,72 +639,13 @@ mod tests {
})
.unwrap();
- let version_row = format!(
- " ╭ {VERSION} ───────────────────────────────────────────────────────────────────────────────────────────────────╮ "
- );
- let expected = [
- " ",
- version_row.as_str(),
- " │ │ ",
- " │ 88 │ ",
- " │ 88 │ ",
- " │ 88 │ ",
- " │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ ",
- r#" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "#,
- r#" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "#,
- r#" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "#,
- r#" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "#,
- " │ │ ",
- " │ A simple tui to view & control docker containers │ ",
- " │ │ ",
- " │ ( 0 ) or ( 1 ) select next panel │ ",
- " │ ( 2 ) or ( 3 ) select previous panel │ ",
- " │ ( q ) or ( r ) scroll list down by one │ ",
- " │ ( y ) or ( z ) scroll list up by one │ ",
- " │ ( o ) or ( p ) scroll list down by many │ ",
- " │ ( w ) or ( x ) scroll list by up many │ ",
- " │ ( s ) or ( t ) scroll list to end │ ",
- " │ ( u ) or ( v ) scroll list to start │ ",
- " │ ( enter ) send docker container command │ ",
- " │ ( g ) or ( h ) exec into a container │ ",
- " │ ( Home ) or ( Del ) toggle this help information - or click heading │ ",
- " │ ( Home ) or ( Del ) save logs to file │ ",
- " │ ( Page Down ) or ( Page Up ) toggle mouse capture - if disabled, text on screen can be selected & copied │ ",
- " │ ( i ) or ( j ) enter filter mode │ ",
- " │ ( Up ) or ( Down ) reset container sorting │ ",
- " │ ( 4 ) or ( 5 ) sort containers by name │ ",
- " │ ( 6 ) or ( 7 ) sort containers by state │ ",
- " │ ( 8 ) or ( 9 ) sort containers by status │ ",
- " │ ( F1 ) or ( F12 ) sort containers by cpu │ ",
- " │ ( # ) or ( - ) sort containers by memory │ ",
- " │ ( / ) or ( = ) sort containers by id │ ",
- r" │ ( , ) or ( \ ) sort containers by image │ ",
- " │ ( . ) or ( ] ) sort containers by rx │ ",
- " │ ( Backspace ) or ( Back Tab ) sort containers by tx │ ",
- " │ ( a ) or ( b ) close dialog │ ",
- " │ ( k ) or ( l ) quit at any time │ ",
- " │ │ ",
- " │ currently an early work in progress, all and any input appreciated │ ",
- " │ https://github.com/mrjackwills/oxker │ ",
- " │ │ ",
- " │ │ ",
- " ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ",
- " ",
- ];
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
+ assert_snapshot!(setup.terminal.backend());
}
#[test]
/// Help panel will show custom keymap if in use, with either one or two definition for each entry
fn test_draw_blocks_help_one_and_two_definitions() {
- let (w, h) = (110, 47);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(110, 47, true, true);
let keymap = Keymap {
clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))),
@@ -889,71 +686,12 @@ mod tests {
})
.unwrap();
- let version_row = format!(
- " ╭ {VERSION} ───────────────────────────────────────────────────────────────────────────────────────────────────╮ "
- );
- let expected = [
- " ",
- version_row.as_str(),
- " │ │ ",
- " │ 88 │ ",
- " │ 88 │ ",
- " │ 88 │ ",
- " │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ ",
- r#" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "#,
- r#" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "#,
- r#" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "#,
- r#" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "#,
- " │ │ ",
- " │ A simple tui to view & control docker containers │ ",
- " │ │ ",
- " │ ( 0 ) select next panel │ ",
- " │ ( 2 ) or ( 3 ) select previous panel │ ",
- " │ ( q ) or ( r ) scroll list down by one │ ",
- " │ ( y ) or ( z ) scroll list up by one │ ",
- " │ ( o ) scroll list down by many │ ",
- " │ ( w ) scroll list by up many │ ",
- " │ ( s ) scroll list to end │ ",
- " │ ( u ) or ( v ) scroll list to start │ ",
- " │ ( enter ) send docker container command │ ",
- " │ ( g ) exec into a container │ ",
- " │ ( Home ) toggle this help information - or click heading │ ",
- " │ ( Home ) save logs to file │ ",
- " │ ( Page Down ) or ( Page Up ) toggle mouse capture - if disabled, text on screen can be selected & copied │ ",
- " │ ( i ) or ( j ) enter filter mode │ ",
- " │ ( Up ) or ( Down ) reset container sorting │ ",
- " │ ( 4 ) sort containers by name │ ",
- " │ ( 6 ) or ( 7 ) sort containers by state │ ",
- " │ ( 8 ) sort containers by status │ ",
- " │ ( F1 ) or ( F12 ) sort containers by cpu │ ",
- " │ ( # ) sort containers by memory │ ",
- " │ ( / ) or ( = ) sort containers by id │ ",
- " │ ( , ) sort containers by image │ ",
- " │ ( . ) or ( ] ) sort containers by rx │ ",
- " │ ( Backspace ) sort containers by tx │ ",
- " │ ( a ) or ( b ) close dialog │ ",
- " │ ( k ) quit at any time │ ",
- " │ │ ",
- " │ currently an early work in progress, all and any input appreciated │ ",
- " │ https://github.com/mrjackwills/oxker │ ",
- " │ │ ",
- " │ │ ",
- " ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ",
- " ",
- ];
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
+ assert_snapshot!(setup.terminal.backend());
}
#[test]
fn test_draw_blocks_help_show_timezone() {
- let (w, h) = (87, 35);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(87, 35, true, true);
setup
.terminal
@@ -968,49 +706,9 @@ mod tests {
})
.unwrap();
- let version_row = format!(
- " ╭ {VERSION} ────────────────────────────────────────────────────────────────────────────╮ "
- );
- let expected = [
- " ",
- version_row.as_str(),
- " │ │ ",
- " │ 88 │ ",
- " │ 88 │ ",
- " │ 88 │ ",
- " │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ ",
- r#" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "#,
- r#" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "#,
- r#" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "#,
- r#" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "#,
- " │ │ ",
- " │ A simple tui to view & control docker containers │ ",
- " │ │ ",
- " │ logs timezone: Asia/Tokyo │ ",
- " │ │ ",
- " │ ( tab ) or ( shift+tab ) change panels │ ",
- " │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ ",
- " │ ( enter ) send docker container command │ ",
- " │ ( e ) exec into a container │ ",
- " │ ( h ) toggle this help information - or click heading │ ",
- " │ ( s ) save logs to file │ ",
- " │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ ",
- " │ ( F1 ) or ( / ) enter filter mode │ ",
- " │ ( 0 ) stop sort │ ",
- " │ ( 1 - 9 ) sort by header - or click header │ ",
- " │ ( esc ) close dialog │ ",
- " │ ( q ) quit at any time │ ",
- " │ │ ",
- " │ currently an early work in progress, all and any input appreciated │ ",
- " │ https://github.com/mrjackwills/oxker │ ",
- " │ │ ",
- " │ │ ",
- " ╰───────────────────────────────────────────────────────────────────────────────────╯ ",
- " ",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
match (row_index, result_cell_index) {
(14, 31..=45) => {
@@ -1021,7 +719,6 @@ mod tests {
}
_ => (),
}
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
}
}
}
diff --git a/src/ui/draw_blocks/info.rs b/src/ui/draw_blocks/info.rs
index 5016a46..89094d6 100644
--- a/src/ui/draw_blocks/info.rs
+++ b/src/ui/draw_blocks/info.rs
@@ -57,30 +57,19 @@ pub fn draw(
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
+ use insta::assert_snapshot;
use ratatui::style::Color;
use crate::{
config::AppColors,
- ui::draw_blocks::tests::{expected_to_vec, get_result, test_setup},
+ ui::draw_blocks::tests::{get_result, test_setup},
};
#[test]
/// Info box drawn in bottom right
fn test_draw_blocks_info() {
- let (w, h) = (45, 9);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(45, 9, true, true);
- let expected = [
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " test ",
- " ",
- ];
let colors = setup.app_data.lock().config.app_colors;
setup
@@ -96,11 +85,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
+ for (result_cell_index, result_cell) in result_row.iter().enumerate() {
let (bg, fg) = match (row_index, result_cell_index) {
(6..=8, 32..=44) => (Color::Blue, Color::White),
_ => (Color::Reset, Color::Reset),
@@ -114,23 +102,11 @@ mod tests {
#[test]
/// Info box drawn in bottom right with custom colors applied
fn test_draw_blocks_info_custom_color() {
- let (w, h) = (45, 9);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(45, 9, true, true);
let mut colors = AppColors::new();
colors.popup_info.background = Color::Red;
colors.popup_info.text = Color::Black;
- let expected = [
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " test ",
- " ",
- ];
setup
.terminal
@@ -145,11 +121,9 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
-
let (bg, fg) = match (row_index, result_cell_index) {
(6..=8, 32..=44) => (Color::Red, Color::Black),
_ => (Color::Reset, Color::Reset),
diff --git a/src/ui/draw_blocks/logs.rs b/src/ui/draw_blocks/logs.rs
index 2510f92..835b01a 100644
--- a/src/ui/draw_blocks/logs.rs
+++ b/src/ui/draw_blocks/logs.rs
@@ -74,6 +74,7 @@ pub fn draw(
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
+ use insta::assert_snapshot;
use ratatui::style::{Color, Modifier};
use uuid::Uuid;
@@ -82,26 +83,15 @@ mod tests {
config::AppColors,
ui::{
FrameData, Status,
- draw_blocks::tests::{
- BORDER_CHARS, expected_to_vec, get_result, insert_logs, test_setup,
- },
+ draw_blocks::tests::{BORDER_CHARS, get_result, insert_logs, test_setup},
},
};
#[test]
/// No logs, panel unselected, then selected, border color changes correctly
fn test_draw_blocks_logs_none() {
- let (w, h) = (35, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(35, 6, true, true);
- let expected = [
- "╭ Logs - container_1 - image_1 ───╮",
- "│ no logs found │",
- "│ │",
- "│ │",
- "│ │",
- "╰─────────────────────────────────╯",
- ];
let colors = setup.app_data.lock().config.app_colors;
setup
@@ -117,11 +107,10 @@ mod tests {
);
})
.unwrap();
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
match (row_index, result_cell_index) {
(0 | 5, 0..=34) | (1..=4, 0) | (1..=5, 34) => {
assert_eq!(result_cell.bg, Color::Reset);
@@ -154,10 +143,8 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ for (_, result_row) in get_result(&setup) {
+ for result_cell in result_row {
if BORDER_CHARS.contains(&result_cell.symbol()) {
assert_eq!(result_cell.fg, Color::LightCyan);
}
@@ -166,22 +153,12 @@ mod tests {
}
#[test]
- /// Parsing logs, spinner visible, and then animates by one frame
- fn test_draw_blocks_logs_parsing() {
- let (w, h) = (32, 6);
- let mut setup = test_setup(w, h, true, true);
+ /// Parsing logs, first frame spinner visible
+ fn test_draw_blocks_logs_parsing_frame_one() {
+ let mut setup = test_setup(32, 6, true, true);
let uuid = Uuid::new_v4();
setup.gui_state.lock().next_loading(uuid);
- let expected = [
- "╭ Logs - container_1 - image_1 ╮",
- "│ parsing logs ⠙ │",
- "│ │",
- "│ │",
- "│ │",
- "╰──────────────────────────────╯",
- ];
-
let mut fd = FrameData::from((&setup.app_data, &setup.gui_state));
fd.status.insert(Status::Init);
@@ -199,11 +176,9 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
-
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
match (row_index, result_cell_index) {
(0, 0..=31) | (1..=4, 0) | (1..=5, 31) | (5, 0..=30) => {
assert_eq!(result_cell.bg, Color::Reset);
@@ -216,19 +191,20 @@ mod tests {
}
}
}
+ }
+ #[test]
+ /// Parsing logs, second frame spinner visible
+ fn test_draw_blocks_logs_parsing_frame_two() {
+ let mut setup = test_setup(32, 6, true, true);
+ let uuid = Uuid::new_v4();
+ setup.gui_state.lock().next_loading(uuid);
+
+ let mut fd = FrameData::from((&setup.app_data, &setup.gui_state));
+ fd.status.insert(Status::Init);
// animation moved by one frame
setup.gui_state.lock().next_loading(uuid);
- let expected = [
- "╭ Logs - container_1 - image_1 ╮",
- "│ parsing logs ⠹ │",
- "│ │",
- "│ │",
- "│ │",
- "╰──────────────────────────────╯",
- ];
-
let mut fd = FrameData::from((&setup.app_data, &setup.gui_state));
fd.status.insert(Status::Init);
setup
@@ -245,10 +221,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
match (row_index, result_cell_index) {
(0, 0..=31) | (1..=4, 0) | (1..=5, 31) | (5, 0..=30) => {
assert_eq!(result_cell.bg, Color::Reset);
@@ -265,9 +241,8 @@ mod tests {
#[test]
/// Logs correct displayed, changing log state also draws correctly
- fn test_draw_blocks_logs_some() {
- let (w, h) = (36, 6);
- let mut setup = test_setup(w, h, true, true);
+ fn test_draw_blocks_logs_some_line_three() {
+ let mut setup = test_setup(36, 6, true, true);
insert_logs(&setup);
@@ -285,19 +260,12 @@ mod tests {
);
})
.unwrap();
- let expected = [
- "╭ Logs 3/3 - container_1 - image_1 ╮",
- "│ line 1 │",
- "│ line 2 │",
- "│▶ line 3 │",
- "│ │",
- "╰──────────────────────────────────╯",
- ];
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
+ // let expected_row = expected_to_vec(&expected, row_index);
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ // assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Reset);
if let (1..=4, 1..=34) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Reset);
@@ -311,7 +279,28 @@ mod tests {
}
}
}
- // Change selected log line
+ }
+ #[test]
+ /// Logs correct displayed, changing log state also draws correctly
+ fn test_draw_blocks_logs_some_line_two() {
+ let mut setup = test_setup(36, 6, true, true);
+
+ insert_logs(&setup);
+
+ let fd = FrameData::from((&setup.app_data, &setup.gui_state));
+ setup
+ .terminal
+ .draw(|f| {
+ super::draw(
+ &setup.app_data,
+ setup.area,
+ AppColors::new(),
+ f,
+ &fd,
+ &setup.gui_state,
+ );
+ })
+ .unwrap();
setup.app_data.lock().log_previous();
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
@@ -329,19 +318,10 @@ mod tests {
})
.unwrap();
- let expected = [
- "╭ Logs 2/3 - container_1 - image_1 ╮",
- "│ line 1 │",
- "│▶ line 2 │",
- "│ line 3 │",
- "│ │",
- "╰──────────────────────────────────╯",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Reset);
if let (1..=4, 1..=34) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Reset);
@@ -360,23 +340,13 @@ mod tests {
#[test]
/// Full (long) name displayed in logs border
fn test_draw_blocks_logs_long_name() {
- let (w, h) = (80, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(80, 6, true, true);
setup.app_data.lock().containers.items[0].name =
ContainerName::from("a_long_container_name_for_the_purposes_of_this_test");
setup.app_data.lock().containers.items[0].image =
ContainerImage::from("a_long_image_name_for_the_purposes_of_this_test");
insert_logs(&setup);
- let expected = [
- "╭ Logs 3/3 - a_long_container_name_for_the_purposes_of_this_test - a_long_image╮",
- "│ line 1 │",
- "│ line 2 │",
- "│▶ line 3 │",
- "│ │",
- "╰──────────────────────────────────────────────────────────────────────────────╯",
- ];
-
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup
@@ -393,30 +363,15 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
+ assert_snapshot!(setup.terminal.backend());
}
#[test]
fn test_draw_blocks_logs_custom_colors_parsing() {
- let (w, h) = (32, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(32, 6, true, true);
let uuid = Uuid::new_v4();
setup.gui_state.lock().next_loading(uuid);
- let expected = [
- "╭ Logs - container_1 - image_1 ╮",
- "│ parsing logs ⠙ │",
- "│ │",
- "│ │",
- "│ │",
- "╰──────────────────────────────╯",
- ];
-
let mut fd = FrameData::from((&setup.app_data, &setup.gui_state));
fd.status.insert(Status::Init);
@@ -438,11 +393,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Green);
if let (1..=4, 1..=29) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Black);
@@ -466,11 +420,8 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
-
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Reset);
if let (1..=4, 1..=29) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Reset);
@@ -482,17 +433,8 @@ mod tests {
#[test]
fn test_draw_blocks_logs_custom_colors_no_logs() {
- let (w, h) = (35, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(35, 6, true, true);
- let expected = [
- "╭ Logs - container_1 - image_1 ───╮",
- "│ no logs found │",
- "│ │",
- "│ │",
- "│ │",
- "╰─────────────────────────────────╯",
- ];
let mut colors = AppColors::new();
colors.logs.background = Color::Green;
colors.logs.text = Color::Black;
@@ -511,10 +453,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Green);
if let (1..=4, 1..=29) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Black);
@@ -537,10 +479,8 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Reset);
if let (1..=4, 1..=29) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Reset);
@@ -552,8 +492,7 @@ mod tests {
#[test]
/// Logs correct displayed with custom colors
fn test_draw_blocks_logs_custom_colors_logs() {
- let (w, h) = (36, 6);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(36, 6, true, true);
insert_logs(&setup);
let mut colors = setup.app_data.lock().config.app_colors;
@@ -576,19 +515,10 @@ mod tests {
);
})
.unwrap();
- let expected = [
- "╭ Logs 3/3 - container_1 - image_1 ╮",
- "│ line 1 │",
- "│ line 2 │",
- "│▶ line 3 │",
- "│ │",
- "╰──────────────────────────────────╯",
- ];
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Reset);
if let (1..=4, 1..=34) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Reset);
@@ -617,10 +547,8 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Green);
if let (1..=4, 1..=34) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Black);
diff --git a/src/ui/draw_blocks/mod.rs b/src/ui/draw_blocks/mod.rs
index 479306a..2a705b0 100644
--- a/src/ui/draw_blocks/mod.rs
+++ b/src/ui/draw_blocks/mod.rs
@@ -117,6 +117,7 @@ pub mod tests {
sync::Arc,
};
+ use insta::assert_snapshot;
use parking_lot::Mutex;
use ratatui::{Terminal, backend::TestBackend, layout::Rect, style::Color};
@@ -211,25 +212,17 @@ pub mod tests {
}
}
- /// Get a single row of String's from the expected data
- pub fn expected_to_vec(expected: &[&str], row_index: usize) -> Vec {
- expected[row_index]
- .chars()
- .map(|i| i.to_string())
- .collect::>()
- }
-
/// Just a shorthand for when enumerating over result cells
pub fn get_result(
setup: &TuiTestSetup,
- w: u16,
+ // w: u16,
) -> std::iter::Enumerate> {
setup
.terminal
.backend()
.buffer()
.content
- .chunks(usize::from(w))
+ .chunks(usize::from(setup.area.width))
.enumerate()
}
@@ -270,8 +263,7 @@ pub mod tests {
#[test]
/// Check that the whole layout is drawn correctly
fn test_draw_blocks_whole_layout() {
- let (w, h) = (160, 30);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(160, 30, true, true);
insert_chart_data(&setup);
insert_logs(&setup);
@@ -282,39 +274,6 @@ pub mod tests {
private: 8003,
public: Some(8003),
});
-
- let expected = [
- " name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- "╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮",
- "│⚪ container_1 ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 image_1 0.00 kB 0.00 kB ││▶ pause │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │",
- "│ ││ delete │",
- "│ ││ │",
- "│ ││ │",
- "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯",
- "╭ Logs 3/3 - container_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│ line 1 │",
- "│ line 2 │",
- "│▶ line 3 │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- "╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮",
- "│10.00%│ •••• ││100.00 kB│ ••• ││ ip private public│",
- "│ │ ••• • ││ │ ••• • ││ 8001 │",
- "│ │•• ••• ││ │•• ••• ││127.0.0.1 8003 8003│",
- "│ │ ││ │ ││ │",
- "╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯",
- ];
let colors = setup.app_data.lock().config.app_colors;
let keymap = setup.app_data.lock().config.keymap.clone();
@@ -326,20 +285,14 @@ pub mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
+ assert_snapshot!(setup.terminal.backend());
}
#[test]
#[allow(clippy::too_many_lines)]
/// Check that the whole layout is drawn correctly
- fn test_draw_blocks_whole_layout_with_filter() {
- let (w, h) = (160, 30);
- let mut setup = test_setup(w, h, true, true);
+ fn test_draw_blocks_whole_layout_with_filter_bar() {
+ let mut setup = test_setup(160, 30, true, true);
insert_chart_data(&setup);
insert_logs(&setup);
@@ -351,55 +304,8 @@ pub mod tests {
public: Some(8003),
});
- let expected = [
- " name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- "╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮",
- "│⚪ container_1 ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 image_1 0.00 kB 0.00 kB ││▶ pause │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │",
- "│ ││ delete │",
- "│ ││ │",
- "│ ││ │",
- "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯",
- "╭ Logs 3/3 - container_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│ line 1 │",
- "│ line 2 │",
- "│▶ line 3 │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- "╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮",
- "│10.00%│ •••• ││100.00 kB│ ••• ││ ip private public│",
- "│ │ ••• • ││ │ ••• • ││ 8001 │",
- "│ │•• ••• ││ │•• ••• ││ │",
- "│ │ ││ │ ││ │",
- "╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯",
- ];
- let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors;
let keymap = setup.app_data.lock().config.keymap.clone();
- setup
- .terminal
- .draw(|f| {
- draw_frame(&setup.app_data, colors, &keymap, f, &fd, &setup.gui_state);
- })
- .unwrap();
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
-
setup
.gui_state
.lock()
@@ -407,39 +313,6 @@ pub mod tests {
setup.app_data.lock().filter_term_push('r');
setup.app_data.lock().filter_term_push('_');
setup.app_data.lock().filter_term_push('1');
-
- let expected = [
- " name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- "╭ Containers 1/1 - filtered ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮",
- "│⚪ container_1 ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 image_1 0.00 kB 0.00 kB ││▶ pause │",
- "│ ││ restart │",
- "│ ││ stop │",
- "│ ││ delete │",
- "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯",
- "╭ Logs 3/3 - container_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮",
- "│ line 1 │",
- "│ line 2 │",
- "│▶ line 3 │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- "╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮",
- "│10.00%│ ••• ││100.00 kB│ •• ││ ip private public│",
- "│ │ •• • ││ │ •• • ││ 8001 │",
- "│ │ ••• • • ││ │ ••• • • ││ │",
- "│ │• •• ││ │• •• ││ │",
- "│ │ ││ │ ││ │",
- "╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯",
- " Esc clear ← by → Name Image Status All term: r_1 ",
- ];
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup
.terminal
@@ -448,19 +321,13 @@ pub mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
+ assert_snapshot!(setup.terminal.backend());
}
#[test]
/// Check that the whole layout is drawn correctly when have long container name and long image name
fn test_draw_blocks_whole_layout_long_name() {
- let (w, h) = (190, 30);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(190, 30, true, true);
insert_chart_data(&setup);
insert_logs(&setup);
@@ -477,38 +344,6 @@ pub mod tests {
setup.app_data.lock().containers.items[0].image =
ContainerImage::from("a_long_image_name_for_the_purposes_of_this_test");
- let expected = [
- " name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ",
- "╭ Containers 1/3 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭─────────────────╮",
- "│⚪ a_long_container_name_for_the… ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 a_long_image_name_for_the_pur… 0.00 kB 0.00 kB ││▶ pause │",
- "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │",
- "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │",
- "│ ││ delete │",
- "│ ││ │",
- "│ ││ │",
- "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰─────────────────╯",
- "╭ Logs 3/3 - a_long_container_name_for_the_purposes_of_this_test - a_long_image_name_for_the_purposes_of_this_test ──────────────────────────────────────────────────────────────────────────╮",
- "│ line 1 │",
- "│ line 2 │",
- "│▶ line 3 │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯",
- "╭───────────────────────────────── cpu 03.00% ─────────────────────────────────╮╭────────────────────────────── memory 30.00 kB ───────────────────────────────╮╭────────── ports ───────────╮",
- "│10.00%│ •••• ││100.00 kB│ ••••• ││ ip private public│",
- "│ │ •••• • ││ │ ••• • ││ 8001 │",
- "│ │••• •••• ││ │••• ••• ││127.0.0.1 8003 8003│",
- "│ │ ││ │ ││ │",
- "╰──────────────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────╯",
- ];
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors;
let keymap = setup.app_data.lock().config.keymap.clone();
@@ -519,11 +354,6 @@ pub mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
- }
- }
+ assert_snapshot!(setup.terminal.backend());
}
}
diff --git a/src/ui/draw_blocks/ports.rs b/src/ui/draw_blocks/ports.rs
index 61add99..fee6dff 100644
--- a/src/ui/draw_blocks/ports.rs
+++ b/src/ui/draw_blocks/ports.rs
@@ -76,6 +76,7 @@ pub fn draw(area: Rect, colors: AppColors, f: &mut Frame, fd: &FrameData) {
mod tests {
use std::net::{IpAddr, Ipv4Addr};
+ use insta::assert_snapshot;
use ratatui::style::{Color, Modifier};
use crate::{
@@ -83,17 +84,14 @@ mod tests {
config::AppColors,
ui::{
FrameData,
- draw_blocks::tests::{
- COLOR_ORANGE, COLOR_RX, COLOR_TX, expected_to_vec, get_result, test_setup,
- },
+ draw_blocks::tests::{COLOR_ORANGE, COLOR_RX, COLOR_TX, get_result, test_setup},
},
};
#[test]
/// Port section when container has no ports
fn test_draw_blocks_ports_no_ports() {
- let (w, h) = (30, 8);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(30, 8, true, true);
setup.app_data.lock().containers.items[0].ports = vec![];
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
@@ -103,22 +101,10 @@ mod tests {
super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd);
})
.unwrap();
+ assert_snapshot!(setup.terminal.backend());
- let expected = [
- "╭────────── ports ───────────╮",
- "│ no ports │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "╰────────────────────────────╯",
- ];
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
match (row_index, result_cell_index) {
(0, 11..=17) => {
assert_eq!(result_cell.bg, Color::Reset);
@@ -138,8 +124,24 @@ mod tests {
}
}
}
+ }
+
+ #[test]
+ /// Port section when container has no ports
+ // When state is "State::Running | State::Paused | State::Restarting, won't show "no ports"
+ fn test_draw_blocks_ports_no_ports_dead() {
+ let mut setup = test_setup(30, 8, true, true);
+ setup.app_data.lock().containers.items[0].ports = vec![];
+
+ let fd = FrameData::from((&setup.app_data, &setup.gui_state));
+ setup
+ .terminal
+ .draw(|f| {
+ super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd);
+ })
+ .unwrap();
+ // split
- // When state is "State::Running | State::Paused | State::Restarting, won't show "no ports"
setup.app_data.lock().containers.items[0].state = State::Dead;
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
@@ -150,21 +152,10 @@ mod tests {
})
.unwrap();
- let expected = [
- "╭────────── ports ───────────╮",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "╰────────────────────────────╯",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Reset);
if let (0, 11..=17) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Red);
@@ -180,8 +171,7 @@ mod tests {
#[test]
/// Port section when container has multiple ports
fn test_draw_blocks_ports_multiple_ports() {
- let (w, h) = (32, 8);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(32, 8, true, true);
setup.app_data.lock().containers.items[0]
.ports
.push(ContainerPorts {
@@ -204,22 +194,10 @@ mod tests {
super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd);
})
.unwrap();
+ assert_snapshot!(setup.terminal.backend());
- let expected = [
- "╭─────────── ports ────────────╮",
- "│ ip private public │",
- "│ 8001 │",
- "│ 8002 │",
- "│127.0.0.1 8003 8003 │",
- "│ │",
- "│ │",
- "╰──────────────────────────────╯",
- ];
-
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Reset);
match (row_index, result_cell_index) {
@@ -247,8 +225,7 @@ mod tests {
#[test]
/// Port section title color correct dependant on state
fn test_draw_blocks_ports_container_state() {
- let (w, h) = (32, 8);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(32, 8, true, true);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup
@@ -258,21 +235,10 @@ mod tests {
})
.unwrap();
- let expected = [
- "╭─────────── ports ────────────╮",
- "│ ip private public │",
- "│ 8001 │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "╰──────────────────────────────╯",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Reset);
if let (0, 12..=18) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Green);
@@ -290,10 +256,8 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Reset);
if let (0, 12..=18) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Yellow);
@@ -311,10 +275,8 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Reset);
if let (0, 12..=18) = (row_index, result_cell_index) {
assert_eq!(result_cell.fg, Color::Red);
@@ -327,8 +289,7 @@ mod tests {
#[test]
/// Custom colors applied to ports panel
fn test_draw_blocks_ports_custom_colors() {
- let (w, h) = (32, 8);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(32, 8, true, true);
let mut colors = AppColors::new();
colors.chart_ports.background = Color::Black;
@@ -345,21 +306,10 @@ mod tests {
})
.unwrap();
- let expected = [
- "╭─────────── ports ────────────╮",
- "│ ip private public │",
- "│ 8001 │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "╰──────────────────────────────╯",
- ];
+ assert_snapshot!(setup.terminal.backend());
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
+ for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
assert_eq!(result_cell.bg, Color::Black);
match (row_index, result_cell_index) {
@@ -387,8 +337,7 @@ mod tests {
#[test]
// Custom state color applied to ports panel title
fn test_draw_blocks_ports_custom_colors_state() {
- let (w, h) = (32, 8);
- let mut setup = test_setup(w, h, true, true);
+ let mut setup = test_setup(32, 8, true, true);
let mut colors = AppColors::new();
colors.container_state.dead = Color::Green;
@@ -402,17 +351,6 @@ mod tests {
colors.chart_ports.title = Color::DarkGray;
- let expected = [
- "╭─────────── ports ────────────╮",
- "│ ip private public │",
- "│ 8001 │",
- "│ │",
- "│ │",
- "│ │",
- "│ │",
- "╰──────────────────────────────╯",
- ];
-
for i in [
(State::Dead, Color::Green),
(State::Exited, Color::Magenta),
@@ -433,11 +371,10 @@ mod tests {
})
.unwrap();
- for (row_index, result_row) in get_result(&setup, w) {
- let expected_row = expected_to_vec(&expected, row_index);
- for (result_cell_index, result_cell) in result_row.iter().enumerate() {
- assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
+ // assert_snapshot!(setup.terminal.backend());
+ for (row_index, result_row) in get_result(&setup) {
+ for (result_cell_index, result_cell) in result_row.iter().enumerate() {
if row_index == 0 && (12..=18).contains(&result_cell_index) {
assert_eq!(result_cell.fg, i.1);
}
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_custom_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_custom_colors.snap
new file mode 100644
index 0000000..a92f2bd
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_custom_colors.snap
@@ -0,0 +1,14 @@
+---
+source: src/ui/draw_blocks/charts.rs
+expression: setup.terminal.backend()
+---
+"╭───────────── cpu 03.00% ─────────────╮╭────────── memory 30.00 kB ───────────╮"
+"│10.00%│ • ││100.00 kB│ •• │"
+"│ │ •• ││ │ •• │"
+"│ │ ••• ││ │ • • │"
+"│ │ • • ││ │ • • │"
+"│ │ • •• ││ │•• •• │"
+"│ │• • ││ │• • │"
+"│ │• • ││ │• • │"
+"│ │ ││ │ │"
+"╰──────────────────────────────────────╯╰──────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_dead.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_dead.snap
new file mode 100644
index 0000000..a92f2bd
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_dead.snap
@@ -0,0 +1,14 @@
+---
+source: src/ui/draw_blocks/charts.rs
+expression: setup.terminal.backend()
+---
+"╭───────────── cpu 03.00% ─────────────╮╭────────── memory 30.00 kB ───────────╮"
+"│10.00%│ • ││100.00 kB│ •• │"
+"│ │ •• ││ │ •• │"
+"│ │ ••• ││ │ • • │"
+"│ │ • • ││ │ • • │"
+"│ │ • •• ││ │•• •• │"
+"│ │• • ││ │• • │"
+"│ │• • ││ │• • │"
+"│ │ ││ │ │"
+"╰──────────────────────────────────────╯╰──────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_paused.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_paused.snap
new file mode 100644
index 0000000..a92f2bd
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_paused.snap
@@ -0,0 +1,14 @@
+---
+source: src/ui/draw_blocks/charts.rs
+expression: setup.terminal.backend()
+---
+"╭───────────── cpu 03.00% ─────────────╮╭────────── memory 30.00 kB ───────────╮"
+"│10.00%│ • ││100.00 kB│ •• │"
+"│ │ •• ││ │ •• │"
+"│ │ ••• ││ │ • • │"
+"│ │ • • ││ │ • • │"
+"│ │ • •• ││ │•• •• │"
+"│ │• • ││ │• • │"
+"│ │• • ││ │• • │"
+"│ │ ││ │ │"
+"╰──────────────────────────────────────╯╰──────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_running_none.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_running_none.snap
new file mode 100644
index 0000000..af71149
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_running_none.snap
@@ -0,0 +1,14 @@
+---
+source: src/ui/draw_blocks/charts.rs
+expression: setup.terminal.backend()
+---
+"╭───────────── cpu 00.00% ─────────────╮╭─────────── memory 0.00 kB ───────────╮"
+"│00.00%│ ││0.00 kB│ │"
+"│ │ ││ │ │"
+"│ │ ││ │ │"
+"│ │ ││ │ │"
+"│ │ ││ │ │"
+"│ │ ││ │ │"
+"│ │ ││ │ │"
+"│ │ ││ │ │"
+"╰──────────────────────────────────────╯╰──────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_running_some.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_running_some.snap
new file mode 100644
index 0000000..a92f2bd
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_running_some.snap
@@ -0,0 +1,14 @@
+---
+source: src/ui/draw_blocks/charts.rs
+expression: setup.terminal.backend()
+---
+"╭───────────── cpu 03.00% ─────────────╮╭────────── memory 30.00 kB ───────────╮"
+"│10.00%│ • ││100.00 kB│ •• │"
+"│ │ •• ││ │ •• │"
+"│ │ ••• ││ │ • • │"
+"│ │ • • ││ │ • • │"
+"│ │ • •• ││ │•• •• │"
+"│ │• • ││ │• • │"
+"│ │• • ││ │• • │"
+"│ │ ││ │ │"
+"╰──────────────────────────────────────╯╰──────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_custom_colors_paused.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_custom_colors_paused.snap
new file mode 100644
index 0000000..8aaca4d
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_custom_colors_paused.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/commands.rs
+expression: setup.terminal.backend()
+---
+"╭──────────╮"
+"│ resume │"
+"│▶ stop │"
+"│ delete │"
+"│ │"
+"╰──────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_custom_colors_running.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_custom_colors_running.snap
new file mode 100644
index 0000000..a4f3838
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_custom_colors_running.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/commands.rs
+expression: setup.terminal.backend()
+---
+"╭──────────╮"
+"│▶ pause │"
+"│ restart │"
+"│ stop │"
+"│ delete │"
+"╰──────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_none.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_none.snap
new file mode 100644
index 0000000..9af74a1
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_none.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/commands.rs
+expression: setup.terminal.backend()
+---
+"╭──────────╮"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_panel_selected_color.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_panel_selected_color.snap
new file mode 100644
index 0000000..a4f3838
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_panel_selected_color.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/commands.rs
+expression: setup.terminal.backend()
+---
+"╭──────────╮"
+"│▶ pause │"
+"│ restart │"
+"│ stop │"
+"│ delete │"
+"╰──────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_some.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_some.snap
new file mode 100644
index 0000000..a4f3838
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_some.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/commands.rs
+expression: setup.terminal.backend()
+---
+"╭──────────╮"
+"│▶ pause │"
+"│ restart │"
+"│ stop │"
+"│ delete │"
+"╰──────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_some_paused.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_some_paused.snap
new file mode 100644
index 0000000..8aaca4d
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__commands__tests__draw_blocks_commands_some_paused.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/commands.rs
+expression: setup.terminal.backend()
+---
+"╭──────────╮"
+"│ resume │"
+"│▶ stop │"
+"│ delete │"
+"│ │"
+"╰──────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_colors.snap
new file mode 100644
index 0000000..b7b75a5
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_colors.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ✓ running Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors.snap
new file mode 100644
index 0000000..b7b75a5
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ✓ running Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_dead.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_dead.snap
new file mode 100644
index 0000000..5e8c7d2
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_dead.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ✖ dead Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_exited.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_exited.snap
new file mode 100644
index 0000000..15afd7c
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_exited.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ✖ exited Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_healthy.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_healthy.snap
new file mode 100644
index 0000000..b7b75a5
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_healthy.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ✓ running Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_paused.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_paused.snap
new file mode 100644
index 0000000..9d95084
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_paused.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ॥ paused Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_removing.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_removing.snap
new file mode 100644
index 0000000..3412602
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_removing.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 removing Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_restarting.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_restarting.snap
new file mode 100644
index 0000000..22c8635
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_restarting.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ↻ restarting Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_unhealthy.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_unhealthy.snap
new file mode 100644
index 0000000..611c8dd
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_unhealthy.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ! running Up 1 hour (unhealthy) 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_unknown.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_unknown.snap
new file mode 100644
index 0000000..a86d2b2
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_custom_colors_state_unknown.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ? unknown Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_dead.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_dead.snap
new file mode 100644
index 0000000..5e8c7d2
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_dead.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ✖ dead Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_exited.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_exited.snap
new file mode 100644
index 0000000..15afd7c
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_exited.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ✖ exited Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_long_name_image.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_long_name_image.snap
new file mode 100644
index 0000000..ef1be6d
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_long_name_image.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ a_long_container_name_for_the… ॥ paused Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 a_long_image_name_for_the_pur… 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_none.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_none.snap
new file mode 100644
index 0000000..55702b6
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_none.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers ──────────────────────────╮"
+"│ no containers running │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_paused.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_paused.snap
new file mode 100644
index 0000000..9d95084
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_paused.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ॥ paused Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_removing.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_removing.snap
new file mode 100644
index 0000000..3412602
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_removing.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 removing Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_restarting.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_restarting.snap
new file mode 100644
index 0000000..332bc75
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_restarting.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ↻ restarting Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_selected_bold.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_selected_bold.snap
new file mode 100644
index 0000000..b7b75a5
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_selected_bold.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ✓ running Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_unhealthy.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_unhealthy.snap
new file mode 100644
index 0000000..264dc2b
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_unhealthy.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ! running Up 1 hour (unhealthy) 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_unknown.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_unknown.snap
new file mode 100644
index 0000000..a86d2b2
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__containers__tests__draw_blocks_containers_unknown.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/containers.rs
+expression: setup.terminal.backend()
+---
+"╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│⚪ container_1 ? unknown Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete.snap
new file mode 100644
index 0000000..9a63a7f
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete.snap
@@ -0,0 +1,14 @@
+---
+source: src/ui/draw_blocks/delete_confirm.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭──────────────────────── Confirm Delete ────────────────────────╮ "
+" │ │ "
+" │ Are you sure you want to delete container: container_1 │ "
+" │ │ "
+" │ ╭─────────────────────╮ ╭─────────────────────╮ │ "
+" │ │ ( n ) no │ │ ( y ) yes │ │ "
+" │ ╰─────────────────────╯ ╰─────────────────────╯ │ "
+" ╰────────────────────────────────────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_custom_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_custom_colors.snap
new file mode 100644
index 0000000..9a63a7f
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_custom_colors.snap
@@ -0,0 +1,14 @@
+---
+source: src/ui/draw_blocks/delete_confirm.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭──────────────────────── Confirm Delete ────────────────────────╮ "
+" │ │ "
+" │ Are you sure you want to delete container: container_1 │ "
+" │ │ "
+" │ ╭─────────────────────╮ ╭─────────────────────╮ │ "
+" │ │ ( n ) no │ │ ( y ) yes │ │ "
+" │ ╰─────────────────────╯ ╰─────────────────────╯ │ "
+" ╰────────────────────────────────────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_custom_keymap_one_definition.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_custom_keymap_one_definition.snap
new file mode 100644
index 0000000..8574f25
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_custom_keymap_one_definition.snap
@@ -0,0 +1,14 @@
+---
+source: src/ui/draw_blocks/delete_confirm.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭──────────────────────── Confirm Delete ────────────────────────╮ "
+" │ │ "
+" │ Are you sure you want to delete container: container_1 │ "
+" │ │ "
+" │ ╭─────────────────────╮ ╭─────────────────────╮ │ "
+" │ │ ( End ) no │ │ ( F10 ) yes │ │ "
+" │ ╰─────────────────────╯ ╰─────────────────────╯ │ "
+" ╰────────────────────────────────────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_custom_keymap_one_two_definition.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_custom_keymap_one_two_definition.snap
new file mode 100644
index 0000000..85cdc89
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_custom_keymap_one_two_definition.snap
@@ -0,0 +1,14 @@
+---
+source: src/ui/draw_blocks/delete_confirm.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭──────────────────────── Confirm Delete ────────────────────────╮ "
+" │ │ "
+" │ Are you sure you want to delete container: container_1 │ "
+" │ │ "
+" │ ╭─────────────────────╮ ╭─────────────────────╮ │ "
+" │ │ ( End | Up ) no │ │ ( F10 ) yes │ │ "
+" │ ╰─────────────────────╯ ╰─────────────────────╯ │ "
+" ╰────────────────────────────────────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_custom_keymap_two_definition.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_custom_keymap_two_definition.snap
new file mode 100644
index 0000000..67f83d1
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_custom_keymap_two_definition.snap
@@ -0,0 +1,14 @@
+---
+source: src/ui/draw_blocks/delete_confirm.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭──────────────────────── Confirm Delete ────────────────────────╮ "
+" │ │ "
+" │ Are you sure you want to delete container: container_1 │ "
+" │ │ "
+" │ ╭─────────────────────╮ ╭─────────────────────╮ │ "
+" │ │ ( End | Up ) no │ │ ( F10 | L ) yes │ │ "
+" │ ╰─────────────────────╯ ╰─────────────────────╯ │ "
+" ╰────────────────────────────────────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_long_name.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_long_name.snap
new file mode 100644
index 0000000..ed1e07c
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__delete_confirm__tests__draw_blocks_delete_long_name.snap
@@ -0,0 +1,14 @@
+---
+source: src/ui/draw_blocks/delete_confirm.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭──────────────────────────────────── Confirm Delete ────────────────────────────────────╮ "
+" │ │ "
+" │ Are you sure you want to delete container: container_1_container_1_container_1 │ "
+" │ │ "
+" │ ╭──────────────────────────────╮ ╭─────────────────────────────╮ │ "
+" │ │ ( n ) no │ │ ( y ) yes │ │ "
+" │ ╰──────────────────────────────╯ ╰─────────────────────────────╯ │ "
+" ╰────────────────────────────────────────────────────────────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_clearable_error.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_clearable_error.snap
new file mode 100644
index 0000000..57550a3
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_clearable_error.snap
@@ -0,0 +1,15 @@
+---
+source: src/ui/draw_blocks/error.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭────────────── Error ──────────────╮ "
+" │ │ "
+" │ Unable to exec into container │ "
+" │ │ "
+" │ ( c ) clear error │ "
+" │ │ "
+" │ ( q ) quit oxker │ "
+" │ │ "
+" ╰───────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_custom_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_custom_colors.snap
new file mode 100644
index 0000000..57550a3
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_custom_colors.snap
@@ -0,0 +1,15 @@
+---
+source: src/ui/draw_blocks/error.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭────────────── Error ──────────────╮ "
+" │ │ "
+" │ Unable to exec into container │ "
+" │ │ "
+" │ ( c ) clear error │ "
+" │ │ "
+" │ ( q ) quit oxker │ "
+" │ │ "
+" ╰───────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_custom_keymap.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_custom_keymap.snap
new file mode 100644
index 0000000..26eab89
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_custom_keymap.snap
@@ -0,0 +1,15 @@
+---
+source: src/ui/draw_blocks/error.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭────────────── Error ──────────────╮ "
+" │ │ "
+" │ Unable to exec into container │ "
+" │ │ "
+" │ ( Back Tab ) clear error │ "
+" │ │ "
+" │ ( F4 ) quit oxker │ "
+" │ │ "
+" ╰───────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_custom_keymap_one_two_definitions.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_custom_keymap_one_two_definitions.snap
new file mode 100644
index 0000000..d00b144
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_custom_keymap_one_two_definitions.snap
@@ -0,0 +1,15 @@
+---
+source: src/ui/draw_blocks/error.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭────────────── Error ──────────────╮ "
+" │ │ "
+" │ Unable to exec into container │ "
+" │ │ "
+" │ ( c ) clear error │ "
+" │ │ "
+" │ ( F4 | End ) quit oxker │ "
+" │ │ "
+" ╰───────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_custom_keymap_two_definitions.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_custom_keymap_two_definitions.snap
new file mode 100644
index 0000000..d238e61
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_custom_keymap_two_definitions.snap
@@ -0,0 +1,15 @@
+---
+source: src/ui/draw_blocks/error.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭────────────── Error ──────────────╮ "
+" │ │ "
+" │ Unable to exec into container │ "
+" │ │ "
+" │ ( Back Tab | m ) clear error │ "
+" │ │ "
+" │ ( F4 | End ) quit oxker │ "
+" │ │ "
+" ╰───────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error.snap
new file mode 100644
index 0000000..050fc2f
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error.snap
@@ -0,0 +1,13 @@
+---
+source: src/ui/draw_blocks/error.rs
+expression: setup.terminal.backend()
+---
+" "
+"╭────────────────── Error ───────────────────╮"
+"│ │"
+"│ Unable to access docker daemon │"
+"│ │"
+"│ oxker::v0.10.0 closing in 04 seconds │"
+"│ │"
+"╰────────────────────────────────────────────╯"
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row.snap
new file mode 100644
index 0000000..9f5b920
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/filter.rs
+expression: setup.terminal.backend()
+---
+" Esc clear ← by → Name Image Status All term: "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row_custom_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row_custom_colors.snap
new file mode 100644
index 0000000..eb2fd5d
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row_custom_colors.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/filter.rs
+expression: setup.terminal.backend()
+---
+" Esc clear ← by → Name Image Status All term: cd "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row_filter_by.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row_filter_by.snap
new file mode 100644
index 0000000..9f5b920
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row_filter_by.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/filter.rs
+expression: setup.terminal.backend()
+---
+" Esc clear ← by → Name Image Status All term: "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row_s.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row_s.snap
new file mode 100644
index 0000000..9f5b920
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row_s.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/filter.rs
+expression: setup.terminal.backend()
+---
+" Esc clear ← by → Name Image Status All term: "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row_text.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row_text.snap
new file mode 100644
index 0000000..eb2fd5d
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__filter__tests__draw_blocks_filter_row_text.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/filter.rs
+expression: setup.terminal.backend()
+---
+" Esc clear ← by → Name Image Status All term: cd "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_animation.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_animation.snap
new file mode 100644
index 0000000..3663ed8
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_animation.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" ⠙ name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_custom_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_custom_colors.snap
new file mode 100644
index 0000000..3663ed8
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_custom_colors.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" ⠙ name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_custom_keymap_one_definition.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_custom_keymap_one_definition.snap
new file mode 100644
index 0000000..89a0392
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_custom_keymap_one_definition.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( T ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_custom_keymap_teo_definitions.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_custom_keymap_teo_definitions.snap
new file mode 100644
index 0000000..5770658
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_custom_keymap_teo_definitions.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( T | Tab ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_custom_keymap_two_definitions.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_custom_keymap_two_definitions.snap
new file mode 100644
index 0000000..5770658
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_custom_keymap_two_definitions.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( T | Tab ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_no_containers_exit_help.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_no_containers_exit_help.snap
new file mode 100644
index 0000000..24104eb
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_no_containers_exit_help.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" ( h ) exit help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_no_containers_hide_help.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_no_containers_hide_help.snap
new file mode 100644
index 0000000..24104eb
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_no_containers_hide_help.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" ( h ) exit help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_no_containers_show_help.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_no_containers_show_help.snap
new file mode 100644
index 0000000..d1dd0af
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_no_containers_show_help.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_some_containers.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_some_containers.snap
new file mode 100644
index 0000000..d33195b
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_some_containers.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_some_containers_reduced_width.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_some_containers_reduced_width.snap
new file mode 100644
index 0000000..ecbc8c6
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_some_containers_reduced_width.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_cpu_asc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_cpu_asc.snap
new file mode 100644
index 0000000..84362a8
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_cpu_asc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu ▲ memory/limit id image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_cpu_desc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_cpu_desc.snap
new file mode 100644
index 0000000..251edd2
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_cpu_desc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu ▼ memory/limit id image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_id_asc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_id_asc.snap
new file mode 100644
index 0000000..a48ca82
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_id_asc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id ▲ image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_id_desc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_id_desc.snap
new file mode 100644
index 0000000..4f0347b
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_id_desc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id ▼ image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_image_asc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_image_asc.snap
new file mode 100644
index 0000000..3eb0637
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_image_asc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ▲ ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_image_desc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_image_desc.snap
new file mode 100644
index 0000000..32e5410
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_image_desc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ▼ ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_memory_asc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_memory_asc.snap
new file mode 100644
index 0000000..1f70235
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_memory_asc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit ▲ id image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_memory_desc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_memory_desc.snap
new file mode 100644
index 0000000..82da172
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_memory_desc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit ▼ id image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_name_asc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_name_asc.snap
new file mode 100644
index 0000000..13c19a7
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_name_asc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name ▲ state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_name_desc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_name_desc.snap
new file mode 100644
index 0000000..7607d82
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_name_desc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name ▼ state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_rx_asc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_rx_asc.snap
new file mode 100644
index 0000000..fc567d1
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_rx_asc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ▲ ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_rx_desc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_rx_desc.snap
new file mode 100644
index 0000000..c63b5f9
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_rx_desc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ▼ ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_state_asc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_state_asc.snap
new file mode 100644
index 0000000..3a24db0
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_state_asc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state ▲ status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_state_desc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_state_desc.snap
new file mode 100644
index 0000000..5e2becf
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_state_desc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state ▼ status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_status_asc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_status_asc.snap
new file mode 100644
index 0000000..7846ba4
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_status_asc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status ▲ cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_status_desc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_status_desc.snap
new file mode 100644
index 0000000..5de7ea7
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_status_desc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status ▼ cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_tx_asc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_tx_asc.snap
new file mode 100644
index 0000000..17666e9
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_tx_asc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ▲ ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_tx_desc.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_tx_desc.snap
new file mode 100644
index 0000000..5941028
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__headers__tests__draw_blocks_headers_sort_containers_tx_desc.snap
@@ -0,0 +1,5 @@
+---
+source: src/ui/draw_blocks/headers.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ▼ ( h ) show help "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help.snap
new file mode 100644
index 0000000..2d2ff4a
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help.snap
@@ -0,0 +1,37 @@
+---
+source: src/ui/draw_blocks/help.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭ 0.10.0 ───────────────────────────────────────────────────────────────────────────╮ "
+" │ │ "
+" │ 88 │ "
+" │ 88 │ "
+" │ 88 │ "
+" │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ "
+" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "
+" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "
+" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "
+" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "
+" │ │ "
+" │ A simple tui to view & control docker containers │ "
+" │ │ "
+" │ ( tab ) or ( shift+tab ) change panels │ "
+" │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ "
+" │ ( enter ) send docker container command │ "
+" │ ( e ) exec into a container │ "
+" │ ( h ) toggle this help information - or click heading │ "
+" │ ( s ) save logs to file │ "
+" │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ "
+" │ ( F1 ) or ( / ) enter filter mode │ "
+" │ ( 0 ) stop sort │ "
+" │ ( 1 - 9 ) sort by header - or click header │ "
+" │ ( esc ) close dialog │ "
+" │ ( q ) quit at any time │ "
+" │ │ "
+" │ currently an early work in progress, all and any input appreciated │ "
+" │ https://github.com/mrjackwills/oxker │ "
+" │ │ "
+" │ │ "
+" ╰───────────────────────────────────────────────────────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_colors.snap
new file mode 100644
index 0000000..2d2ff4a
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_colors.snap
@@ -0,0 +1,37 @@
+---
+source: src/ui/draw_blocks/help.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭ 0.10.0 ───────────────────────────────────────────────────────────────────────────╮ "
+" │ │ "
+" │ 88 │ "
+" │ 88 │ "
+" │ 88 │ "
+" │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ "
+" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "
+" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "
+" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "
+" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "
+" │ │ "
+" │ A simple tui to view & control docker containers │ "
+" │ │ "
+" │ ( tab ) or ( shift+tab ) change panels │ "
+" │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ "
+" │ ( enter ) send docker container command │ "
+" │ ( e ) exec into a container │ "
+" │ ( h ) toggle this help information - or click heading │ "
+" │ ( s ) save logs to file │ "
+" │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ "
+" │ ( F1 ) or ( / ) enter filter mode │ "
+" │ ( 0 ) stop sort │ "
+" │ ( 1 - 9 ) sort by header - or click header │ "
+" │ ( esc ) close dialog │ "
+" │ ( q ) quit at any time │ "
+" │ │ "
+" │ currently an early work in progress, all and any input appreciated │ "
+" │ https://github.com/mrjackwills/oxker │ "
+" │ │ "
+" │ │ "
+" ╰───────────────────────────────────────────────────────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_definition.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_definition.snap
new file mode 100644
index 0000000..eb31006
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_definition.snap
@@ -0,0 +1,51 @@
+---
+source: src/ui/draw_blocks/help.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭ 0.10.0 ────────────────────────────────────────────────────────────────────────────────────╮ "
+" │ │ "
+" │ 88 │ "
+" │ 88 │ "
+" │ 88 │ "
+" │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ "
+" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "
+" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "
+" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "
+" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "
+" │ │ "
+" │ A simple tui to view & control docker containers │ "
+" │ │ "
+" │ ( 0 ) select next panel │ "
+" │ ( 2 ) select previous panel │ "
+" │ ( q ) scroll list down by one │ "
+" │ ( y ) scroll list up by one │ "
+" │ ( o ) scroll list down by many │ "
+" │ ( w ) scroll list by up many │ "
+" │ ( s ) scroll list to end │ "
+" │ ( u ) scroll list to start │ "
+" │ ( enter ) send docker container command │ "
+" │ ( g ) exec into a container │ "
+" │ ( Home ) toggle this help information - or click heading │ "
+" │ ( m ) save logs to file │ "
+" │ ( Page Down ) toggle mouse capture - if disabled, text on screen can be selected & copied │ "
+" │ ( i ) enter filter mode │ "
+" │ ( Up ) reset container sorting │ "
+" │ ( 4 ) sort containers by name │ "
+" │ ( 6 ) sort containers by state │ "
+" │ ( 8 ) sort containers by status │ "
+" │ ( F1 ) sort containers by cpu │ "
+" │ ( # ) sort containers by memory │ "
+" │ ( / ) sort containers by id │ "
+" │ ( , ) sort containers by image │ "
+" │ ( . ) sort containers by rx │ "
+" │ ( Backspace ) sort containers by tx │ "
+" │ ( a ) close dialog │ "
+" │ ( k ) quit at any time │ "
+" │ │ "
+" │ currently an early work in progress, all and any input appreciated │ "
+" │ https://github.com/mrjackwills/oxker │ "
+" │ │ "
+" │ │ "
+" ╰────────────────────────────────────────────────────────────────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_two_definitions.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_two_definitions.snap
new file mode 100644
index 0000000..af0f4d3
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_two_definitions.snap
@@ -0,0 +1,51 @@
+---
+source: src/ui/draw_blocks/help.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭ 0.10.0 ──────────────────────────────────────────────────────────────────────────────────────────────────╮ "
+" │ │ "
+" │ 88 │ "
+" │ 88 │ "
+" │ 88 │ "
+" │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ "
+" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "
+" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "
+" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "
+" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "
+" │ │ "
+" │ A simple tui to view & control docker containers │ "
+" │ │ "
+" │ ( 0 ) or ( 1 ) select next panel │ "
+" │ ( 2 ) or ( 3 ) select previous panel │ "
+" │ ( q ) or ( r ) scroll list down by one │ "
+" │ ( y ) or ( z ) scroll list up by one │ "
+" │ ( o ) or ( p ) scroll list down by many │ "
+" │ ( w ) or ( x ) scroll list by up many │ "
+" │ ( s ) or ( t ) scroll list to end │ "
+" │ ( u ) or ( v ) scroll list to start │ "
+" │ ( enter ) send docker container command │ "
+" │ ( g ) or ( h ) exec into a container │ "
+" │ ( Home ) or ( Del ) toggle this help information - or click heading │ "
+" │ ( m ) or ( n ) save logs to file │ "
+" │ ( Page Down ) or ( Page Up ) toggle mouse capture - if disabled, text on screen can be selected & copied │ "
+" │ ( i ) or ( j ) enter filter mode │ "
+" │ ( Up ) or ( Down ) reset container sorting │ "
+" │ ( 4 ) or ( 5 ) sort containers by name │ "
+" │ ( 6 ) or ( 7 ) sort containers by state │ "
+" │ ( 8 ) or ( 9 ) sort containers by status │ "
+" │ ( F1 ) or ( F12 ) sort containers by cpu │ "
+" │ ( # ) or ( - ) sort containers by memory │ "
+" │ ( / ) or ( = ) sort containers by id │ "
+" │ ( , ) or ( \ ) sort containers by image │ "
+" │ ( . ) or ( ] ) sort containers by rx │ "
+" │ ( Backspace ) or ( Back Tab ) sort containers by tx │ "
+" │ ( a ) or ( b ) close dialog │ "
+" │ ( k ) or ( l ) quit at any time │ "
+" │ │ "
+" │ currently an early work in progress, all and any input appreciated │ "
+" │ https://github.com/mrjackwills/oxker │ "
+" │ │ "
+" │ │ "
+" ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_one_and_two_definitions.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_one_and_two_definitions.snap
new file mode 100644
index 0000000..9411081
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_one_and_two_definitions.snap
@@ -0,0 +1,51 @@
+---
+source: src/ui/draw_blocks/help.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭ 0.10.0 ──────────────────────────────────────────────────────────────────────────────────────────────────╮ "
+" │ │ "
+" │ 88 │ "
+" │ 88 │ "
+" │ 88 │ "
+" │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ "
+" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "
+" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "
+" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "
+" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "
+" │ │ "
+" │ A simple tui to view & control docker containers │ "
+" │ │ "
+" │ ( 0 ) select next panel │ "
+" │ ( 2 ) or ( 3 ) select previous panel │ "
+" │ ( q ) or ( r ) scroll list down by one │ "
+" │ ( y ) or ( z ) scroll list up by one │ "
+" │ ( o ) scroll list down by many │ "
+" │ ( w ) scroll list by up many │ "
+" │ ( s ) scroll list to end │ "
+" │ ( u ) or ( v ) scroll list to start │ "
+" │ ( enter ) send docker container command │ "
+" │ ( g ) exec into a container │ "
+" │ ( Home ) toggle this help information - or click heading │ "
+" │ ( m ) or ( n ) save logs to file │ "
+" │ ( Page Down ) or ( Page Up ) toggle mouse capture - if disabled, text on screen can be selected & copied │ "
+" │ ( i ) or ( j ) enter filter mode │ "
+" │ ( Up ) or ( Down ) reset container sorting │ "
+" │ ( 4 ) sort containers by name │ "
+" │ ( 6 ) or ( 7 ) sort containers by state │ "
+" │ ( 8 ) sort containers by status │ "
+" │ ( F1 ) or ( F12 ) sort containers by cpu │ "
+" │ ( # ) sort containers by memory │ "
+" │ ( / ) or ( = ) sort containers by id │ "
+" │ ( , ) sort containers by image │ "
+" │ ( . ) or ( ] ) sort containers by rx │ "
+" │ ( Backspace ) sort containers by tx │ "
+" │ ( a ) or ( b ) close dialog │ "
+" │ ( k ) quit at any time │ "
+" │ │ "
+" │ currently an early work in progress, all and any input appreciated │ "
+" │ https://github.com/mrjackwills/oxker │ "
+" │ │ "
+" │ │ "
+" ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_show_timezone.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_show_timezone.snap
new file mode 100644
index 0000000..7a205e9
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_show_timezone.snap
@@ -0,0 +1,39 @@
+---
+source: src/ui/draw_blocks/help.rs
+expression: setup.terminal.backend()
+---
+" "
+" ╭ 0.10.0 ───────────────────────────────────────────────────────────────────────────╮ "
+" │ │ "
+" │ 88 │ "
+" │ 88 │ "
+" │ 88 │ "
+" │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ "
+" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "
+" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "
+" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "
+" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "
+" │ │ "
+" │ A simple tui to view & control docker containers │ "
+" │ │ "
+" │ logs timezone: Asia/Tokyo │ "
+" │ │ "
+" │ ( tab ) or ( shift+tab ) change panels │ "
+" │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ "
+" │ ( enter ) send docker container command │ "
+" │ ( e ) exec into a container │ "
+" │ ( h ) toggle this help information - or click heading │ "
+" │ ( s ) save logs to file │ "
+" │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ "
+" │ ( F1 ) or ( / ) enter filter mode │ "
+" │ ( 0 ) stop sort │ "
+" │ ( 1 - 9 ) sort by header - or click header │ "
+" │ ( esc ) close dialog │ "
+" │ ( q ) quit at any time │ "
+" │ │ "
+" │ currently an early work in progress, all and any input appreciated │ "
+" │ https://github.com/mrjackwills/oxker │ "
+" │ │ "
+" │ │ "
+" ╰───────────────────────────────────────────────────────────────────────────────────╯ "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__info__tests__draw_blocks_info.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__info__tests__draw_blocks_info.snap
new file mode 100644
index 0000000..1f84da6
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__info__tests__draw_blocks_info.snap
@@ -0,0 +1,13 @@
+---
+source: src/ui/draw_blocks/info.rs
+expression: setup.terminal.backend()
+---
+" "
+" "
+" "
+" "
+" "
+" "
+" "
+" test "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__info__tests__draw_blocks_info_custom_color.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__info__tests__draw_blocks_info_custom_color.snap
new file mode 100644
index 0000000..1f84da6
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__info__tests__draw_blocks_info_custom_color.snap
@@ -0,0 +1,13 @@
+---
+source: src/ui/draw_blocks/info.rs
+expression: setup.terminal.backend()
+---
+" "
+" "
+" "
+" "
+" "
+" "
+" "
+" test "
+" "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_custom_colors_logs.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_custom_colors_logs.snap
new file mode 100644
index 0000000..8794006
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_custom_colors_logs.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/logs.rs
+expression: setup.terminal.backend()
+---
+"╭ Logs 3/3 - container_1 - image_1 ╮"
+"│ line 1 │"
+"│ line 2 │"
+"│▶ line 3 │"
+"│ │"
+"╰──────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_custom_colors_no_logs.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_custom_colors_no_logs.snap
new file mode 100644
index 0000000..2dd7564
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_custom_colors_no_logs.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/logs.rs
+expression: setup.terminal.backend()
+---
+"╭ Logs - container_1 - image_1 ───╮"
+"│ no logs found │"
+"│ │"
+"│ │"
+"│ │"
+"╰─────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_custom_colors_parsing.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_custom_colors_parsing.snap
new file mode 100644
index 0000000..e5d9fe3
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_custom_colors_parsing.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/logs.rs
+expression: setup.terminal.backend()
+---
+"╭ Logs - container_1 - image_1 ╮"
+"│ parsing logs ⠙ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_long_name.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_long_name.snap
new file mode 100644
index 0000000..292e367
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_long_name.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/logs.rs
+expression: setup.terminal.backend()
+---
+"╭ Logs 3/3 - a_long_container_name_for_the_purposes_of_this_test - a_long_image╮"
+"│ line 1 │"
+"│ line 2 │"
+"│▶ line 3 │"
+"│ │"
+"╰──────────────────────────────────────────────────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_none.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_none.snap
new file mode 100644
index 0000000..2dd7564
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_none.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/logs.rs
+expression: setup.terminal.backend()
+---
+"╭ Logs - container_1 - image_1 ───╮"
+"│ no logs found │"
+"│ │"
+"│ │"
+"│ │"
+"╰─────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_parsing.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_parsing.snap
new file mode 100644
index 0000000..e5d9fe3
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_parsing.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/logs.rs
+expression: setup.terminal.backend()
+---
+"╭ Logs - container_1 - image_1 ╮"
+"│ parsing logs ⠙ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_parsing_frame_one.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_parsing_frame_one.snap
new file mode 100644
index 0000000..e5d9fe3
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_parsing_frame_one.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/logs.rs
+expression: setup.terminal.backend()
+---
+"╭ Logs - container_1 - image_1 ╮"
+"│ parsing logs ⠙ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_parsing_frame_two.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_parsing_frame_two.snap
new file mode 100644
index 0000000..7408f6c
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_parsing_frame_two.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/logs.rs
+expression: setup.terminal.backend()
+---
+"╭ Logs - container_1 - image_1 ╮"
+"│ parsing logs ⠹ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_some_line_three.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_some_line_three.snap
new file mode 100644
index 0000000..8794006
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_some_line_three.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/logs.rs
+expression: setup.terminal.backend()
+---
+"╭ Logs 3/3 - container_1 - image_1 ╮"
+"│ line 1 │"
+"│ line 2 │"
+"│▶ line 3 │"
+"│ │"
+"╰──────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_some_line_two.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_some_line_two.snap
new file mode 100644
index 0000000..45b697f
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__logs__tests__draw_blocks_logs_some_line_two.snap
@@ -0,0 +1,10 @@
+---
+source: src/ui/draw_blocks/logs.rs
+expression: setup.terminal.backend()
+---
+"╭ Logs 2/3 - container_1 - image_1 ╮"
+"│ line 1 │"
+"│▶ line 2 │"
+"│ line 3 │"
+"│ │"
+"╰──────────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_container_state.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_container_state.snap
new file mode 100644
index 0000000..704a66e
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_container_state.snap
@@ -0,0 +1,12 @@
+---
+source: src/ui/draw_blocks/ports.rs
+expression: setup.terminal.backend()
+---
+"╭─────────── ports ────────────╮"
+"│ ip private public │"
+"│ 8001 │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors.snap
new file mode 100644
index 0000000..704a66e
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors.snap
@@ -0,0 +1,12 @@
+---
+source: src/ui/draw_blocks/ports.rs
+expression: setup.terminal.backend()
+---
+"╭─────────── ports ────────────╮"
+"│ ip private public │"
+"│ 8001 │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors_state-2.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors_state-2.snap
new file mode 100644
index 0000000..704a66e
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors_state-2.snap
@@ -0,0 +1,12 @@
+---
+source: src/ui/draw_blocks/ports.rs
+expression: setup.terminal.backend()
+---
+"╭─────────── ports ────────────╮"
+"│ ip private public │"
+"│ 8001 │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors_state-3.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors_state-3.snap
new file mode 100644
index 0000000..704a66e
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors_state-3.snap
@@ -0,0 +1,12 @@
+---
+source: src/ui/draw_blocks/ports.rs
+expression: setup.terminal.backend()
+---
+"╭─────────── ports ────────────╮"
+"│ ip private public │"
+"│ 8001 │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors_state-4.snap.new b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors_state-4.snap.new
new file mode 100644
index 0000000..b95ceea
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors_state-4.snap.new
@@ -0,0 +1,13 @@
+---
+source: src/ui/draw_blocks/ports.rs
+assertion_line: 381
+expression: setup.terminal.backend()
+---
+"╭─────────── ports ────────────╮"
+"│ ip private public │"
+"│ 8001 │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors_state.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors_state.snap
new file mode 100644
index 0000000..704a66e
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_custom_colors_state.snap
@@ -0,0 +1,12 @@
+---
+source: src/ui/draw_blocks/ports.rs
+expression: setup.terminal.backend()
+---
+"╭─────────── ports ────────────╮"
+"│ ip private public │"
+"│ 8001 │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_multiple_ports.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_multiple_ports.snap
new file mode 100644
index 0000000..0294461
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_multiple_ports.snap
@@ -0,0 +1,12 @@
+---
+source: src/ui/draw_blocks/ports.rs
+expression: setup.terminal.backend()
+---
+"╭─────────── ports ────────────╮"
+"│ ip private public │"
+"│ 8001 │"
+"│ 8002 │"
+"│127.0.0.1 8003 8003 │"
+"│ │"
+"│ │"
+"╰──────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_no_ports.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_no_ports.snap
new file mode 100644
index 0000000..5d0aa17
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_no_ports.snap
@@ -0,0 +1,12 @@
+---
+source: src/ui/draw_blocks/ports.rs
+expression: setup.terminal.backend()
+---
+"╭────────── ports ───────────╮"
+"│ no ports │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_no_ports_dead.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_no_ports_dead.snap
new file mode 100644
index 0000000..af9a790
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__ports__tests__draw_blocks_ports_no_ports_dead.snap
@@ -0,0 +1,12 @@
+---
+source: src/ui/draw_blocks/ports.rs
+expression: setup.terminal.backend()
+---
+"╭────────── ports ───────────╮"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout.snap
new file mode 100644
index 0000000..f90c683
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout.snap
@@ -0,0 +1,34 @@
+---
+source: src/ui/draw_blocks/mod.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
+"╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮"
+"│⚪ container_1 ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 image_1 0.00 kB 0.00 kB ││▶ pause │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │"
+"│ ││ delete │"
+"│ ││ │"
+"│ ││ │"
+"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯"
+"╭ Logs 3/3 - container_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│ line 1 │"
+"│ line 2 │"
+"│▶ line 3 │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
+"│10.00%│ •••• ││100.00 kB│ ••• ││ ip private public│"
+"│ │ ••• • ││ │ ••• • ││ 8001 │"
+"│ │•• ••• ││ │•• ••• ││127.0.0.1 8003 8003│"
+"│ │ ││ │ ││ │"
+"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_long_name.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_long_name.snap
new file mode 100644
index 0000000..27fe0a0
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_long_name.snap
@@ -0,0 +1,34 @@
+---
+source: src/ui/draw_blocks/mod.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
+"╭ Containers 1/3 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭─────────────────╮"
+"│⚪ a_long_container_name_for_the… ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 a_long_image_name_for_the_pur… 0.00 kB 0.00 kB ││▶ pause │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │"
+"│ ││ delete │"
+"│ ││ │"
+"│ ││ │"
+"╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰─────────────────╯"
+"╭ Logs 3/3 - a_long_container_name_for_the_purposes_of_this_test - a_long_image_name_for_the_purposes_of_this_test ──────────────────────────────────────────────────────────────────────────╮"
+"│ line 1 │"
+"│ line 2 │"
+"│▶ line 3 │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+"╭───────────────────────────────── cpu 03.00% ─────────────────────────────────╮╭────────────────────────────── memory 30.00 kB ───────────────────────────────╮╭────────── ports ───────────╮"
+"│10.00%│ •••• ││100.00 kB│ ••••• ││ ip private public│"
+"│ │ •••• • ││ │ ••• • ││ 8001 │"
+"│ │••• •••• ││ │••• ••• ││127.0.0.1 8003 8003│"
+"│ │ ││ │ ││ │"
+"╰──────────────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_with_filter.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_with_filter.snap
new file mode 100644
index 0000000..15e9f0f
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_with_filter.snap
@@ -0,0 +1,34 @@
+---
+source: src/ui/draw_blocks/mod.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
+"╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮"
+"│⚪ container_1 ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 image_1 0.00 kB 0.00 kB ││▶ pause │" Hidden by multi-width symbols: [(2, " ")]
+"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │"
+"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │"
+"│ ││ delete │"
+"│ ││ │"
+"│ ││ │"
+"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯"
+"╭ Logs 3/3 - container_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│ line 1 │"
+"│ line 2 │"
+"│▶ line 3 │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
+"│10.00%│ •••• ││100.00 kB│ ••• ││ ip private public│"
+"│ │ ••• • ││ │ ••• • ││ 8001 │"
+"│ │•• ••• ││ │•• ••• ││ │"
+"│ │ ││ │ ││ │"
+"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_with_filter_a.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_with_filter_a.snap
new file mode 100644
index 0000000..4e98c14
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_with_filter_a.snap
@@ -0,0 +1,34 @@
+---
+source: src/ui/draw_blocks/mod.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
+"╭ Containers 1/1 - filtered ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮"
+"│⚪ container_1 ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 image_1 0.00 kB 0.00 kB ││▶ pause │" Hidden by multi-width symbols: [(2, " ")]
+"│ ││ restart │"
+"│ ││ stop │"
+"│ ││ delete │"
+"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯"
+"╭ Logs 3/3 - container_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│ line 1 │"
+"│ line 2 │"
+"│▶ line 3 │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
+"│10.00%│ ••• ││100.00 kB│ •• ││ ip private public│"
+"│ │ •• • ││ │ •• • ││ 8001 │"
+"│ │ ••• • • ││ │ ••• • • ││ │"
+"│ │• •• ││ │• •• ││ │"
+"│ │ ││ │ ││ │"
+"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
+" Esc clear ← by → Name Image Status All term: r_1 "
diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_with_filter_bar.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_with_filter_bar.snap
new file mode 100644
index 0000000..4e98c14
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_with_filter_bar.snap
@@ -0,0 +1,34 @@
+---
+source: src/ui/draw_blocks/mod.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
+"╭ Containers 1/1 - filtered ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮"
+"│⚪ container_1 ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 image_1 0.00 kB 0.00 kB ││▶ pause │" Hidden by multi-width symbols: [(2, " ")]
+"│ ││ restart │"
+"│ ││ stop │"
+"│ ││ delete │"
+"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯"
+"╭ Logs 3/3 - container_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│ line 1 │"
+"│ line 2 │"
+"│▶ line 3 │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
+"│10.00%│ ••• ││100.00 kB│ •• ││ ip private public│"
+"│ │ •• • ││ │ •• • ││ 8001 │"
+"│ │ ••• • • ││ │ ••• • • ││ │"
+"│ │• •• ││ │• •• ││ │"
+"│ │ ││ │ ││ │"
+"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
+" Esc clear ← by → Name Image Status All term: r_1 "
diff --git a/src/ui/gui_state.rs b/src/ui/gui_state.rs
index 40d9aa7..0c9d57a 100644
--- a/src/ui/gui_state.rs
+++ b/src/ui/gui_state.rs
@@ -282,7 +282,7 @@ impl GuiState {
Region::HelpPanel => {
self.intersect_help = Some(area);
}
- };
+ }
}
/// Check if an ContainerId is set in the delete_container field
diff --git a/src/ui/mod.rs b/src/ui/mod.rs
index c017e51..cc64199 100644
--- a/src/ui/mod.rs
+++ b/src/ui/mod.rs
@@ -93,7 +93,7 @@ impl Ui {
}
if let Err(e) = ui.reset_terminal() {
error!("{e}");
- };
+ }
}
_ => {
error!("Terminal Error");
@@ -180,7 +180,7 @@ impl Ui {
self.app_data
.lock()
.set_error(e, &self.gui_state, Status::Error);
- };
+ }
}
self.terminal.clear().ok();
self.reset_terminal().ok();