btape 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +191 -67
- data/lib/btape/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3f37303b9cf610d9df7cf05199599bee524a462812e328e73bfda3f003138aec
|
|
4
|
+
data.tar.gz: 62151779b2bbe5ae60c645c66e4349b6a7f6655cf6828999c3b3d1c4bba4ff73
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 82c0c748046529933359f27c5c46dc821f64f6b8b8bfee78b27b477155cf33f446f17ddc8b89b17eadda4afc1f643b588b6ecab88a64499dc4aa91afc38f3156
|
|
7
|
+
data.tar.gz: 52f9314a98ab708877b04832bd52aa5f7732c20999b597e8c4abe936d7fa285d49fabe9624e73a55b75c32d62dbf3a2b40e95701bd26462f671df8851fd3577c
|
data/README.md
CHANGED
|
@@ -4,16 +4,105 @@
|
|
|
4
4
|
<img src="assets/logo.png" alt="btape logo" width="480">
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="assets/demo.gif" alt="btape recording a login flow into an animated GIF" width="720">
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<sub>Run a <code>.tape</code> file, get that GIF. This one is <code>examples/demo.tape</code>.</sub>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
## What is btape
|
|
16
|
+
|
|
17
|
+
A `.tape` file lists the browser actions you would otherwise click through by
|
|
18
|
+
hand. `btape` performs them against a real browser and records what the page
|
|
19
|
+
did as an animated GIF:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
Output demo.gif
|
|
23
|
+
Viewport 1280x720
|
|
24
|
+
|
|
25
|
+
Goto https://example.com
|
|
26
|
+
Click "text=Login"
|
|
27
|
+
Type "#email" "demo@example.com"
|
|
28
|
+
Sleep 2s
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
It is a small, VHS-inspired Ruby CLI. Ferrum controls Chromium and captures
|
|
32
|
+
PNG frames, and a pure-Ruby encoder produces the GIF. It does not require
|
|
33
|
+
Playwright, Selenium, ffmpeg, or an external service.
|
|
11
34
|
|
|
12
35
|
Tapes are written by hand, or asked of a language model running on the same
|
|
13
36
|
machine: `btape generate` describes the language to LM Studio, Ollama or
|
|
14
37
|
anything else speaking their API, and holds the answer to the parser before
|
|
15
38
|
handing it over.
|
|
16
39
|
|
|
40
|
+
## Demo
|
|
41
|
+
|
|
42
|
+
The GIF above was recorded by btape itself, from `examples/demo.tape` driving
|
|
43
|
+
`examples/demo_app.html` — a static page bundled with the repository, so the
|
|
44
|
+
demo needs no other service running:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
Output demo.gif
|
|
48
|
+
Viewport 1280x720
|
|
49
|
+
|
|
50
|
+
Goto file:///app/examples/demo_app.html
|
|
51
|
+
Sleep 500ms
|
|
52
|
+
|
|
53
|
+
Click "text=Login"
|
|
54
|
+
Sleep 300ms
|
|
55
|
+
Type "#email" "demo@example.com"
|
|
56
|
+
Type "#password" "password"
|
|
57
|
+
Click "text=Sign in"
|
|
58
|
+
Sleep 2s
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`file:///app/…` is where the development container mounts the checkout, so
|
|
62
|
+
`dip demo` records it as it stands. Outside the container, point `Goto` at
|
|
63
|
+
your own copy of that file — or at any URL at all, which is the point.
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
gem install btape
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
or, in a Gemfile:
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
gem 'btape'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Chromium — or Chrome — must be installed and discoverable by Ferrum:
|
|
78
|
+
`brew install --cask chromium` on macOS, `apt-get install chromium` on Debian
|
|
79
|
+
or Ubuntu. A browser installed somewhere unusual is named by `BROWSER_PATH`,
|
|
80
|
+
and one running on another machine by `--ws-url`; see
|
|
81
|
+
[A browser running somewhere else](#a-browser-running-somewhere-else).
|
|
82
|
+
|
|
83
|
+
## Quick start
|
|
84
|
+
|
|
85
|
+
Write `demo.tape`:
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
Output demo.gif
|
|
89
|
+
Viewport 1280x720
|
|
90
|
+
|
|
91
|
+
Goto https://example.com
|
|
92
|
+
Sleep 1s
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
and run it:
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
btape demo.tape
|
|
99
|
+
# Created /path/to/demo.gif
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`Output` is where the GIF goes, resolved relative to the tape file. Point
|
|
103
|
+
`Goto` at your own application and add `Click`, `Type` and `WaitFor` lines
|
|
104
|
+
until the recording shows what you want it to. The whole language is below.
|
|
105
|
+
|
|
17
106
|
## Commands
|
|
18
107
|
|
|
19
108
|
```text
|
|
@@ -32,27 +121,53 @@ Screenshot [name]
|
|
|
32
121
|
Sleep <number>ms|s
|
|
33
122
|
```
|
|
34
123
|
|
|
124
|
+
| Command | What it does |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `Output` | Where the GIF is written. Required, and read before the run wherever it appears |
|
|
127
|
+
| `Viewport` | The size the page is rendered at, and so the size of the GIF before `Scale` |
|
|
128
|
+
| `Set` | Configures the run rather than acting on the page; the names are in [Settings](#settings) |
|
|
129
|
+
| `Goto` | Navigates the page, and puts the following commands back in the main frame |
|
|
130
|
+
| `Click` | Clicks the first element the selector matches |
|
|
131
|
+
| `Type` | Focuses the first element the selector matches and types the text into it |
|
|
132
|
+
| `Press` | Presses a key by name — `Enter`, `Tab`, `Right` — once, or `count` times |
|
|
133
|
+
| `Frame` | Points the following commands at an iframe; `Frame main` returns to the page |
|
|
134
|
+
| `Evaluate` | Runs JavaScript in the current frame |
|
|
135
|
+
| `WaitFor` | Polls until the selector matches something, then carries on |
|
|
136
|
+
| `WaitForJS` | Polls until the JavaScript returns something truthy |
|
|
137
|
+
| `Screenshot` | Captures one frame now, and with a name puts it at a predictable path too |
|
|
138
|
+
| `Sleep` | Holds where it is for the duration, recording all the while |
|
|
139
|
+
|
|
35
140
|
Arguments containing spaces must be quoted. Empty lines and lines beginning
|
|
36
141
|
with `#` are ignored. `Output` is required; `Viewport` defaults to `1280x720`.
|
|
37
|
-
Output paths are resolved relative to the tape file.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
142
|
+
Output paths are resolved relative to the tape file. Selectors are CSS, or
|
|
143
|
+
`text=Some text` to match an element by the text it shows. Durations are a
|
|
144
|
+
number followed by `ms` or `s` — `500ms`, `1.5s`.
|
|
145
|
+
|
|
146
|
+
`Evaluate` is how a tape reaches an API the page exposes rather than clicking
|
|
147
|
+
at it. It runs in whatever frame is current, which is the page until a `Frame`
|
|
148
|
+
says otherwise; navigating returns to the page too, since the frame belonged
|
|
149
|
+
to the page that was left.
|
|
150
|
+
|
|
151
|
+
`WaitFor` and `WaitForJS` poll instead of guessing at a `Sleep`. Both check
|
|
152
|
+
every `WaitInterval`, and are satisfied only once `WaitStable` checks in a row
|
|
153
|
+
have passed; a check that fails puts the count back to zero, so a page that
|
|
154
|
+
reports itself ready before it has settled is caught by raising `WaitStable`
|
|
155
|
+
above 1. A duration on the line is how long that one wait gets, and
|
|
156
|
+
`WaitTimeout` is how long the ones without get. Neither raises for a check
|
|
157
|
+
that failed — only running out of time does, against the line that was
|
|
158
|
+
waiting. An expression that raises counts as not-yet-true rather than as an
|
|
159
|
+
error, but the last thing it raised is named in the timeout, so an expression
|
|
160
|
+
that could never be true still says why.
|
|
161
|
+
|
|
162
|
+
`Screenshot` captures a frame there and then. It is what `Set CaptureMode
|
|
163
|
+
manual` records with — one frame per page of a deck, rather than a few hundred
|
|
164
|
+
near-identical ones — and a name makes that one frame findable afterwards, as
|
|
165
|
+
`frame-NAME.png` under `--frames-dir` and as `result.named_frames['NAME']`
|
|
166
|
+
from Ruby.
|
|
167
|
+
|
|
168
|
+
`Sleep` is for holding a finished frame on screen long enough to be seen,
|
|
169
|
+
which is a different job from waiting for the page: interval recording carries
|
|
170
|
+
on throughout one, so a `Sleep` is what gives a GIF its pauses.
|
|
56
171
|
|
|
57
172
|
## Settings
|
|
58
173
|
|
|
@@ -76,15 +191,7 @@ can run in more than one place.
|
|
|
76
191
|
| `WaitStable` | `1` | How many checks in a row must pass before a wait is satisfied |
|
|
77
192
|
| `MaxFrames` | `600` | Stop rather than record a hung page until the disk fills |
|
|
78
193
|
|
|
79
|
-
##
|
|
80
|
-
|
|
81
|
-
Chromium must be installed and discoverable by Ferrum. Then:
|
|
82
|
-
|
|
83
|
-
```sh
|
|
84
|
-
bundle install
|
|
85
|
-
bundle exec btape demo.tape
|
|
86
|
-
bundle exec rake spec
|
|
87
|
-
```
|
|
194
|
+
## Command line
|
|
88
195
|
|
|
89
196
|
```text
|
|
90
197
|
Usage: btape [options] SCRIPT.tape
|
|
@@ -100,6 +207,35 @@ Subcommands:
|
|
|
100
207
|
|
|
101
208
|
`BTAPE_WS_URL` is used when neither `--ws-url` nor `--set WsUrl=` is given.
|
|
102
209
|
|
|
210
|
+
## Advanced usage
|
|
211
|
+
|
|
212
|
+
### A browser running somewhere else
|
|
213
|
+
|
|
214
|
+
btape launches its own Chromium by default. Point it at one that is already
|
|
215
|
+
running — a `browserless`/`chrome` container, say — and no browser needs to be
|
|
216
|
+
in the image btape runs from:
|
|
217
|
+
|
|
218
|
+
```sh
|
|
219
|
+
btape --ws-url ws://chrome:3000 examples/thumbnails.tape
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Each connection gets its own browser context, so concurrent runs against one
|
|
223
|
+
shared browser do not see each other. The viewport is applied over the wire,
|
|
224
|
+
since a browser that is already running cannot be told its window size at
|
|
225
|
+
launch.
|
|
226
|
+
|
|
227
|
+
`examples/thumbnails.tape` is the other shape of run: one frame per page of a
|
|
228
|
+
deck, captured with `Set CaptureMode manual` against a remote browser.
|
|
229
|
+
|
|
230
|
+
### Frames, not just the GIF
|
|
231
|
+
|
|
232
|
+
Frames are normally written to a temporary directory and removed as the run
|
|
233
|
+
unwinds. `--frames-dir` keeps them:
|
|
234
|
+
|
|
235
|
+
```sh
|
|
236
|
+
btape --frames-dir frames examples/thumbnails.tape
|
|
237
|
+
```
|
|
238
|
+
|
|
103
239
|
### Fonts, and text that is not Latin
|
|
104
240
|
|
|
105
241
|
Glyphs come from the fonts the browser can see, which is not necessarily the
|
|
@@ -166,31 +302,7 @@ WaitForJS "(() => { const c = document.createElement('canvas').getContext('2d');
|
|
|
166
302
|
Tape files themselves are read as UTF-8 whatever the locale says, so a `Type`
|
|
167
303
|
line or a `text=` selector can be written in any script.
|
|
168
304
|
|
|
169
|
-
###
|
|
170
|
-
|
|
171
|
-
btape launches its own Chromium by default. Point it at one that is already
|
|
172
|
-
running — a `browserless`/`chrome` container, say — and no browser needs to be
|
|
173
|
-
in the image btape runs from:
|
|
174
|
-
|
|
175
|
-
```sh
|
|
176
|
-
btape --ws-url ws://chrome:3000 examples/thumbnails.tape
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
Each connection gets its own browser context, so concurrent runs against one
|
|
180
|
-
shared browser do not see each other. The viewport is applied over the wire,
|
|
181
|
-
since a browser that is already running cannot be told its window size at
|
|
182
|
-
launch.
|
|
183
|
-
|
|
184
|
-
### Frames, not just the GIF
|
|
185
|
-
|
|
186
|
-
Frames are normally written to a temporary directory and removed as the run
|
|
187
|
-
unwinds. `--frames-dir` keeps them:
|
|
188
|
-
|
|
189
|
-
```sh
|
|
190
|
-
btape --frames-dir frames examples/thumbnails.tape
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
## Writing a tape with a local model
|
|
305
|
+
### Writing a tape with a local model
|
|
194
306
|
|
|
195
307
|
`btape generate` describes the language to a model running on your own
|
|
196
308
|
machine and asks it for a tape:
|
|
@@ -238,7 +350,7 @@ when told which line. What it cannot know is your markup, so a tape it wrote
|
|
|
238
350
|
still names selectors that have to be checked against the page. Read it before
|
|
239
351
|
you run it, the way you would read anything else generated for you.
|
|
240
352
|
|
|
241
|
-
|
|
353
|
+
#### A model that is not on this machine
|
|
242
354
|
|
|
243
355
|
`--llm-url` is the whole of the configuration, so a hosted endpoint speaking
|
|
244
356
|
the same API works as well as a local one. Name the model rather than leaving
|
|
@@ -261,7 +373,7 @@ model keeps the description and the `--context` file on the machine that ran
|
|
|
261
373
|
the command; a hosted one is handed both, and a context file is usually a page
|
|
262
374
|
of your own markup rather than something you would have published.
|
|
263
375
|
|
|
264
|
-
|
|
376
|
+
### From Ruby
|
|
265
377
|
|
|
266
378
|
`Runner#run` returns a `Btape::Result`:
|
|
267
379
|
|
|
@@ -310,10 +422,19 @@ tape = generator.call('record the dashboard loading', context: page_markup)
|
|
|
310
422
|
Btape::Runner.new.run(Btape::Parser.new.parse(tape), base_directory: '.', output: buffer)
|
|
311
423
|
```
|
|
312
424
|
|
|
313
|
-
##
|
|
425
|
+
## Development
|
|
314
426
|
|
|
315
|
-
|
|
316
|
-
|
|
427
|
+
Working on btape itself, rather than using it, starts from a checkout:
|
|
428
|
+
|
|
429
|
+
```sh
|
|
430
|
+
bundle install
|
|
431
|
+
bundle exec rake spec
|
|
432
|
+
bundle exec rake rubocop
|
|
433
|
+
bundle exec exe/btape examples/demo.tape
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Or from the development image, which contains Ruby, Chromium and Latin fonts;
|
|
437
|
+
tapes that record other scripts need fonts for them added to it:
|
|
317
438
|
|
|
318
439
|
```sh
|
|
319
440
|
dip provision
|
|
@@ -325,11 +446,9 @@ wip dispatch demo
|
|
|
325
446
|
wip dispatch btape examples/demo.tape
|
|
326
447
|
```
|
|
327
448
|
|
|
328
|
-
|
|
329
|
-
`examples/
|
|
330
|
-
|
|
331
|
-
something else. `examples/thumbnails.tape` shows the other shape of run: one
|
|
332
|
-
frame per page of a deck, against a browser running elsewhere.
|
|
449
|
+
CI runs the specs and RuboCop across Ruby 3.2 to 4.0, and records
|
|
450
|
+
`examples/demo.tape` against a real Chromium to check that a GIF still comes
|
|
451
|
+
out the other end. [CONTRIBUTING.md](CONTRIBUTING.md) has the rest.
|
|
333
452
|
|
|
334
453
|
## Limitations
|
|
335
454
|
|
|
@@ -356,3 +475,8 @@ This project follows the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
|
356
475
|
## License
|
|
357
476
|
|
|
358
477
|
[MIT](LICENSE)
|
|
478
|
+
|
|
479
|
+
## Repository topics
|
|
480
|
+
|
|
481
|
+
Suggested GitHub topics for this repository: `browser-automation`, `chromium`,
|
|
482
|
+
`cli`, `ruby`, `developer-tools`, `screen-recording`, `gif`, `ferrum`, `vhs`.
|
data/lib/btape/version.rb
CHANGED