diff --git a/behat-steps.md b/behat-steps.md
new file mode 100644
index 000000000..d75b776c0
--- /dev/null
+++ b/behat-steps.md
@@ -0,0 +1,172 @@
+# Behat Steps
+
+WP-CLI makes use of a Behat-based testing framework and provides a set of custom step definitions to write feature tests.
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+***
+## Given
+
+
+
+
+## When
+
+
+
+
+## Then
+
+
+
+
diff --git a/behat-steps/given-a-custom-wp-content-directory.md b/behat-steps/given-a-custom-wp-content-directory.md
new file mode 100644
index 000000000..a5823495c
--- /dev/null
+++ b/behat-steps/given-a-custom-wp-content-directory.md
@@ -0,0 +1,105 @@
+# Given a custom wp-content directory
+
+Configure a custom `wp-content` directory.
+
+
+***
+
+## Usage
+
+Defines the `WP_CONTENT_DIR`, `WP_PLUGIN_DIR`, and `WPMU_PLUGIN_DIR` constants.
+
+```
+Scenario: My example scenario
+ Given a WP install
+ And a custom wp-content directory
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-a-database.md b/behat-steps/given-a-database.md
new file mode 100644
index 000000000..198886e67
--- /dev/null
+++ b/behat-steps/given-a-database.md
@@ -0,0 +1,105 @@
+# Given a database
+
+Creates an empty database.
+
+
+***
+
+## Usage
+
+Has no effect when tests run with SQLite.
+
+```
+Scenario: My example scenario
+ Given a database
+ ...
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-a-dependency-on-current-wp-cli.md b/behat-steps/given-a-dependency-on-current-wp-cli.md
new file mode 100644
index 000000000..ce93f71a7
--- /dev/null
+++ b/behat-steps/given-a-dependency-on-current-wp-cli.md
@@ -0,0 +1,103 @@
+# Given a dependency on current wp-cli
+
+Add `wp-cli/wp-cli` as a Composer dependency.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation with Composer
+ And a dependency on current wp-cli
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-a-downloaded-phar-with-the-same-version-version.md b/behat-steps/given-a-downloaded-phar-with-the-same-version-version.md
new file mode 100644
index 000000000..04342e5c0
--- /dev/null
+++ b/behat-steps/given-a-downloaded-phar-with-the-same-version-version.md
@@ -0,0 +1,107 @@
+# Given /^a downloaded Phar with (?:the same version|version "([^"]+)")$/
+
+Download a specific WP-CLI Phar version from GitHub.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given an empty directory
+ And a downloaded Phar with version "2.11.0"
+
+Scenario: My other scenario
+ Given an empty directory
+ And a downloaded Phar with the same version
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-a-misconfigured-wp-content-dir-constant-directory.md b/behat-steps/given-a-misconfigured-wp-content-dir-constant-directory.md
new file mode 100644
index 000000000..6f6caaff7
--- /dev/null
+++ b/behat-steps/given-a-misconfigured-wp-content-dir-constant-directory.md
@@ -0,0 +1,103 @@
+# Given a misconfigured WP_CONTENT_DIR constant directory
+
+Modify wp-config.php to set `WP_CONTENT_DIR` to an empty string.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP install
+ And a misconfigured WP_CONTENT_DIR constant directory
+ ```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-a-new-phar-with-the-same-version-version.md b/behat-steps/given-a-new-phar-with-the-same-version-version.md
new file mode 100644
index 000000000..50607dc14
--- /dev/null
+++ b/behat-steps/given-a-new-phar-with-the-same-version-version.md
@@ -0,0 +1,103 @@
+# Given /^a new Phar with (?:the same version|version "([^"]+)")$/
+
+Build a new WP-CLI Phar file with a given version.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given an empty directory
+ And a new Phar with version "2.11.0"
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-a-php-built-in-web-server-to-serve-subdir.md b/behat-steps/given-a-php-built-in-web-server-to-serve-subdir.md
new file mode 100644
index 000000000..22feca3af
--- /dev/null
+++ b/behat-steps/given-a-php-built-in-web-server-to-serve-subdir.md
@@ -0,0 +1,103 @@
+# Given a PHP built-in web server to serve :subdir
+
+Start a PHP built-in web server in the given subdirectory.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation
+ And a PHP built-in web server to serve 'WordPress'
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-a-php-built-in-web-server.md b/behat-steps/given-a-php-built-in-web-server.md
new file mode 100644
index 000000000..c6722bafb
--- /dev/null
+++ b/behat-steps/given-a-php-built-in-web-server.md
@@ -0,0 +1,103 @@
+# Given a PHP built-in web server
+
+Start a PHP built-in web server in the current directory.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation
+ And a PHP built-in web server
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-a-wp-installation-in-subdir.md b/behat-steps/given-a-wp-installation-in-subdir.md
new file mode 100644
index 000000000..cf09cc6e0
--- /dev/null
+++ b/behat-steps/given-a-wp-installation-in-subdir.md
@@ -0,0 +1,107 @@
+# Given a WP install(ation) in :subdir
+
+Installs WordPress in a given directory.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation in 'foo'
+ ...
+
+Scenario: My other scenario
+ Given a WP install in 'bar'
+ ...
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-a-wp-installation-with-composer-and-a-custom-vendor-directory-vendor-directory.md b/behat-steps/given-a-wp-installation-with-composer-and-a-custom-vendor-directory-vendor-directory.md
new file mode 100644
index 000000000..72f49b505
--- /dev/null
+++ b/behat-steps/given-a-wp-installation-with-composer-and-a-custom-vendor-directory-vendor-directory.md
@@ -0,0 +1,107 @@
+# Given a WP install(ation) with Composer and a custom vendor directory :vendor_directory
+
+Installs WordPress with Composer and a custom vendor directory.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation with Composer and a custom vendor directory 'vendor-custom'
+ ...
+
+Scenario: My other scenario
+ Given a WP install with Composer with Composer and a custom vendor directory 'vendor-custom'
+ ...
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-a-wp-installation-with-composer.md b/behat-steps/given-a-wp-installation-with-composer.md
new file mode 100644
index 000000000..79bd32300
--- /dev/null
+++ b/behat-steps/given-a-wp-installation-with-composer.md
@@ -0,0 +1,107 @@
+# Given a WP install(ation) with Composer
+
+Installs WordPress with Composer.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation with Composer
+ ...
+
+Scenario: My other scenario
+ Given a WP install with Composer
+ ...
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-a-wp-installation.md b/behat-steps/given-a-wp-installation.md
new file mode 100644
index 000000000..16a0f08d8
--- /dev/null
+++ b/behat-steps/given-a-wp-installation.md
@@ -0,0 +1,107 @@
+# Given a WP install(ation)
+
+Installs WordPress.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation
+ ...
+
+Scenario: My other scenario
+ Given a WP install
+ ...
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-a-wp-multisite-subdirectory-subdomaininstall-installation.md b/behat-steps/given-a-wp-multisite-subdirectory-subdomaininstall-installation.md
new file mode 100644
index 000000000..501dbd9e2
--- /dev/null
+++ b/behat-steps/given-a-wp-multisite-subdirectory-subdomaininstall-installation.md
@@ -0,0 +1,109 @@
+# Given /^a WP multisite (subdirectory|subdomain)?\s?(install|installation)$/
+
+Installs WordPress Multisite.
+
+
+***
+
+## Usage
+
+Supports either subdirectory or subdomain installation.
+
+```
+Scenario: My example scenario
+ Given a WP multisite subdomain installation
+ ...
+
+Scenario: My other scenario
+ Given a WP subdirectory install
+ ...
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-an-empty-cache.md b/behat-steps/given-an-empty-cache.md
new file mode 100644
index 000000000..92878e3c0
--- /dev/null
+++ b/behat-steps/given-an-empty-cache.md
@@ -0,0 +1,103 @@
+# Given an empty cache
+
+Clears the WP-CLI cache directory.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given an empty cache
+ ...
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-an-empty-directory.md b/behat-steps/given-an-empty-directory.md
new file mode 100644
index 000000000..44fffa6a6
--- /dev/null
+++ b/behat-steps/given-an-empty-directory.md
@@ -0,0 +1,103 @@
+# Given an empty directory
+
+Creates an empty directory.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given an empty directory
+ ...
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-an-empty-non-existent-directory.md b/behat-steps/given-an-empty-non-existent-directory.md
new file mode 100644
index 000000000..971ac72a5
--- /dev/null
+++ b/behat-steps/given-an-empty-non-existent-directory.md
@@ -0,0 +1,104 @@
+# Given /^an? (empty|non-existent) ([^\s]+) directory$/
+
+Creates or deletes a specific directory.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given an empty foo-plugin directory
+ And a non-existent bar-plugin directory
+ ...
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-an-file-cache-file.md b/behat-steps/given-an-file-cache-file.md
new file mode 100644
index 000000000..383c99403
--- /dev/null
+++ b/behat-steps/given-an-file-cache-file.md
@@ -0,0 +1,110 @@
+# Given /^an? ([^\s]+) (file|cache file):$/
+
+Creates a file with the given contents.
+
+
+***
+
+## Usage
+
+The file can be created either in the current working directory
+or in the cache directory.
+
+```
+Scenario: My example scenario
+ Given a wp-cli.yml file:
+ """
+ @foo:
+ path: foo
+ user: admin
+ """
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-download.md b/behat-steps/given-download.md
new file mode 100644
index 000000000..21d3d8bae
--- /dev/null
+++ b/behat-steps/given-download.md
@@ -0,0 +1,105 @@
+# Given download:
+
+Download multiple files into the given destinations.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given download:
+ | path | url |
+ | {CACHE_DIR}/foo.jpg | https://example.com/foo.jpg |
+ | {CACHE_DIR}/bar.png | https://example.com/another-image.png |
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-replaced-with-in-the-file.md b/behat-steps/given-replaced-with-in-the-file.md
new file mode 100644
index 000000000..fcdc55a22
--- /dev/null
+++ b/behat-steps/given-replaced-with-in-the-file.md
@@ -0,0 +1,103 @@
+# Given /^"([^"]+)" replaced with "([^"]+)" in the ([^\s]+) file$/
+
+Search and replace a string in a file using regex.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given "Foo" replaced with "Bar" in the readme.html file
+ ...
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-save-stdout-stderr-as.md b/behat-steps/given-save-stdout-stderr-as.md
new file mode 100644
index 000000000..f009da45e
--- /dev/null
+++ b/behat-steps/given-save-stdout-stderr-as.md
@@ -0,0 +1,107 @@
+# Given /^save (STDOUT|STDERR) ([\'].+[^\'])?\s?as \{(\w+)\}$/
+
+Store STDOUT or STDERR contents in a variable.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ When I run `wp package path`
+ Then save STDOUT as {PACKAGE_PATH}
+
+Scenario: My other scenario
+ When I run `wp core download`
+ Then save STDOUT 'Downloading WordPress ([\d\.]+)' as {VERSION}
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-save-the-file-as.md b/behat-steps/given-save-the-file-as.md
new file mode 100644
index 000000000..59e0f1bb3
--- /dev/null
+++ b/behat-steps/given-save-the-file-as.md
@@ -0,0 +1,103 @@
+# Given /^save the (.+) file ([\'].+[^\'])?as \{(\w+)\}$/
+
+Stores the contents of the given file in a variable.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation with Composer
+ And save the {RUN_DIR}/composer.json file as {COMPOSER_JSON}
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-that-http-requests-to-will-respond-with.md b/behat-steps/given-that-http-requests-to-will-respond-with.md
new file mode 100644
index 000000000..16bca6de2
--- /dev/null
+++ b/behat-steps/given-that-http-requests-to-will-respond-with.md
@@ -0,0 +1,108 @@
+# Given /^that HTTP requests to (.*?) will respond with:$/
+
+Mock HTTP requests to a given URL.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given that HTTP requests to https://api.github.com/repos/wp-cli/wp-cli/releases?per_page=100 will respond with:
+ """
+ HTTP/1.1 200
+ Content-Type: application/json
+
+ { "foo": "bar" }
+ """
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-these-installed-and-active-plugins.md b/behat-steps/given-these-installed-and-active-plugins.md
new file mode 100644
index 000000000..735c64dc3
--- /dev/null
+++ b/behat-steps/given-these-installed-and-active-plugins.md
@@ -0,0 +1,107 @@
+# Given these installed and active plugins:
+
+Installs and activates one or more plugins.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation
+ And these installed and active plugins:
+ """
+ akismet
+ wordpress-importer
+ """
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-wp-config-php.md b/behat-steps/given-wp-config-php.md
new file mode 100644
index 000000000..068d06ced
--- /dev/null
+++ b/behat-steps/given-wp-config-php.md
@@ -0,0 +1,104 @@
+# Given wp-config.php
+
+Create a wp-config.php file using `wp config create`.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given an empty directory
+ And WP files
+ And wp-config.php
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/given-wp-files.md b/behat-steps/given-wp-files.md
new file mode 100644
index 000000000..ed458d181
--- /dev/null
+++ b/behat-steps/given-wp-files.md
@@ -0,0 +1,103 @@
+# Given WP files
+
+Download WordPress files without installing.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given an empty directory
+ And WP files
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-an-email-should-be-sent-not-be-sent.md b/behat-steps/then-an-email-should-be-sent-not-be-sent.md
new file mode 100644
index 000000000..ca0e2e7fa
--- /dev/null
+++ b/behat-steps/then-an-email-should-be-sent-not-be-sent.md
@@ -0,0 +1,82 @@
+# Then /^an email should (be sent|not be sent)$/
+
+Expect an email to be sent (or not).
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ When I run `wp user reset-password 1`
+ Then an email should be sent
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-stdout-should-be-a-json-array-containing.md b/behat-steps/then-stdout-should-be-a-json-array-containing.md
new file mode 100644
index 000000000..2b8074c7b
--- /dev/null
+++ b/behat-steps/then-stdout-should-be-a-json-array-containing.md
@@ -0,0 +1,87 @@
+# Then /^STDOUT should be a JSON array containing:$/
+
+Expect valid JSON array output in STDOUT.
+
+
+***
+
+## Usage
+
+Errors when some items are missing from the expected array.
+
+```
+Scenario: My example scenario
+ When I run `wp plugin list --field=name --format=json`
+ Then STDOUT should be a JSON array containing:
+ """
+ ["akismet", "hello-dolly"]
+ """
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-stdout-should-be-a-table-containing-rows.md b/behat-steps/then-stdout-should-be-a-table-containing-rows.md
new file mode 100644
index 000000000..d76ccf6c5
--- /dev/null
+++ b/behat-steps/then-stdout-should-be-a-table-containing-rows.md
@@ -0,0 +1,86 @@
+# Then /^STDOUT should be a table containing rows:$/
+
+Expect STDOUT to be a table containing the given rows.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation
+ When I run `wp config list --fields=name,type`
+ Then STDOUT should be a table containing rows:
+ | name | type |
+ | DB_NAME | constant |
+ | DB_USER | constant |
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-stdout-should-be-csv-containing.md b/behat-steps/then-stdout-should-be-csv-containing.md
new file mode 100644
index 000000000..ed7b88c19
--- /dev/null
+++ b/behat-steps/then-stdout-should-be-csv-containing.md
@@ -0,0 +1,84 @@
+# Then /^STDOUT should be CSV containing:$/
+
+Expect STDOUT to be CSV containing certain values.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ When I run `wp term list post_tag --fields=name,slug --format=csv`
+ Then STDOUT should be CSV containing:
+ | name | slug |
+ | Test term | test |
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-stdout-should-be-json-containing.md b/behat-steps/then-stdout-should-be-json-containing.md
new file mode 100644
index 000000000..0372cabc0
--- /dev/null
+++ b/behat-steps/then-stdout-should-be-json-containing.md
@@ -0,0 +1,87 @@
+# Then /^STDOUT should be JSON containing:$/
+
+Expect valid JSON output in STDOUT.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ When I run `wp post meta get 1 meta-key --format=json`
+ Then STDOUT should be JSON containing:
+ """
+ {
+ "foo": "baz"
+ }
+ """
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-stdout-should-be-yaml-containing.md b/behat-steps/then-stdout-should-be-yaml-containing.md
new file mode 100644
index 000000000..a3b64b44c
--- /dev/null
+++ b/behat-steps/then-stdout-should-be-yaml-containing.md
@@ -0,0 +1,87 @@
+# Then /^STDOUT should be YAML containing:$/
+
+Expect STDOUT to be YAML containig certain content.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ When I run `wp cli alias list`
+ Then STDOUT should be YAML containing:
+ """
+ @all: Run command against every registered alias.
+ @foo:
+ path: {TEST_DIR}/foo
+ """
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-stdout-should-end-with-a-table-containing-rows.md b/behat-steps/then-stdout-should-end-with-a-table-containing-rows.md
new file mode 100644
index 000000000..52321288c
--- /dev/null
+++ b/behat-steps/then-stdout-should-end-with-a-table-containing-rows.md
@@ -0,0 +1,93 @@
+# Then /^STDOUT should end with a table containing rows:$/
+
+Expect STDOUT to end with a table containing the given rows.
+
+
+***
+
+## Usage
+
+Useful when the table is preceded by some other output.
+
+```
+Scenario: My example scenario
+ Given a WP installation
+ When I run `wp search-replace foo bar --report-changed-only`
+ Then STDOUT should contain:
+ """
+ Success: Made 3 replacements.
+ """
+ And STDOUT should end with a table containing rows:
+ | Table | Column | Replacements | Type |
+ | wp_options | option_value | 1 | PHP |
+ | wp_postmeta | meta_value | 1 | SQL |
+ | wp_posts | post_title | 1 | SQL |
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-stdout-stderr-should-be-a-number.md b/behat-steps/then-stdout-stderr-should-be-a-number.md
new file mode 100644
index 000000000..dac462836
--- /dev/null
+++ b/behat-steps/then-stdout-stderr-should-be-a-number.md
@@ -0,0 +1,83 @@
+# Then /^(STDOUT|STDERR) should be a number$/
+
+Expect STDOUT or STDERR to be a numeric value.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation
+ When I run `wp db size --size_format=b`
+ Then STDOUT should be a number
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-stdout-stderr-should-be-a-version-string-w.md b/behat-steps/then-stdout-stderr-should-be-a-version-string-w.md
new file mode 100644
index 000000000..912460841
--- /dev/null
+++ b/behat-steps/then-stdout-stderr-should-be-a-version-string-w.md
@@ -0,0 +1,83 @@
+# Then /^(STDOUT|STDERR) should be a version string (<|<=|>|>=|==|=|!=|<>) ([+\w.{}-]+)$/
+
+Expect STDOUT or STDERR to be a version string comparing to the given version.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP install
+ When I run `wp core version
+ Then STDOUT should be a version string >= 6.8
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-stdout-stderr-should-be-empty.md b/behat-steps/then-stdout-stderr-should-be-empty.md
new file mode 100644
index 000000000..80a2da4c0
--- /dev/null
+++ b/behat-steps/then-stdout-stderr-should-be-empty.md
@@ -0,0 +1,83 @@
+# Then /^(STDOUT|STDERR) should be empty$/
+
+Expect STDOUT or STDERR to be empty.
+
+
+***
+
+## Usage
+
+```
+Scenario: My other scenario
+ Given a WP install
+ When I run `wp plugin install akismet`
+ Then STDERR should be empty
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-stdout-stderr-should-not-be-a-number.md b/behat-steps/then-stdout-stderr-should-not-be-a-number.md
new file mode 100644
index 000000000..abdd1d914
--- /dev/null
+++ b/behat-steps/then-stdout-stderr-should-not-be-a-number.md
@@ -0,0 +1,83 @@
+# Then /^(STDOUT|STDERR) should not be a number$/
+
+Expect STDOUT or STDERR to not be a numeric value.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation
+ When I run `wp post list --format=json`
+ Then STDOUT should not be a number
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-stdout-stderr-should-not-be-empty.md b/behat-steps/then-stdout-stderr-should-not-be-empty.md
new file mode 100644
index 000000000..a205dc37a
--- /dev/null
+++ b/behat-steps/then-stdout-stderr-should-not-be-empty.md
@@ -0,0 +1,82 @@
+# Then /^(STDOUT|STDERR) should not be empty$/
+
+Expect STDOUT or STDERR not to be empty.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ When I run `wp user create examplejane jane@example.com`
+ Then STDOUT should not be empty
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-stdout-stderr-should-not-match.md b/behat-steps/then-stdout-stderr-should-not-match.md
new file mode 100644
index 000000000..7e2fdaee8
--- /dev/null
+++ b/behat-steps/then-stdout-stderr-should-not-match.md
@@ -0,0 +1,82 @@
+# Then /^(STDOUT|STDERR) should( not)? match (((\/.+\/)|(#.+#))([a-z]+)?)$/
+
+Match STDOUT or STDERR against a regex.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ When I run `wp dist-archive wp-content/plugins/hello-world`
+ Then STDOUT should match /^Success: Created hello-world.0.1.0.zip \(Size: \d+(?:\.\d*)? [a-zA-Z]{1,3}\)$/
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-stdout-stderr-should-strictly-be-contain-not-contain.md b/behat-steps/then-stdout-stderr-should-strictly-be-contain-not-contain.md
new file mode 100644
index 000000000..2383b93da
--- /dev/null
+++ b/behat-steps/then-stdout-stderr-should-strictly-be-contain-not-contain.md
@@ -0,0 +1,92 @@
+# Then /^(STDOUT|STDERR) should( strictly)? (be|contain|not contain):$/
+
+Check the contents of STDOUT or STDERR.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given an empty directory
+ When I run `wp core is-installed`
+ Then STDOUT should be empty
+
+Scenario: My other scenario
+ Given a WP install
+ When I run `wp plugin install akismet`
+ Then STDOUT should contain:
+ """
+ Plugin installed successfully.
+ """
+ And STDERR should be empty
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-the-contents-of-the-file-should-not-match.md b/behat-steps/then-the-contents-of-the-file-should-not-match.md
new file mode 100644
index 000000000..8ff8e10e7
--- /dev/null
+++ b/behat-steps/then-the-contents-of-the-file-should-not-match.md
@@ -0,0 +1,82 @@
+# Then /^the contents of the (.+) file should( not)? match (((\/.+\/)|(#.+#))([a-z]+)?)$/
+
+Match file contents against a regex.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ When I run `wp scaffold plugin hello-world`
+ Then the contents of the wp-content/plugins/hello-world/languages/hello-world.pot file should match /X-Generator:\s/
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-the-file-directory-should-strictly-exist-not-exist-be-contain-not-contain.md b/behat-steps/then-the-file-directory-should-strictly-exist-not-exist-be-contain-not-contain.md
new file mode 100644
index 000000000..e5f12b7be
--- /dev/null
+++ b/behat-steps/then-the-file-directory-should-strictly-exist-not-exist-be-contain-not-contain.md
@@ -0,0 +1,92 @@
+# Then /^the (.+) (file|directory) should( strictly)? (exist|not exist|be:|contain:|not contain:)$/
+
+Expect a certain file or directory to (not) exist or (not) contain certain contents.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ When I run `wp core download`
+ Then the wp-settings.php file should exist
+ And the wp-content directory should exist
+ And the {RUN_DIR} directory should contain:
+ """
+ index.php
+ license.txt
+ """
+ And the wp-config.php file should contain:
+ """
+ That's all, stop editing! Happy publishing.
+ """
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-the-http-status-code-should-be-code.md b/behat-steps/then-the-http-status-code-should-be-code.md
new file mode 100644
index 000000000..6f007b963
--- /dev/null
+++ b/behat-steps/then-the-http-status-code-should-be-code.md
@@ -0,0 +1,83 @@
+# Then the HTTP status code should be :code
+
+Expect the HTTP status code for visiting `http://localhost:8080`.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation with Composer
+ And a PHP built-in web server to serve 'WordPress'
+ Then the HTTP status code should be 200
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/then-the-return-code-should-not-be.md b/behat-steps/then-the-return-code-should-not-be.md
new file mode 100644
index 000000000..6fdfa4bc1
--- /dev/null
+++ b/behat-steps/then-the-return-code-should-not-be.md
@@ -0,0 +1,83 @@
+# Then /^the return code should( not)? be (\d+)$/
+
+Expect a specific exit code of the previous command.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP installation
+ When I try `wp plugin install`
+ Then the return code should be 1
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/when-i-launch-in-the-background.md b/behat-steps/when-i-launch-in-the-background.md
new file mode 100644
index 000000000..feac22c32
--- /dev/null
+++ b/behat-steps/when-i-launch-in-the-background.md
@@ -0,0 +1,41 @@
+# When /^I launch in the background `([^`]+)`$/
+
+Launch a given command in the background.
+
+
+***
+
+## Usage
+
+```
+Scenario: My example scenario
+ Given a WP install
+ And I launch in the background `wp server --host=localhost --port=8181`
+ ...
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/when-i-run-try-from.md b/behat-steps/when-i-run-try-from.md
new file mode 100644
index 000000000..9fe5657f2
--- /dev/null
+++ b/behat-steps/when-i-run-try-from.md
@@ -0,0 +1,45 @@
+# When /^I (run|try) `([^`]+)` from '([^\s]+)'$/
+
+Run or try a given command in a subdirectory.
+
+
+***
+
+## Usage
+
+`run` expects an exit code 0, whereas `try` allows for non-zero exit codes.
+
+```
+Scenario: My example scenario
+ When I run `wp core is-installed`
+ Then STDOUT should be empty
+
+ When I run `wp core is-installed` from 'foo/wp-content'
+ Then STDOUT should be empty
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/when-i-run-try-the-previous-command-again.md b/behat-steps/when-i-run-try-the-previous-command-again.md
new file mode 100644
index 000000000..b840cef6c
--- /dev/null
+++ b/behat-steps/when-i-run-try-the-previous-command-again.md
@@ -0,0 +1,51 @@
+# When /^I (run|try) the previous command again$/
+
+Run or try the previous command again.
+
+
+***
+
+## Usage
+
+`run` expects an exit code 0, whereas `try` allows for non-zero exit codes.
+
+```
+Scenario: My example scenario
+ When I run `wp site option update admin_user_id 1`
+ Then STDOUT should contain:
+ """
+ Success: Updated 'admin_user_id' site option.
+ """
+
+ When I run the previous command again
+ Then STDOUT should contain:
+ """
+ Success: Value passed for 'admin_user_id' site option is unchanged.
+ """
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/behat-steps/when-i-run-try.md b/behat-steps/when-i-run-try.md
new file mode 100644
index 000000000..d6316d4ab
--- /dev/null
+++ b/behat-steps/when-i-run-try.md
@@ -0,0 +1,55 @@
+# When /^I (run|try) `([^`]+)`$/
+
+Run or try a given command.
+
+
+***
+
+## Usage
+
+`run` expects an exit code 0, whereas `try` allows for non-zero exit codes.
+
+So if using `run` and the command errors, the step will fail.
+
+```
+Scenario: My example scenario
+ When I run `wp core version`
+ Then STDOUT should contain:
+ """
+ 6.8
+ """
+
+Scenario: My other scenario
+ When I try `wp i18n make-pot foo bar/baz.pot`
+ Then STDERR should contain:
+ """
+ Error: Not a valid source directory.
+ """
+ And the return code should be 1
+```
+
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+
+***
+
+## Related
+
+
+
+
diff --git a/bin/command.php b/bin/command.php
index b93a79544..09a953acb 100644
--- a/bin/command.php
+++ b/bin/command.php
@@ -36,26 +36,42 @@ public function gen_all( $args, $assoc_args ) {
}
self::gen_api_docs();
+ self::gen_behat_docs();
self::gen_commands( $args, $assoc_args );
self::gen_commands_manifest();
self::gen_hb_manifest();
WP_CLI::success( 'Generated all doc pages.' );
}
+ private function prepare_api_slug( $full_name ) {
+ $replacements = [
+ '\\w+' => '',
+ '\\s' => '',
+ '\\d' => '',
+ 'a-z' => '',
+ 's?' => '',
+ '::' => '-',
+ '_' => '-',
+ '\\' => '-',
+ ' ' => '-',
+ '.' => '-',
+ '|' => '-',
+ ];
+ $full_name = strtolower( str_replace( array_keys( $replacements ), array_values( $replacements ), $full_name ) );
+ $full_name = preg_replace( '/[^a-zA-Z0-9-]/', '', $full_name );
+ $full_name = preg_replace( '/-+/', '-', $full_name );
+ $full_name = trim( $full_name, '-' );
+ return $full_name;
+ }
+
/**
* Generates internal API doc pages.
*
* @subcommand gen-api-docs
*/
public function gen_api_docs() {
- $apis = WP_CLI::runcommand(
- 'handbook api-dump',
- [
- 'launch' => false,
- 'return' => 'stdout',
- 'parse' => 'json',
- ]
- );
+ $apis = $this->get_internal_apis();
+
$categories = [
'Registration' => [],
'Output' => [],
@@ -65,19 +81,9 @@ public function gen_api_docs() {
'Misc' => [],
];
- $prepare_api_slug = function ( $full_name ) {
- $replacements = [
- '()' => '',
- '::' => '-',
- '_' => '-',
- '\\' => '-',
- ];
- return strtolower( str_replace( array_keys( $replacements ), array_values( $replacements ), $full_name ) );
- };
-
foreach ( $apis as $api ) {
- $api['api_slug'] = $prepare_api_slug( $api['full_name'] );
+ $api['api_slug'] = $this->prepare_api_slug( $api['full_name'] );
if ( ! empty( $api['phpdoc']['parameters']['category'][0][0] )
&& isset( $categories[ $api['phpdoc']['parameters']['category'][0][0] ] ) ) {
@@ -122,8 +128,9 @@ function ( $parameter ) {
unset( $api['related'][ $i ] );
$api['related'] = array_values( $api['related'] );
$api['has_related'] = ! empty( $api['related'] );
- $api_doc = self::render( 'internal-api.mustache', $api );
- $path = WP_CLI_HANDBOOK_PATH . "/internal-api/{$api['api_slug']}.md";
+
+ $api_doc = self::render( 'internal-api.mustache', $api );
+ $path = WP_CLI_HANDBOOK_PATH . "/internal-api/{$api['api_slug']}.md";
if ( ! is_dir( dirname( $path ) ) ) {
mkdir( dirname( $path ) );
}
@@ -136,6 +143,81 @@ function ( $parameter ) {
WP_CLI::success( 'Generated internal-api/' );
}
+ /**
+ * Generates Behat steps doc pages.
+ *
+ * @subcommand gen-behat-docs
+ */
+ public function gen_behat_docs() {
+ $apis = $this->get_behat_steps();
+
+ $categories = [
+ 'Given' => [],
+ 'When' => [],
+ 'Then' => [],
+ ];
+
+ foreach ( $apis as $api ) {
+
+ $api['api_slug'] = $this->prepare_api_slug( $api['full_name'] );
+
+ if ( isset( $api['phpdoc']['parameters']['Given'] ) ) {
+ $categories['Given'][] = $api;
+ } elseif ( isset( $api['phpdoc']['parameters']['When'] ) ) {
+ $categories['When'][] = $api;
+ } elseif ( isset( $api['phpdoc']['parameters']['Then'] ) ) {
+ $categories['Then'][] = $api;
+ }
+ }
+ $out = << $apis ) {
+ $out .= '## ' . $name . PHP_EOL . PHP_EOL;
+ $out .= self::render( 'behat-steps-list.mustache', [ 'apis' => $apis ] );
+ foreach ( $apis as $i => $api ) {
+ $api['category'] = $name;
+ $api['related'] = $apis;
+ $api['phpdoc']['parameters'] = array_map(
+ function ( $parameter ) {
+ foreach ( $parameter as $key => $values ) {
+ if ( isset( $values[2] ) ) {
+ $values[2] = str_replace( array( PHP_EOL ), array( '
' ), $values[2] );
+ $parameter[ $key ] = $values;
+ }
+ }
+ return $parameter;
+ },
+ $api['phpdoc']['parameters']
+ );
+ unset( $api['related'][ $i ] );
+ $api['related'] = array_values( $api['related'] );
+ $api['has_related'] = ! empty( $api['related'] );
+
+ $api_doc = self::render( 'behat-steps.mustache', $api );
+ $path = WP_CLI_HANDBOOK_PATH . "/behat-steps/{$api['api_slug']}.md";
+ if ( ! is_dir( dirname( $path ) ) ) {
+ mkdir( dirname( $path ) );
+ }
+ file_put_contents( $path, $api_doc );
+ }
+ $out .= PHP_EOL . PHP_EOL;
+ }
+
+ file_put_contents( WP_CLI_HANDBOOK_PATH . '/behat-steps.md', $out );
+ WP_CLI::success( 'Generated behat-steps/' );
+ }
+
/**
* Generates all command pages.
*
@@ -176,7 +258,7 @@ public function gen_commands( $args, $assoc_args ) {
$verbose = Utils\get_flag_value( $assoc_args, 'verbose', false );
foreach ( $wp['subcommands'] as $cmd ) {
- if ( in_array( $cmd['name'], [ 'website', 'api-dump', 'handbook' ], true ) ) {
+ if ( in_array( $cmd['name'], [ 'website', 'handbook' ], true ) ) {
continue;
}
self::gen_cmd_pages( $cmd, [] /*parent*/, $verbose );
@@ -348,12 +430,7 @@ public function gen_hb_manifest() {
WP_CLI::success( 'Generated bin/handbook-manifest.json' );
}
- /**
- * Dumps internal API PHPDoc to JSON.
- *
- * @subcommand api-dump
- */
- public function api_dump() {
+ private function get_internal_apis() {
$apis = [];
$functions = get_defined_functions();
foreach ( $functions['user'] as $function ) {
@@ -364,11 +441,38 @@ public function api_dump() {
}
$apis[] = self::get_simple_representation( $reflection );
}
+
$classes = get_declared_classes();
foreach ( $classes as $class ) {
if ( false === stripos( $class, 'WP_CLI' ) ) {
continue;
}
+
+ $reflection = new \ReflectionClass( $class );
+ foreach ( $reflection->getMethods() as $method ) {
+ $method_reflection = new \ReflectionMethod( $method->class, $method->name );
+ $phpdoc = $method_reflection->getDocComment();
+ if ( false === stripos( $phpdoc, '@access public' ) ) {
+ continue;
+ }
+ $apis[] = self::get_simple_representation( $method_reflection );
+ }
+ }
+
+ return $apis;
+ }
+
+ private function get_behat_steps() {
+ $apis = [];
+ $classes = [
+ '\WP_CLI\Tests\Context\FeatureContext',
+ ];
+
+ foreach ( $classes as $class ) {
+ if ( false === stripos( $class, 'WP_CLI' ) ) {
+ continue;
+ }
+
$reflection = new \ReflectionClass( $class );
foreach ( $reflection->getMethods() as $method ) {
$method_reflection = new \ReflectionMethod( $method->class, $method->name );
@@ -379,7 +483,8 @@ public function api_dump() {
$apis[] = self::get_simple_representation( $method_reflection );
}
}
- echo json_encode( $apis );
+
+ return $apis;
}
private static function gen_cmd_pages( $cmd, $parent = [], $verbose = false ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.parentFound
@@ -569,7 +674,7 @@ private static function get_simple_representation( $reflection ) {
} else {
$signature = $signature . '()';
}
- $phpdoc = $reflection->getDocComment();
+ $phpdoc = self::parse_docblock( $reflection->getDocComment() );
$type = strtolower( str_replace( 'Reflection', '', get_class( $reflection ) ) );
$class = '';
switch ( $type ) {
@@ -584,8 +689,13 @@ private static function get_simple_representation( $reflection ) {
$full_name = $reflection->getName();
break;
}
+
+ if ( isset( $phpdoc['behat_step'] ) ) {
+ $full_name = $phpdoc['behat_step'];
+ }
+
return [
- 'phpdoc' => self::parse_docblock( $phpdoc ),
+ 'phpdoc' => $phpdoc,
'type' => $type,
'signature' => $signature,
'short_name' => $reflection->getShortName(),
@@ -623,6 +733,11 @@ private static function parse_docblock( $docblock ) {
preg_match( '/@(\w+)/', $info, $matches );
$param_name = $matches[1];
$value = str_replace( "@$param_name ", '', $info );
+
+ if ( in_array( $param_name, [ 'Given', 'Then', 'When' ], true ) ) {
+ $ret['behat_step'] = "$param_name $value";
+ }
+
if ( ! isset( $ret['parameters'][ $param_name ] ) ) {
$ret['parameters'][ $param_name ] = [];
}
diff --git a/bin/handbook-manifest.json b/bin/handbook-manifest.json
index b4c97f5d4..bb2ae01e5 100644
--- a/bin/handbook-manifest.json
+++ b/bin/handbook-manifest.json
@@ -1,4 +1,10 @@
{
+ "behat-steps": {
+ "title": "Behat Steps",
+ "slug": "behat-steps",
+ "markdown_source": "https:\/\/github.com\/wp-cli\/handbook\/blob\/main\/behat-steps.md",
+ "parent": null
+ },
"bug-reports": {
"title": "Bug Reports",
"slug": "bug-reports",
@@ -509,4 +515,4 @@
"markdown_source": "https:\/\/github.com\/wp-cli\/handbook\/blob\/main\/internal-api\/wp-cli-warning.md",
"parent": "internal-api"
}
-}
\ No newline at end of file
+}
diff --git a/bin/templates/behat-steps-list.mustache b/bin/templates/behat-steps-list.mustache
new file mode 100644
index 000000000..f29dd9f9f
--- /dev/null
+++ b/bin/templates/behat-steps-list.mustache
@@ -0,0 +1,9 @@
+
+
+{{#apis}}
+
+- {{full_name}} - {{phpdoc.short_description}}
+
+{{/apis}}
+
+
diff --git a/bin/templates/behat-steps.mustache b/bin/templates/behat-steps.mustache
new file mode 100644
index 000000000..868b4f806
--- /dev/null
+++ b/bin/templates/behat-steps.mustache
@@ -0,0 +1,38 @@
+# {{full_name}}
+
+{{phpdoc.short_description}}
+
+{{#phpdoc.long_description}}
+
+***
+
+## Usage
+
+{{{phpdoc.long_description}}}
+
+{{/phpdoc.long_description}}
+
+*Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.*
+
+{{#has_related}}
+
+***
+
+## Related
+
+
+
+{{/has_related}}
+
+{{#related}}
+
+- {{full_name}} - {{phpdoc.short_description}}
+
+{{/related}}
+
+{{#has_related}}
+
+
+
+{{/has_related}}
+
diff --git a/composer.json b/composer.json
index 596ec3179..3ed63d277 100644
--- a/composer.json
+++ b/composer.json
@@ -14,7 +14,7 @@
},
"require-dev": {
"wp-cli/wp-cli": "^2.11",
- "wp-cli/wp-cli-tests": "^4.2"
+ "wp-cli/wp-cli-tests": "^4.3.14"
},
"minimum-stability": "dev",
"prefer-stable": true,