comma test runner (#715)

- Stabilized the format
- Test suit runner
- Support batch mode
- Watch supports postpone
- `settings.theme`
- docker.nu Simplify process-list logic

---------

Co-authored-by: nash <nash@iffy.me>
This commit is contained in:
fj0r 2023-12-29 23:49:28 +08:00 committed by GitHub
parent b9cfa0d204
commit 9b7c1772e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1340 additions and 464 deletions

View File

@ -2,76 +2,129 @@ Working dir task runner, similar to `pwd-module`, but supports completion and de
- `,` or `*` need to be exported in order to use `,` directly
- Directly execute `,` to create a new template file or edit an existing file
- Custom tasks are written in `$env.comma` and can be nested
- no spaces allowed in name (except testing)
- Generate completions based on the structure of `$env.comma`
- You can use closure to customize completion
- In `$env.commax.act` of default closure, you can receive parameters after the current position
- In `$env.commax.cmp` , you can receive the parameter before the current position
- In `$_.act` of default closure, you can receive parameters after the current position
- In `$_.cmp` , you can receive the parameter before the current position
- Supports `computed`, the definition method refers to `$env.comma_scope.computed`, accepts two parameters, runtime parameters and `$env.comma_scope`
- Supports `filter`, similar to `computed`, but only runs when declared, specified through `$env.comm.flt`
- Supports `watch`, specified through `$env.comm.wth`. Support polling like 'poll:2sec'
- Supports `filter`, similar to `computed`, but only runs when declared, specified through `$_.flt`
- Supports `watch` (and polling), specified through `$_.wth`.
- `glob` defaults to `*`, `op` defaults to `['Write']`, `postpone` defaults to `false`
- In watch mode (not Polling) inject `$_.wth`(op, path, new_path) into parameter `$s`
- when the `interval` field is included, it is polling mode(`clear` defaults to 'false')
- Identity supports alias
- children sub s
- description desc dsc d
- action act a
- completion cmp c
- filter flt f
- computed cpu u
- watch wth w
- tag
- expect exp e x
- mock test_args m
- report rpt r
example:
```
$env.comma_scope = {
created: '2023-12-21{4}10:35:31'
computed: {$env.comm.cpu:{|a, s| $'($s.created)($a)' }}
say: {|s| print $'(ansi yellow_italic)($s)(ansi reset)' }
quick: {$env.comm.flt:{|a, s| do $s.say 'run a `quick` filter' }}
slow: {$env.comm.flt:{|a, s|
$env.comma_scope = {|_|{
created: '2023-12-24{0}01:46:27'
computed: {$_.computed:{|a, s| $'($s.created)($a)' }}
say: {|s| print $'(ansi $_.settings.theme.info)($s)(ansi reset)' }
quick: {$_.filter:{|a, s| do $s.say 'run a `quick` filter' }}
slow: {$_.filter:{|a, s|
do $s.say 'run a `slow` filter'
sleep 1sec
do $s.say 'filter need to be declared'
sleep 1sec
$'($s.computed)<($a)>'
}}
}
}}
$env.comma = {
$env.comma = {|_|{
created: {|a, s| $s.computed }
open: {
$env.comm.sub: {
any: {
$env.comm.act: {|a, s| open $a.0}
$env.comm.cmp: {ls | get name}
$env.comm.dsc: 'open a file'
}
json: {
$env.comm.act: {|a, s| open $a.0}
$env.comm.cmp: {ls *.json | get name}
$env.comm.dsc: 'open a json file'
$env.comm.wth: '*.json'
}
scope: {
$env.comm.act: {|a, s| print $'args: ($a)'; $s }
$env.comm.flt: ['slow']
$env.comm.dsc: 'open scope'
$env.comm.wth: 'poll:2sec'
}
}
$env.comm.dsc: 'open something'
$env.comm.flt: ['quick']
}
# Nest as you like
a: {
b: {
c: {
$env.commax.act: {|x| print $x }
$env.commax.dsc: 'description'
$env.commax.cmp: {|| ls | get name }
}
d: { pwd }
}
x: {
$env.commax.sub: {
y: {
$env.commax.act: {|x| print y}
$env.commax.cmp: {|| [y1 y2 y3]}
$env.commax.dsc: 'description'
inspect: {|a, s| {index: $_, scope: $s, args: $a} | table -e }
test: {
$_.sub: {
batch: { 'created; inspect' | do $_.batch }
watch: {
$_.action: {|a, s| $s | get $_.watch }
$_.completion: {ls *.json | get name}
$_.desc: 'inspect watch context'
$_.watch: {
glob: '*'
op: ['Write', 'Create']
postpone: true
}
}
open_file: {
$_.action: {|a, s| open $a.0 }
$_.completion: {ls | get name}
$_.desc: 'open a file'
$_.filter: ['slow']
}
ping: {
$_.action: {|a, s| ping -c 2 localhost }
$_.watch: {
interval: 2sec
clear: true
}
}
$env.commax.dsc: 'xxx'
}
$_.desc: 'run test'
$_.filter: ['quick']
}
}
}}
```
### todo
- [x] run
- [ ] dry
- [ ] dry wrap lines
- [ ] accept list<string>
- [x] formatter: outdent
- [x] complete
- [x] with args
- [x] scoped test
- [x] tree map
- [x] test
- [ ] tag
- [ ] watch mode
- [x] override sub node watch
- [x] args
- [x] allow running on leaf node
- [x] test action
- [x] scope
- [ ] filter
- [x] support many expect (list) for one spec
- [ ] curl integration
- [ ] report
- [x] `$x.report` in `test_message` should be `list<string>`
- [x] diff
- [ ] run with `nu -c` (dynamic source nu file)
- [ ] template
- [x] vscode-tasks
- [ ] should panic when identity not exists
- [ ] integration
- [x] gen vscode task json file
- [x] tree map
- [x] batch mode
- [x] run complete in batch mode
- [x] Input variables
- [ ] pickString
- [x] augustocdias.tasks-shell-input
- [x] allow rest args as `promptString`
- https://code.visualstudio.com/docs/editor/variables-reference
- [x] clean filter output
- [x] add gen vscode-tasks to template
- [ ] test and watch
- [x] modulize
- [x] refactor with `resolve node`
- [x] run
- [x] complete
- [x] fix redundant filter in description
- [x] theme
- [x] poll sep bar
- [x] tips

File diff suppressed because it is too large Load Diff

252
modules/comma/comma_test.nu Normal file
View File

@ -0,0 +1,252 @@
$env.comma_scope = {|_|{
created: '2023-12-23{6}12:51:14'
computed: {$_.computed:{|a, s| $'($s.created)($a)' }}
say: {|s| print -e $'(ansi $_.settings.theme.info)($s)(ansi reset)' }
q1: {$_.flt:{|a, s| do $s.say 'run `q1` filter' }}
q2: {$_.flt:{|a, s| do $s.say 'run `q2` filter' }}
q3: {$_.flt:{|a, s| do $s.say 'run `q3` filter' }}
q4: {$_.flt:{|a, s| do $s.say 'run `q4` filter' }}
slow: {$_.flt:{|a, s|
do $s.say 'run a `slow` filter'
sleep 1sec
do $s.say 'filter need to be declared'
sleep 1sec
$'($s.computed)<($a)>'
}}
}}
$env.comma = {|_|{
created: {|a, s| $s.computed }
inspect: {|a, s| {index: $_, scope: $s, args: $a} }
suit: {
scope: {
'`say` in scope': {
$_.act: {|a, s| $s}
$_.exp: {|r, a| 'say' in $r }
}
}
dry_run: {
a: { pp pwd }
no: {
$_.a: {, suit dry_run a }
$_.x: {|r| ($r | lines | get 0) == $env.PWD }
}
yes: {
$_.a: {, -p suit dry_run a }
$_.x: {|r| $r == 'pwd'}
}
ee: {
$_.a: {
pp --print aaa bbbb ccccc dddddd eeeeee [
ffffff gggggggggg [
hhhhhhhhh iiiiiiiiii lllllllll
] mmmmmmmmmmmmm nnnnnnnnnnnn
aaaaaaaaaaaaaaa
xxxxxxxxxxxxxxxx
yyyyyyyyyyyyyyyy
zzzzzzzzzzzzzzz
jjjjjjjjjjjjj
] oooooooo ppppppppp [qqqqqq [rrrrrr ssssss tttttt] uuuuuu]
}
$_.x: ( '
aaa bbbb ccccc dddddd eeeeee \
ffffff gggggggggg \
hhhhhhhhh iiiiiiiiii lllllllll \
mmmmmmmmmmmmm nnnnnnnnnnnn aaaaaaaaaaaaaaa xxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyy \
zzzzzzzzzzzzzzz jjjjjjjjjjjjj \
oooooooo ppppppppp \
qqqqqq \
rrrrrr ssssss tttttt \
uuuuuu' | do $_.outdent)
$_.report: $_.diff
}
}
completion: {
'example a b c e': {
$_.act: {, -c example a b c e }
$_.x: [
{|r,a| $r | where value == 'f' | not ($in | is-empty) }
$_.T
{|r,a| 'q1|q2|q3|q4| open a file' == ($r | get 1.description) }
]
}
'with args': {
$_.act: { , -c example a b c e open_file }
$_.x: {|r,a| ',.nu' in $r }
}
args: {
$_.a: {|a,s| $a }
$_.c: {|a,s| $a }
}
'': {
$_.act: { , test -c }
$_.x: {|r,a| $r | where value == 'ping' | not ($in | is-empty) }
}
'run with args': {
$_.act: {|a| , suit completion args $a }
$_.m: [a b c d e f g]
$_.x: {|r,a| $r == $a }
}
'with multiple args': {
$_.act: {|a| , -c suit completion args $a }
$_.m: [a b c d e f g]
$_.x: {|r,a| $r == $a }
}
}
vscode: {
'gen': {
$_.a: { , --vscode }
$_.x: {|r,a| $r.version == '2.0.0' }
}
}
}
example: {
a: {
b: {
$_.sub: {
t1: {
$_.act: $_.T
$_.exp: $_.T
}
open_file: {
$_.act: {|a, s| open $a.0 }
$_.cmp: {ls | get name}
$_.dsc: 'open a file'
}
c: {
$_.sub: {
d: {|| print 'ok'}
e: {
$_.sub: {
f: {
$_.act: $_.T
}
open_file: {
$_.act: {|a, s| open $a.0 }
$_.cmp: {ls | get name}
$_.dsc: 'open a file'
$_.filter: ['q4']
$_.exp: {|r, a| $r == (open ',.nu') }
$_.mock: [',.nu']
$_.report: $_.diff
}
t3: {
$_.act: $_.T
$_.exp: $_.T
}
}
$_.dsc: 'ok'
$_.flt: ['q3']
}
}
}
t2: {
$_.act: $_.T
$_.exp: $_.T
}
}
$_.dsc: 'this way'
$_.filter: ['q1' 'q2']
}
g: {}
}
set: {|a, s| do $_.config {|d| $d | upsert $a.0 $a.1 } }
get: {|a,s| $_.settings; {a: 123} }
add: {|a,s| ($a.0 | into int) + ($a.1 | into int) }
}
test: {
comma: {
$_.act: {
', test all' | do $_.batch 'comma_test.nu'
}
$_.wth: {
glob: '*.nu'
clear: true
}
$_.dsc: 'copy this to uplevel'
}
all: {
do $_.test 'add' {
expect: {|x| $x == 3 }
spec: {|a|, example add 1 2 }
}
do $_.test 'struct' {
spec: {|a| , test struct }
}
do $_.test 'args' {
expect: {|x, a| true }
spec: {|x| [$x] }
args: 'this is args'
context: {|x, a| $x}
}
do $_.test 'set env' {
spec: { , test set-env }
expect: {|x| $x.a? == 123 }
}
do $_.test 'open file' {
expect: {|x| $x == (open ,.nu) }
spec: { , example a b c e open_file ,.nu }
}
do $_.test 'completion' {
expect: {|x| ',.nu' in $x }
spec: { , -c example a b c e open_file }
args: [example a b c e open_file]
}
do $_.test 'completion' {
expect: {|x| $x | where value == 'f' | not ($in | is-empty) }
spec: {|x| , -c $x }
args: [example a b c e]
}
do $_.test 'run test' {
expect: $_.T
spec: {, -e test example }
}
do $_.test 'run leaf test' {
expect: $_.T
spec: { , -t example a b t1 }
}
do $_.test 'suit' {
expect: $_.T
spec: { , -t suit }
}
}
struct: {
$_.act: {
, example a b c e f
}
}
set-env: {
$_.act: {
, example set a 123
, example get
}
}
other: {|a, s|
$s
}
poll: {
$_.act: { print $env.PWD }
$_.wth: {
interval: 1sec
}
}
ping: {
$_.action: {|a, s| ping -c 2 localhost }
$_.watch: {
interval: 2sec
clear: true
}
$_.filter: ['slow']
}
vscode: {
gen: {
$_.act: {|a,s| print $a.0 }
$_.wth: { glob: '*.nu' }
}
complete: {
$_.act: $_.T
$_.wth: { glob: '*.nu' }
}
}
}
}}

View File

@ -0,0 +1,64 @@
$env.comma_scope = {|_|{
created: '{{time}}'
computed: {$_.computed:{|a, s| $'($s.created)($a)' }}
quick: {$_.filter:{|a, s| do $_.tips 'run filter' `quick` }}
slow: {$_.filter:{|a, s|
do $_.tips 'run filter' `slow`
sleep 1sec
do $_.tips 'filter need to be declared'
sleep 1sec
$'($s.computed)<($a)>'
}}
}}
$env.comma = {|_|{
created: {|a, s| $s.computed }
inspect: {|a, s| {index: $_, scope: $s, args: $a} | table -e }
vscode-tasks: {
$_.a: {
mkdir .vscode
', --vscode -j' | do $_.batch ',.nu' | save -f .vscode/tasks.json
}
$_.d: "generate .vscode/tasks.json"
$_.w: { glob: ',.nu' }
}
dev: {
run: {
$_.action: {|a,s| nu $a.0 }
$_.watch: { glob: '*.nu', clear: true }
$_.completion: { ls *.nu | get name }
$_.desc: "develop a nu script"
}
watch: {
$_.a: {|a,s| $', dev run ($a.0)' | do $_.batch ',.nu' }
$_.x: {|r,a| false }
$_.m: [,.nu]
$_.c: { ls *.nu | get name }
}
}
test: {
$_.sub: {
batch: { ', created; , inspect' | do $_.batch ',.nu' }
watch: {
$_.act: {|a, s| $s | get $_.watch }
$_.cmp: {ls *.json | get name}
$_.dsc: 'inspect watch context'
$_.wth: {
glob: '*'
op: ['Write', 'Create']
postpone: true
}
}
ping: {
$_.action: {|a, s| ping -c 2 localhost }
$_.watch: {
interval: 2sec
clear: true
}
$_.filter: ['slow']
}
}
$_.desc: 'run test'
$_.filter: ['quick']
}
}}

View File

@ -77,6 +77,11 @@ export def empty-sqlite [] {
| decode base64 --binary | gzip -d
}
export def 'cwd history delete' [cwd] {
open $env.cwd_history_file
| query db $"delete from cwd_history where cwd = '($cwd)';"
}
export-env {
$env.cwd_history_full = false
$env.cwd_history_file = '~/.cache/nu_cwd_history.sqlite'

View File

@ -36,37 +36,30 @@ export def container-process-list [
-n: string@"nu-complete docker ns"
container?: string@"nu-complete docker containers"
] {
let cli = $env.docker-cli
if ($container | is-empty) {
let cli = $env.docker-cli
if $cli == 'docker' {
^$cli ps -a --format '{"id":"{{.ID}}", "image": "{{.Image}}", "name":"{{.Names}}", "cmd":{{.Command}}, "port":"{{.Ports}}", "status":"{{.Status}}", "created":"{{.CreatedAt}}"}'
let fmt = '{"id":"{{.ID}}", "image": "{{.Image}}", "name":"{{.Names}}", "cmd":{{.Command}}, "port":"{{.Ports}}", "status":"{{.Status}}", "created":"{{.CreatedAt}}"}'
let fmt = if $cli == 'podman' { $fmt | str replace '{{.Command}}' '"{{.Command}}"' | str replace '{{.CreatedAt}}' '{{.Created}}' } else { $fmt }
^$cli ps -a --format $fmt
| lines
| each {|x|
let r = $x | from json
let t = $r.created | str substring ..25 | into datetime -f '%Y-%m-%d %H:%M:%S %z'
let t = $r.created | into datetime
$r | upsert created $t
}
} else if $cli == 'podman' {
^$cli ps -a --format '{"id":"{{.ID}}", "image": "{{.Image}}", "name":"{{.Names}}", "cmd":"{{.Command}}", "port":"{{.Ports}}", "status":"{{.Status}}", "created":"{{.Created}}"}'
| lines
| each {|x|
let r = $x | from json
let t = $r.created | str substring ..32 | into datetime
$r | upsert created $t
}
} else {
^$cli ($n | with-flag -n) ps -a
| from ssv
| rename id image cmd created status port name
}
} else {
let r = ^$env.docker-cli ($n | with-flag -n) inspect $container
let r = ^$cli ($n | with-flag -n) inspect $container
| from json
| get 0
#let e = $r.Config.Env | reduce -f {} {|i, a|
# let x = $i | split row '='
# $a | upsert $x.0 $x.1?
#}
let image = $r.Image
let img = ^$cli ($n | with-flag -n) inspect $image
| from json
| get 0
let imgEnv = $img.Config.Env?
| reduce -f {} {|i, a|
let x = $i | split row '='
$a | upsert $x.0 $x.1?
}
let m = $r.Mounts
| reduce -f {} {|i, a|
if $i.Type == 'bind' {
@ -78,13 +71,13 @@ export def container-process-list [
{
name: $r.Name?
hostname: $r.Config.Hostname?
image: $r.Image
image: $image
created: $r.Created
id: $r.Id
ports: $p
# FIXME: env
env: $imgEnv
mounts: $m
path: $r.Path
entrypoint: $r.Path?
args: $r.Args
}
}
@ -97,29 +90,30 @@ export def image-list [
] {
if ($img | is-empty) {
^$env.docker-cli ($n | with-flag -n) images
| from ssv -a
| each {|x|
let size = $x.SIZE | into filesize
let path = $x.REPOSITORY | split row '/'
let image = $path | last
let repo = $path | range ..(($path|length) - 2) | str join '/'
{
repo: $repo
image: $image
tag: $x.TAG
id: $x.'IMAGE ID'
created: $x.CREATED
size: $size
| from ssv -a
| each {|x|
let size = $x.SIZE | into filesize
let path = $x.REPOSITORY | split row '/'
let image = $path | last
let repo = $path | range ..(($path|length) - 2) | str join '/'
{
repo: $repo
image: $image
tag: $x.TAG
id: $x.'IMAGE ID'
created: $x.CREATED
size: $size
}
}
}
} else {
let r = ^$env.docker-cli ($n | with-flag -n) inspect $img
| from json
| get 0
let e = $r.Config.Env | reduce -f {} {|i, a|
let x = $i | split row '='
$a | upsert $x.0 $x.1?
}
| from json
| get 0
let e = $r.Config.Env?
| reduce -f {} {|i, a|
let x = $i | split row '='
$a | upsert $x.0 $x.1?
}
{
id: $r.Id
created: $r.Created
@ -129,7 +123,7 @@ export def image-list [
size: $r.Size
labels: $r.Labels?
env: $e
entrypoint: $r.Config.Entrypoint
entrypoint: $r.Config.Entrypoint?
cmd: $r.Config.Cmd?
}
}

View File

@ -526,7 +526,7 @@ export def kgno [] {
def "nu-complete kube deploys and pods" [context: string, offset: int] {
let ctx = $context | argx parse
let ns = $ctx.namespace? | with-flag -n
if ($ctx._pos.pod? | default '' | str ends-with '-') {
if ($ctx.a? | default false) or ($ctx._pos.pod? | default '' | str ends-with '-') {
kubectl get $ns pods | from ssv -a | get NAME
} else {
kubectl get $ns deployments | from ssv -a | get NAME | each {|x| $"($x)-"}
@ -588,6 +588,7 @@ export def --wrapped ka [
--namespace (-n): string@"nu-complete kube ns"
--container(-c): string@"nu-complete kube ctns"
--selector(-l): string
--all-pods(-a)
...args
] {
let n = $namespace | with-flag -n
@ -634,6 +635,7 @@ export def kl [
--container(-c): string@"nu-complete kube ctns"
--follow(-f)
--previous(-p)
--all-pods(-a)
] {
let n = $namespace | with-flag -n
let c = $container | with-flag -c