rundoc 2.0.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +10 -7
- data/CHANGELOG.md +15 -0
- data/CONTRIBUTING.md +25 -0
- data/README.md +35 -48
- data/Rakefile +5 -1
- data/bin/rundoc +4 -17
- data/lib/rundoc/cli.rb +207 -49
- data/lib/rundoc/cli_argument_parser.rb +140 -0
- data/lib/rundoc/code_command/bash.rb +8 -1
- data/lib/rundoc/code_command/rundoc/depend_on.rb +1 -24
- data/lib/rundoc/code_command/rundoc/require.rb +13 -7
- data/lib/rundoc/code_command/website/driver.rb +7 -5
- data/lib/rundoc/code_command/website/navigate.rb +1 -1
- data/lib/rundoc/code_command/website/screenshot.rb +7 -2
- data/lib/rundoc/code_command/website/visit.rb +1 -1
- data/lib/rundoc/code_section.rb +4 -6
- data/lib/rundoc/context/after_build.rb +14 -0
- data/lib/rundoc/context/execution.rb +22 -0
- data/lib/rundoc/parser.rb +9 -5
- data/lib/rundoc/version.rb +1 -1
- data/lib/rundoc.rb +13 -5
- data/rundoc.gemspec +1 -0
- data/test/fixtures/cnb/ruby/download.md +22 -0
- data/test/fixtures/cnb/ruby/image_structure.md +34 -0
- data/test/fixtures/cnb/ruby/intro.md +5 -0
- data/test/fixtures/cnb/ruby/multiple_langs.md +43 -0
- data/test/fixtures/cnb/ruby/rundoc.md +48 -0
- data/test/fixtures/cnb/ruby/what_is_pack_build.md +18 -0
- data/test/fixtures/cnb/shared/call_to_action.md +11 -0
- data/test/fixtures/cnb/shared/configure_builder.md +23 -0
- data/test/fixtures/cnb/shared/install_pack.md +14 -0
- data/test/fixtures/cnb/shared/pack_build.md +20 -0
- data/test/fixtures/cnb/shared/procfile.md +13 -0
- data/test/fixtures/cnb/shared/use_the_image.md +52 -0
- data/test/fixtures/cnb/shared/what_is_a_builder.md +18 -0
- data/test/fixtures/rails_4/rundoc.md +5 -7
- data/test/fixtures/rails_5/rundoc.md +1 -1
- data/test/fixtures/rails_6/rundoc.md +2 -2
- data/test/fixtures/rails_7/rundoc.md +2 -3
- data/test/fixtures/simple_git/rundoc.md +13 -0
- data/test/integration/after_build_test.rb +62 -0
- data/test/integration/print_test.rb +9 -9
- data/test/integration/require_test.rb +63 -0
- data/test/integration/website_test.rb +35 -0
- data/test/rundoc/cli_argument_parser_test.rb +118 -0
- data/test/rundoc/code_section_test.rb +40 -8
- data/test/rundoc/parser_test.rb +3 -3
- data/test/rundoc/peg_parser_test.rb +6 -6
- data/test/rundoc/regex_test.rb +22 -0
- data/test/system/exe_cli_test.rb +231 -0
- data/test/test_helper.rb +74 -1
- metadata +40 -3
@@ -0,0 +1,14 @@
|
|
1
|
+
## Install the pack CLI
|
2
|
+
|
3
|
+
We assume you have [docker installed](https://docs.docker.com/engine/install/) and a working copy [of git](https://github.com/git-guides/install-git). Next, you will need to install the CLI tool for building CNBs, [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/). If you're on a Mac you can install it via Homebrew:
|
4
|
+
|
5
|
+
```
|
6
|
+
:::-> print.text
|
7
|
+
$ brew install buildpacks/tap/pack
|
8
|
+
```
|
9
|
+
|
10
|
+
Ensure that `pack` is installed correctly:
|
11
|
+
|
12
|
+
```
|
13
|
+
:::>> $ pack --version
|
14
|
+
```
|
@@ -0,0 +1,20 @@
|
|
1
|
+
## Build the application image with the pack CLI
|
2
|
+
|
3
|
+
Now build an image named `my-image-name` by executing the heroku builder against the application by running the
|
4
|
+
`pack build` command:
|
5
|
+
|
6
|
+
```
|
7
|
+
$ pack build my-image-name --path .
|
8
|
+
:::-- $ docker rmi -f my-image-name
|
9
|
+
:::-- $ pack build my-image-name --path . 2>&1 | tee build_output.txt
|
10
|
+
:::-> $ cat build_output.txt
|
11
|
+
```
|
12
|
+
|
13
|
+
> [!NOTE]
|
14
|
+
> Your output may differ.
|
15
|
+
|
16
|
+
Verify that you see “Successfully built image my-image-name” at the end of the output. And verify that the image is present locally:
|
17
|
+
|
18
|
+
```
|
19
|
+
:::>> $ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" | grep my-image-name
|
20
|
+
```
|
@@ -0,0 +1,13 @@
|
|
1
|
+
## Configuring your web process with the Procfile
|
2
|
+
|
3
|
+
Most buildpacks rely on existing community standards to allow you to configure your application declaratively. They can also implement custom logic based on file contents on disk or environment variables present at build time.
|
4
|
+
|
5
|
+
The `Procfile` is a configuration file format that was [introduced by Heroku in 2011](https://devcenter.heroku.com/articles/procfile), you can now use this behavior on your CNB-powered application via the `heroku/procfile`, which like the rest of the buildpacks in our builder [is open source](https://github.com/heroku/buildpacks-procfile). The `heroku/procfile` buildpack allows you to configure your web startup process.
|
6
|
+
|
7
|
+
This is the `Procfile` of the getting started guide:
|
8
|
+
|
9
|
+
```
|
10
|
+
:::-> $ cat Procfile
|
11
|
+
```
|
12
|
+
|
13
|
+
By including this file and using `heroku/procfile` buildpack, your application will receive a default web process. You can configure this behavior by changing the contents of that file.
|
@@ -0,0 +1,52 @@
|
|
1
|
+
## Use the image
|
2
|
+
|
3
|
+
Even though we used `pack` and CNBs to build our image, it can be run with your favorite tools like any other OCI image. We will be using the `docker` command line to run our image.
|
4
|
+
|
5
|
+
By default, images will be booted into a web server configuration. You can launch the app we just built by running:
|
6
|
+
|
7
|
+
```
|
8
|
+
$ docker run -it --rm --env PORT=9292 -p 9292:9292 my-image-name
|
9
|
+
:::-> background.start("docker run --rm --env PORT=9292 -p 9292:9292 my-image-name", name: "docker_server")
|
10
|
+
:::-- $ sleep 10
|
11
|
+
:::-> background.log.read(name: "docker_server")
|
12
|
+
```
|
13
|
+
|
14
|
+
Now when you visit [http://localhost:9292](http://localhost:9292) you should see a working web application:
|
15
|
+
|
16
|
+
```
|
17
|
+
:::>> website.visit(name: "localhost", url: "http://localhost:9292")
|
18
|
+
:::>> website.screenshot(name: "localhost")
|
19
|
+
```
|
20
|
+
|
21
|
+
Don't forget to stop the docker container when you're done.
|
22
|
+
|
23
|
+
```
|
24
|
+
:::-- $ docker stop $(docker ps -q --filter ancestor=my-image-name )
|
25
|
+
:::-- background.stop(name: "docker_server")
|
26
|
+
```
|
27
|
+
|
28
|
+
Here's a quick breakdown of that command we just ran:
|
29
|
+
|
30
|
+
- `docker run` Create and run a new container from an image.
|
31
|
+
- `-it` Makes the container interactive and allocates a TTY.
|
32
|
+
- `--rm` Automatically remove the container when it exits.
|
33
|
+
- `--env PORT=9292` Creates an environment variable named `PORT` and sets it to `9292` this is needed so the application inside the container knows what port to bind the web server.
|
34
|
+
- `-p 9292:9292` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container.
|
35
|
+
- `my-image-name` The name of the image you want to use for the application.
|
36
|
+
|
37
|
+
So far, we've downloaded an application via git and run a single command `pack build` to generate an image, and then we can use that image as if it was generated via a Dockerfile via the `docker run` command.
|
38
|
+
|
39
|
+
In addition to running the image as a web server, you can access the container's terminal interactively. In a new terminal window try running this command:
|
40
|
+
|
41
|
+
```
|
42
|
+
$ docker run -it --rm my-image-name bash
|
43
|
+
```
|
44
|
+
|
45
|
+
Now you can inspect the container interactively. For example, you can see the files on disk with `ls`:
|
46
|
+
|
47
|
+
```
|
48
|
+
$ ls
|
49
|
+
:::-> $ docker run --rm my-image-name ls
|
50
|
+
```
|
51
|
+
|
52
|
+
And anything else you would typically do via an interactive container session.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
## What is a builder?
|
2
|
+
|
3
|
+
> [!NOTE]
|
4
|
+
> Skip ahead if you want to build the application first and get into the details later. You won't need to
|
5
|
+
> know about builders for the rest of this tutorial.
|
6
|
+
|
7
|
+
In short, a builder is a delivery mechanism for buildpacks. A builder contains references to base images and individual buildpacks. A base image contains the operating system and system dependencies. Buildpacks are the components that will configure an image to run your application, that’s where the bulk of the logic lives and why the project is called “Cloud Native Buildpacks” and not “Cloud Native Builders.”
|
8
|
+
|
9
|
+
You can view the contents of a builder via the command `pack builder inspect`. For example:
|
10
|
+
|
11
|
+
```
|
12
|
+
:::>> $ pack builder inspect heroku/builder:22 | grep Buildpacks: -m1 -A10
|
13
|
+
```
|
14
|
+
|
15
|
+
> [!NOTE]
|
16
|
+
> Your output version numbers may differ.
|
17
|
+
|
18
|
+
This output shows the various buildpacks that represent the different languages that are supported by this builder such as `heroku/go` and `heroku/nodejs-engine`.
|
@@ -252,13 +252,11 @@ Provision a Postgresql database using Add-ons.
|
|
252
252
|
>A `mini` Postgres size costs [$5 a month, prorated to the minute](https://elements.heroku.com/addons/heroku-postgresql). At the end of this tutorial, you will be prompted to [delete your database](https://devcenter.heroku.com/articles/heroku-postgresql#removing-the-add-on) to minimize costs.
|
253
253
|
|
254
254
|
```term
|
255
|
-
$ heroku addons:create heroku-postgresql:
|
256
|
-
Creating heroku-postgresql:
|
257
|
-
Database
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
Created postgresql-encircled-75487 as DATABASE_URL
|
255
|
+
$ heroku addons:create heroku-postgresql:essential-0
|
256
|
+
Creating heroku-postgresql:essential-0 on ⬢ shrouded-anchorage-34700... ~$0.007/hour (max $5/month)
|
257
|
+
Database should be available soon
|
258
|
+
postgresql-encircled-75487 is being created in the background. The app will restart when complete...
|
259
|
+
Use heroku addons:info postgresql-encircled-75487 to check creation progress
|
262
260
|
Use heroku addons:docs heroku-postgresql to view documentation
|
263
261
|
```
|
264
262
|
|
@@ -249,7 +249,7 @@ Provision a Postgresql database using Add-ons.
|
|
249
249
|
>A `mini` Postgres size costs [$5 a month, prorated to the minute](https://elements.heroku.com/addons/heroku-postgresql). At the end of this tutorial, you will be prompted to [delete your database](https://devcenter.heroku.com/articles/heroku-postgresql#removing-the-add-on) to minimize costs.
|
250
250
|
|
251
251
|
```term
|
252
|
-
:::>> $ heroku addons:create heroku-postgresql:
|
252
|
+
:::>> $ heroku addons:create heroku-postgresql:essential-0
|
253
253
|
```
|
254
254
|
|
255
255
|
Your Heroku app now has access to a Postgresql database. The credentials are stored in the `DATABASE_URL` environment variable, which Rails will connect to by convention.
|
@@ -279,7 +279,7 @@ Provision a [Heroku Postgres](https://devcenter.heroku.com/articles/heroku-postg
|
|
279
279
|
>A `mini` Postgres size costs [$5 a month, prorated to the minute](https://elements.heroku.com/addons/heroku-postgresql). At the end of this tutorial, we prompt you to [delete your database](https://devcenter.heroku.com/articles/heroku-postgresql#removing-the-add-on) to minimize costs.
|
280
280
|
|
281
281
|
```term
|
282
|
-
:::>> $ heroku addons:create heroku-postgresql:
|
282
|
+
:::>> $ heroku addons:create heroku-postgresql:essential-0
|
283
283
|
```
|
284
284
|
|
285
285
|
Your Heroku app can now access this Postgres database. The `DATABASE_URL` environment variable stores your credentials, which Rails connects to by convention.
|
@@ -315,7 +315,7 @@ To use an interactive shell session instead, you can execute `heroku run bash`.
|
|
315
315
|
|
316
316
|
## Scale and Access the Application
|
317
317
|
|
318
|
-
Heroku runs application code using defined processes and [process types](procfile). New applications don't a process type active by default. The following command scales your app up to one dyno, running the `web` process:
|
318
|
+
Heroku runs application code using defined processes and [process types](procfile). New applications don't have a process type active by default. The following command scales your app up to one dyno, running the `web` process:
|
319
319
|
|
320
320
|
```term
|
321
321
|
:::>- $ heroku ps:scale web=1
|
@@ -3,7 +3,6 @@
|
|
3
3
|
email = ENV['HEROKU_EMAIL'] || `heroku auth:whoami`
|
4
4
|
|
5
5
|
Rundoc.configure do |config|
|
6
|
-
config.project_root = "myapp"
|
7
6
|
config.filter_sensitive(email => "developer@example.com")
|
8
7
|
config.filter_sensitive(Dir.pwd => ".")
|
9
8
|
end
|
@@ -251,7 +250,7 @@ Provision a [Heroku Postgres](https://devcenter.heroku.com/articles/heroku-postg
|
|
251
250
|
>A `mini` Postgres size costs [$5 a month, prorated to the minute](https://elements.heroku.com/addons/heroku-postgresql). At the end of this tutorial, we prompt you to [delete your database](https://devcenter.heroku.com/articles/heroku-postgresql#removing-the-add-on) to minimize costs.
|
252
251
|
|
253
252
|
```term
|
254
|
-
:::>> $ heroku addons:create heroku-postgresql:
|
253
|
+
:::>> $ heroku addons:create heroku-postgresql:essential-0
|
255
254
|
```
|
256
255
|
|
257
256
|
Your Heroku app can now access this Postgres database. The `DATABASE_URL` environment variable stores your credentials, which Rails connects to by convention.
|
@@ -287,7 +286,7 @@ To use an interactive shell session instead, you can execute `heroku run bash`.
|
|
287
286
|
|
288
287
|
## Scale and Access the Application
|
289
288
|
|
290
|
-
Heroku runs application code using defined processes and [process types](procfile). New applications don't a process type active by default. The following command scales your app up to one dyno, running the `web` process:
|
289
|
+
Heroku runs application code using defined processes and [process types](procfile). New applications don't have a process type active by default. The following command scales your app up to one dyno, running the `web` process:
|
291
290
|
|
292
291
|
```term
|
293
292
|
:::>- $ heroku ps:scale web=1
|
@@ -0,0 +1,13 @@
|
|
1
|
+
```
|
2
|
+
:::-- $ git config --get user.email || git config --global user.email "developer@example.com"
|
3
|
+
:::-- $ git config --get user.name || git config --global user.name "Developer name"
|
4
|
+
```
|
5
|
+
|
6
|
+
```
|
7
|
+
:::>> $ echo "hello world" >> lol.txt
|
8
|
+
:::>> $ git init .
|
9
|
+
:::>> $ git add .
|
10
|
+
:::>> $ git commit -m first
|
11
|
+
:::>> $ echo "so long and thanks for all the fish" >> lol.txt
|
12
|
+
:::>> $ git add . && git commit -m second
|
13
|
+
```
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class IntegrationAfterBuildTest < Minitest::Test
|
4
|
+
def test_modifies_directory_structure
|
5
|
+
Dir.mktmpdir do |dir|
|
6
|
+
Dir.chdir(dir) do
|
7
|
+
dir = Pathname(dir)
|
8
|
+
|
9
|
+
source_path = dir.join("RUNDOC.md")
|
10
|
+
source_path.write <<~EOF
|
11
|
+
```
|
12
|
+
:::>> $ mkdir lol
|
13
|
+
:::>> $ touch lol/rofl.txt
|
14
|
+
```
|
15
|
+
EOF
|
16
|
+
|
17
|
+
io = StringIO.new
|
18
|
+
|
19
|
+
Rundoc::CLI.new(
|
20
|
+
io: io,
|
21
|
+
source_path: source_path,
|
22
|
+
on_success_dir: dir.join(SUCCESS_DIRNAME)
|
23
|
+
).call
|
24
|
+
|
25
|
+
assert dir.join(SUCCESS_DIRNAME).join("lol").exist?
|
26
|
+
assert dir.join(SUCCESS_DIRNAME).join("lol").join("rofl.txt").exist?
|
27
|
+
|
28
|
+
FileUtils.remove_entry_secure(dir.join(SUCCESS_DIRNAME))
|
29
|
+
|
30
|
+
source_path.write <<~EOF
|
31
|
+
```
|
32
|
+
:::-- rundoc.configure
|
33
|
+
Rundoc.configure do |config|
|
34
|
+
config.after_build do |context|
|
35
|
+
Dir.glob(context.output_dir.join("lol").join("*")).each do |item|
|
36
|
+
FileUtils.mv(item, context.output_dir)
|
37
|
+
end
|
38
|
+
|
39
|
+
FileUtils.rm_rf(context.output_dir.join("lol"))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
```
|
45
|
+
:::>> $ mkdir lol
|
46
|
+
:::>> $ touch lol/rofl.txt
|
47
|
+
```
|
48
|
+
EOF
|
49
|
+
|
50
|
+
io = StringIO.new
|
51
|
+
|
52
|
+
Rundoc::CLI.new(
|
53
|
+
io: io,
|
54
|
+
source_path: source_path,
|
55
|
+
on_success_dir: dir.join(SUCCESS_DIRNAME)
|
56
|
+
).call
|
57
|
+
|
58
|
+
assert dir.join(SUCCESS_DIRNAME).join("rofl.txt").exist?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
|
-
class
|
3
|
+
class IntegrationPrintTest < Minitest::Test
|
4
4
|
def test_erb_shared_binding_persists_values
|
5
5
|
key = SecureRandom.hex
|
6
6
|
contents = <<~RUBY
|
@@ -20,7 +20,7 @@ class ParserTest < Minitest::Test
|
|
20
20
|
one
|
21
21
|
#{key}
|
22
22
|
EOF
|
23
|
-
parsed =
|
23
|
+
parsed = parse_contents(contents)
|
24
24
|
actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
|
25
25
|
assert_equal expected, actual
|
26
26
|
end
|
@@ -43,7 +43,7 @@ class ParserTest < Minitest::Test
|
|
43
43
|
expected = <<~EOF
|
44
44
|
one
|
45
45
|
EOF
|
46
|
-
parsed =
|
46
|
+
parsed = parse_contents(contents)
|
47
47
|
actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
|
48
48
|
assert_equal expected, actual
|
49
49
|
end
|
@@ -69,7 +69,7 @@ class ParserTest < Minitest::Test
|
|
69
69
|
there
|
70
70
|
```
|
71
71
|
EOF
|
72
|
-
parsed =
|
72
|
+
parsed = parse_contents(contents)
|
73
73
|
actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
|
74
74
|
assert_equal expected, actual
|
75
75
|
end
|
@@ -93,7 +93,7 @@ class ParserTest < Minitest::Test
|
|
93
93
|
Hello
|
94
94
|
there
|
95
95
|
EOF
|
96
|
-
parsed =
|
96
|
+
parsed = parse_contents(contents)
|
97
97
|
actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
|
98
98
|
assert_equal expected, actual
|
99
99
|
end
|
@@ -117,7 +117,7 @@ class ParserTest < Minitest::Test
|
|
117
117
|
Hello
|
118
118
|
there
|
119
119
|
EOF
|
120
|
-
parsed =
|
120
|
+
parsed = parse_contents(contents)
|
121
121
|
actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
|
122
122
|
assert_equal expected, actual
|
123
123
|
end
|
@@ -141,7 +141,7 @@ class ParserTest < Minitest::Test
|
|
141
141
|
Hello
|
142
142
|
there
|
143
143
|
EOF
|
144
|
-
parsed =
|
144
|
+
parsed = parse_contents(contents)
|
145
145
|
actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
|
146
146
|
assert_equal expected, actual
|
147
147
|
end
|
@@ -162,7 +162,7 @@ class ParserTest < Minitest::Test
|
|
162
162
|
expected = <<~EOF
|
163
163
|
Hello there
|
164
164
|
EOF
|
165
|
-
parsed =
|
165
|
+
parsed = parse_contents(contents)
|
166
166
|
actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
|
167
167
|
assert_equal expected, actual
|
168
168
|
end
|
@@ -185,7 +185,7 @@ class ParserTest < Minitest::Test
|
|
185
185
|
Hello there
|
186
186
|
```
|
187
187
|
EOF
|
188
|
-
parsed =
|
188
|
+
parsed = parse_contents(contents)
|
189
189
|
actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
|
190
190
|
assert_equal expected, actual
|
191
191
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class IntegrationRequireTest < Minitest::Test
|
4
|
+
def test_require_embeds_results
|
5
|
+
Dir.mktmpdir do |dir|
|
6
|
+
Dir.chdir(dir) do
|
7
|
+
dir = Pathname(dir)
|
8
|
+
source_path = dir.join("RUNDOC.md")
|
9
|
+
source_path.write <<~EOF
|
10
|
+
```
|
11
|
+
:::>> rundoc.require "./day_one/rundoc.md"
|
12
|
+
```
|
13
|
+
EOF
|
14
|
+
|
15
|
+
dir.join("day_one").tap(&:mkpath).join("rundoc.md").write <<~EOF
|
16
|
+
```
|
17
|
+
:::-> print.text Hello World!
|
18
|
+
```
|
19
|
+
EOF
|
20
|
+
|
21
|
+
parsed = parse_contents(
|
22
|
+
source_path.read,
|
23
|
+
source_path: source_path
|
24
|
+
)
|
25
|
+
actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
|
26
|
+
assert_equal "Hello World!", actual.strip
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_require_runs_code_but_embeds_nothing_if_hidden
|
32
|
+
Dir.mktmpdir do |dir|
|
33
|
+
Dir.chdir(dir) do
|
34
|
+
dir = Pathname(dir)
|
35
|
+
source_path = dir.join("RUNDOC.md")
|
36
|
+
source_path.write <<~EOF
|
37
|
+
```
|
38
|
+
:::-- rundoc.require "./day_one/rundoc.md"
|
39
|
+
```
|
40
|
+
EOF
|
41
|
+
|
42
|
+
dir.join("day_one").tap(&:mkpath).join("rundoc.md").write <<~EOF
|
43
|
+
```
|
44
|
+
:::-> print.text Hello World!
|
45
|
+
:::>> $ echo "echo hello world" > foo.txt
|
46
|
+
```
|
47
|
+
EOF
|
48
|
+
|
49
|
+
parsed = parse_contents(
|
50
|
+
source_path.read,
|
51
|
+
source_path: source_path
|
52
|
+
)
|
53
|
+
actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
|
54
|
+
# Command was run
|
55
|
+
assert dir.join("foo.txt").exist?
|
56
|
+
assert "echo hello world", dir.join("foo.txt").read.strip
|
57
|
+
|
58
|
+
# But nothing was embedded
|
59
|
+
assert_equal "", actual.strip
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class IntegrationWebsiteTest < Minitest::Test
|
4
|
+
def test_screenshot_command
|
5
|
+
contents = <<~RUBY
|
6
|
+
```
|
7
|
+
:::>> website.visit(name: "example", url: "http://example.com")
|
8
|
+
:::>> website.screenshot(name: "example")
|
9
|
+
```
|
10
|
+
RUBY
|
11
|
+
|
12
|
+
Dir.mktmpdir do |dir|
|
13
|
+
Dir.chdir(dir) do
|
14
|
+
env = {}
|
15
|
+
env[:before] = []
|
16
|
+
|
17
|
+
screenshots_dirname = "screenshots"
|
18
|
+
screenshots_dir = Pathname(dir).join(screenshots_dirname)
|
19
|
+
screenshot_1_path = screenshots_dir.join("screenshot_1.png")
|
20
|
+
|
21
|
+
parsed = parse_contents(
|
22
|
+
contents,
|
23
|
+
output_dir: screenshots_dir.parent,
|
24
|
+
screenshots_dirname: screenshots_dirname
|
25
|
+
)
|
26
|
+
actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
|
27
|
+
|
28
|
+
expected = "![Screenshot of http://example.com/](screenshots/screenshot_1.png)"
|
29
|
+
assert_equal expected, actual.strip
|
30
|
+
|
31
|
+
assert screenshot_1_path.exist?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class CliArgumentParserTest < Minitest::Test
|
4
|
+
def test_help
|
5
|
+
io = StringIO.new
|
6
|
+
exit_obj = FakeExit.new
|
7
|
+
parser = Rundoc::CLIArgumentParser.new(
|
8
|
+
io: io,
|
9
|
+
argv: ["--help"],
|
10
|
+
env: {},
|
11
|
+
exit_obj: exit_obj
|
12
|
+
)
|
13
|
+
parser.call
|
14
|
+
output = io.string
|
15
|
+
|
16
|
+
assert output.include?("Prints this help output")
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_help_no_flag
|
20
|
+
io = StringIO.new
|
21
|
+
exit_obj = FakeExit.new
|
22
|
+
parser = Rundoc::CLIArgumentParser.new(
|
23
|
+
io: io,
|
24
|
+
argv: ["help"],
|
25
|
+
env: {},
|
26
|
+
exit_obj: exit_obj
|
27
|
+
)
|
28
|
+
parser.call
|
29
|
+
output = io.string
|
30
|
+
|
31
|
+
assert output.include?("Prints this help output")
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_no_such_file
|
35
|
+
io = StringIO.new
|
36
|
+
exit_obj = FakeExit.new
|
37
|
+
parser = Rundoc::CLIArgumentParser.new(
|
38
|
+
io: io,
|
39
|
+
argv: ["fileDoesNotExist.txt"],
|
40
|
+
env: {},
|
41
|
+
exit_obj: exit_obj
|
42
|
+
)
|
43
|
+
parser.call
|
44
|
+
output = io.string
|
45
|
+
|
46
|
+
assert exit_obj.value == 1
|
47
|
+
assert_includes output, "No such file"
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_dir_not_file
|
51
|
+
Dir.mktmpdir do |dir|
|
52
|
+
io = StringIO.new
|
53
|
+
exit_obj = FakeExit.new
|
54
|
+
parser = Rundoc::CLIArgumentParser.new(
|
55
|
+
io: io,
|
56
|
+
argv: [dir],
|
57
|
+
env: {},
|
58
|
+
exit_obj: exit_obj
|
59
|
+
)
|
60
|
+
parser.call
|
61
|
+
output = io.string
|
62
|
+
|
63
|
+
assert exit_obj.value == 1
|
64
|
+
assert_includes output, "Path is not a file"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_valid_inputs
|
69
|
+
Dir.mktmpdir do |dir|
|
70
|
+
Dir.chdir(dir) do
|
71
|
+
tmp = Pathname(dir)
|
72
|
+
rundoc = tmp.join("rundoc.md")
|
73
|
+
rundoc.write("")
|
74
|
+
|
75
|
+
io = StringIO.new
|
76
|
+
exit_obj = FakeExit.new
|
77
|
+
parser = Rundoc::CLIArgumentParser.new(
|
78
|
+
io: io,
|
79
|
+
argv: [
|
80
|
+
rundoc,
|
81
|
+
"--on-success-dir=./success",
|
82
|
+
"--on-failure-dir=./failure",
|
83
|
+
"--dotenv-path=./lol/.env",
|
84
|
+
"--screenshots-dirname=pics",
|
85
|
+
"--output-filename=OUTPUT.md",
|
86
|
+
"--force"
|
87
|
+
],
|
88
|
+
env: {},
|
89
|
+
exit_obj: exit_obj
|
90
|
+
)
|
91
|
+
parser.call
|
92
|
+
output = io.string
|
93
|
+
|
94
|
+
assert !exit_obj.called?
|
95
|
+
assert output.empty?
|
96
|
+
|
97
|
+
assert_equal "./success", parser.options[:on_success_dir]
|
98
|
+
assert_equal "./failure", parser.options[:on_failure_dir]
|
99
|
+
assert_equal "./lol/.env", parser.options[:dotenv_path]
|
100
|
+
assert_equal "pics", parser.options[:screenshots_dirname]
|
101
|
+
assert_equal "OUTPUT.md", parser.options[:output_filename]
|
102
|
+
assert_equal true, parser.options[:force]
|
103
|
+
|
104
|
+
expected = [
|
105
|
+
:on_success_dir,
|
106
|
+
:on_failure_dir,
|
107
|
+
:dotenv_path,
|
108
|
+
:screenshots_dirname,
|
109
|
+
:output_filename,
|
110
|
+
:force,
|
111
|
+
:io,
|
112
|
+
:source_path
|
113
|
+
]
|
114
|
+
assert_equal expected.sort, parser.options.keys.sort
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -15,7 +15,11 @@ class CodeSectionTest < Minitest::Test
|
|
15
15
|
Dir.mktmpdir do |dir|
|
16
16
|
Dir.chdir(dir) do
|
17
17
|
match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
|
18
|
-
result = Rundoc::CodeSection.new(
|
18
|
+
result = Rundoc::CodeSection.new(
|
19
|
+
match,
|
20
|
+
keyword: ":::",
|
21
|
+
context: default_context
|
22
|
+
).render
|
19
23
|
assert_equal "", result
|
20
24
|
end
|
21
25
|
end
|
@@ -30,7 +34,11 @@ class CodeSectionTest < Minitest::Test
|
|
30
34
|
RUBY
|
31
35
|
|
32
36
|
match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
|
33
|
-
result = Rundoc::CodeSection.new(
|
37
|
+
result = Rundoc::CodeSection.new(
|
38
|
+
match,
|
39
|
+
keyword: ":::",
|
40
|
+
context: default_context
|
41
|
+
).render
|
34
42
|
assert_equal contents, result.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "\n")
|
35
43
|
end
|
36
44
|
|
@@ -42,7 +50,11 @@ class CodeSectionTest < Minitest::Test
|
|
42
50
|
RUBY
|
43
51
|
|
44
52
|
match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
|
45
|
-
code_section = Rundoc::CodeSection.new(
|
53
|
+
code_section = Rundoc::CodeSection.new(
|
54
|
+
match,
|
55
|
+
keyword: ":::",
|
56
|
+
context: default_context
|
57
|
+
)
|
46
58
|
code_section.render
|
47
59
|
|
48
60
|
code_command = code_section.commands.first
|
@@ -56,7 +68,11 @@ class CodeSectionTest < Minitest::Test
|
|
56
68
|
RUBY
|
57
69
|
|
58
70
|
match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
|
59
|
-
code_section = Rundoc::CodeSection.new(
|
71
|
+
code_section = Rundoc::CodeSection.new(
|
72
|
+
match,
|
73
|
+
keyword: ":::",
|
74
|
+
context: default_context
|
75
|
+
)
|
60
76
|
code_section.render
|
61
77
|
|
62
78
|
code_command = code_section.commands.first
|
@@ -72,7 +88,11 @@ class CodeSectionTest < Minitest::Test
|
|
72
88
|
RUBY
|
73
89
|
|
74
90
|
match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
|
75
|
-
code_section = Rundoc::CodeSection.new(
|
91
|
+
code_section = Rundoc::CodeSection.new(
|
92
|
+
match,
|
93
|
+
keyword: ":::",
|
94
|
+
context: default_context
|
95
|
+
)
|
76
96
|
code_section.render
|
77
97
|
|
78
98
|
puts code_section.commands.inspect
|
@@ -89,7 +109,11 @@ class CodeSectionTest < Minitest::Test
|
|
89
109
|
RUBY
|
90
110
|
|
91
111
|
match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
|
92
|
-
code_section = Rundoc::CodeSection.new(
|
112
|
+
code_section = Rundoc::CodeSection.new(
|
113
|
+
match,
|
114
|
+
keyword: ":::",
|
115
|
+
context: default_context
|
116
|
+
)
|
93
117
|
code_section.render
|
94
118
|
|
95
119
|
code_command = code_section.commands.first
|
@@ -103,7 +127,11 @@ class CodeSectionTest < Minitest::Test
|
|
103
127
|
RUBY
|
104
128
|
|
105
129
|
match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
|
106
|
-
code_section = Rundoc::CodeSection.new(
|
130
|
+
code_section = Rundoc::CodeSection.new(
|
131
|
+
match,
|
132
|
+
keyword: ":::",
|
133
|
+
context: default_context
|
134
|
+
)
|
107
135
|
code_section.render
|
108
136
|
|
109
137
|
code_command = code_section.commands.first
|
@@ -119,7 +147,11 @@ class CodeSectionTest < Minitest::Test
|
|
119
147
|
RUBY
|
120
148
|
|
121
149
|
match = contents.match(Rundoc::Parser::CODEBLOCK_REGEX)
|
122
|
-
code_section = Rundoc::CodeSection.new(
|
150
|
+
code_section = Rundoc::CodeSection.new(
|
151
|
+
match,
|
152
|
+
keyword: ":::",
|
153
|
+
context: default_context
|
154
|
+
)
|
123
155
|
code_section.render
|
124
156
|
|
125
157
|
code_command = code_section.commands.first
|