+```
+
+Get your k6 Cloud token from the [account settings page](https://app.k6.io/account/token).
+
+## How often to run tests
+
+The boring, but true, answer to the question of how often you should run load tests is that "it depends". It depends on a number of parameters. How often is your application changing? Do you need many or few VUs to generate the necessary load? How long is one full cycle of a VU iteration? etc. Testing a full user journey or just a single API endpoint or website page has different implications on the answer as well.
+
+There are three primary factors that will affect what solution is the best for you:
+
+- VU iteration duration
+- Your branching strategy
+- Pre-production test environment
+
+### VU iteration duration
+
+A rule of thumb is that the shorter the "VU iteration duration" the more frequent you *can* run your tests without introducing long delays in the development cycle feedback loop, or blocking your team mates' deployments from access to shared pre-production environments.
+
+A quick re-cap of the [test life cycle](/using-k6/test-life-cycle) article:
+
+```js
+export default function() {
+ // k6 will execute this default function in a loop for every VU
+ // for the duration of the test run.
+
+ // The duration it takes for a VU to complete one loop, or iteration,
+ // of this function is what we refer to as "VU iteration duration".
+}
+```
+
+You can find the VU iteration duration in the k6 terminal output:
+
+
+
+### Branching strategy
+
+Another important aspect to consider is your team's version control branching policy. If you are strict with keeping feature branches separate from you main team-wide development branch, and you have per-feature test environments, then you can also generally run load tests with a higher frequency than if your builds are competing for exclusive access to a shared pre-production environment.
+
+It's also important to consider that load test might not need to run at the same frequency as unit or functional tests. Running load tests could just as well be run as part of Pull/Merge Request creation or when a feature branch gets merged into the main team-wide development branch. Both of these setup variants can be achieved with most CI tools.
+
+
+### Pre-production test environment
+
+As touched upon in the previous section, on branching strategy, the test environment is the third point to consider. We point out "pre-production" specifically as that is the best practices when it comes to load testing. If your team is at the maturity level of running chaos experiments in productionn then fine, run load test in production as well. If not, stick to pre-production.
+
+Things to consider with your test environment:
+
+- Make sure there's no other traffic hitting the same test environment as your test
+- Make sure the system has an adequate amount of data in any database that is being accessed as a result of the test traffic
+- Make sure databases only contains test data, and not real production data
+
+### Guidance
+
+Generalized, our recommendation is as follows, broken down by VU iteration duration:
+
+| VU iteration duration | Test frequency | Trigger mechanism |
+| --------------------- | -------------- | -------------- |
+| <10s | On every commit | Commit push |
+| <60s | Daily/Nightly | Scheduling, Pull/Merge Requests or when merging into main development branch |
+| >60s | Couple of times a week | Scheduling, Pull/Merge Requests or when merging into main development branch |
+| >5min | Once a week | Scheduling, Pull/Merge Requests or when merging into main development branch |
+
+**Scheduling**
+
+Besides triggering tests based on commit events, we also often see users and customers use [cron](https://k6.io/blog/performance-monitoring-with-cron-and-k6) or CI tool equivalent mechanisms for running tests on off-hours or at a particular cadence.
+
+If you're using k6 Cloud you can use the built in [scheduling feature](/cloud/creating-and-running-a-test/scheduling-tests) to trigger tests at a frequency of your chosing.
+
+**Load test suite**
+
+Do you need to run your full load test suite on every commit? Probably not, but in any case should not be the initial ambition. Repeating the advice from earlier in the guide, start small and expand from there. Start by evaluating the most business critical transactions/journeys you have in your system and start by automating those.
+
+## Notifications
+
+Once you have your load tests integrated into a CI pipeline you should make sure you’re also getting notified whenever a build/pipeline fails. You might already get notification via email, Slack, Microsoft Teams or Webhook through your CI tool, but if not you can set it up as follows.
+
+### For k6 OSS
+
+There’s no builtin notification mechanism in k6 OSS, so you’d have to send a notification from the test script. One way to achieve this is by sending a notification event request to Slack or Microsoft Teams.
+
+For Slack you need to first [setup an incoming webhook](https://slack.com/intl/en-se/help/articles/115005265063-Incoming-WebHooks-for-Slack). Once setup you get a webhook URL that you specify as the target of the POST request in the teardown function:
+
+```js
+import {sleep} from "k6";
+import http from "k6/http";
+
+export let options = {
+ thresholds: {
+ // fail the test if 95th percentile response goes above 500ms
+ "http_req_duration": ["p(95)<500"]
+ }
+};
+
+export default function() {
+ http.get('https://test.k6.io/');
+ sleep(5.0);
+}
+
+export function teardown(data) {
+ // send notification request to Slack API
+ let event = {
+ text: 'My test just finished!',
+ };
+ let res = http.post('https://hooks.slack.com/services/...',
+ JSON.stringify(event), { headers: { 'Content-Type': 'application/json' } });
+}
+```
+
+For Microsoft Teams you need to first [setup an incoming webhook connector](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook). Once setup you get a webhook URL that you specify as the target of the POST request in the teardown function:
+
+```js
+import http from "k6/http";
+import {sleep} from "k6";
+
+export let options = {
+ thresholds: {
+ // fail the test if 95th percentile response goes above 500ms
+ "http_req_duration": ["p(95)<500"]
+ },
+ // Increase teardown function timeout as MS Teams API seems to be slower than >10s
+ teardownTimeout: '60s'
+};
+
+export default function() {
+ http.get('https://test.k6.io/');
+ sleep(5.0);
+}
+
+export function teardown(data) {
+ // send notification request to Microsoft Teams API
+ let event = {
+ text: "My test just finished!"
+ };
+ let res = http.post('https://outlook.office.com/webhook/...',
+ JSON.stringify(event), { headers: { 'Content-Type': 'application/json' } });
+}
+```
+
+### For k6 cloud
+
+If you’re running your tests from k6 Cloud then you can make use of our [Slack integration](/cloud/integrations/notifications#slack).
+
+
+## CI integration guides
+
+We have written CI tool specific guides following the steps mentioned above:
+
+
+
+
+## See also
+
+- [Test life cycle](/using-k6/test-life-cycle)
+- [Thresholds](/using-k6/thresholds)
+- [Modules](/using-k6/modules)
+- [Scheduling](/cloud/creating-and-running-a-test/scheduling-tests)
+- [Slack integration](/cloud/integrations/notifications#slack)
diff --git a/src/data/markdown/docs/01 guides/05 Testing Guides/Frontend-Backend-LoadTesting.png b/src/data/markdown/docs/01 guides/05 Testing Guides/Frontend-Backend-LoadTesting.png
new file mode 100644
index 0000000000..dff6dacd9d
Binary files /dev/null and b/src/data/markdown/docs/01 guides/05 Testing Guides/Frontend-Backend-LoadTesting.png differ
diff --git a/src/data/markdown/docs/01 guides/05 Testing Guides/images/vu-iteration-duration-k6-cli.png b/src/data/markdown/docs/01 guides/05 Testing Guides/images/vu-iteration-duration-k6-cli.png
new file mode 100644
index 0000000000..cc7116c808
Binary files /dev/null and b/src/data/markdown/docs/01 guides/05 Testing Guides/images/vu-iteration-duration-k6-cli.png differ
diff --git a/src/data/markdown/docs/01 guides/05 Testing Guides/our-tools.png b/src/data/markdown/docs/01 guides/05 Testing Guides/our-tools.png
new file mode 100644
index 0000000000..3facf8bc89
Binary files /dev/null and b/src/data/markdown/docs/01 guides/05 Testing Guides/our-tools.png differ
diff --git a/src/data/markdown/docs/01 guides/05 Testing Guides/postman-export.png b/src/data/markdown/docs/01 guides/05 Testing Guides/postman-export.png
new file mode 100644
index 0000000000..5f8169c6bf
Binary files /dev/null and b/src/data/markdown/docs/01 guides/05 Testing Guides/postman-export.png differ
diff --git a/src/data/markdown/docs/01 guides/06 Misc/01 Usage reports.md b/src/data/markdown/docs/01 guides/06 Misc/01 Usage reports.md
new file mode 100644
index 0000000000..33a4ddbf3e
--- /dev/null
+++ b/src/data/markdown/docs/01 guides/06 Misc/01 Usage reports.md
@@ -0,0 +1,62 @@
+---
+title: "Usage reports"
+excerpt: ""
+---
+
+By default, k6 sends a usage report each time it is run, so that we can track how often people use it. This report can be turned off by setting the environment variable `K6_NO_USAGE_REPORT` or by adding the option `--no-usage-report` when executing k6.
+
+The usage report does not contain any information about what you are testing. The contents are the following:
+
+- The k6 version (string, e.g. "0.17.2")
+- Max VUs configured (number)
+- Test duration (number)
+- Total stages duration (number)
+- VU iterations configured (number)
+- The running program's operating system target (`darwin`, `freebsd`, `linux`...)
+- The running program's architecture target (386, amd64, arm, s390x...)
+
+This info is sent to an HTTP server that collects statistics on k6 usage.
+
+For those interested, here is the actual Go [code](https://github.com/loadimpact/k6/blob/master/cmd/run.go) that generates and sends the usage report:
+
+
+
+```go
+ // If the user hasn't opted out: report usage.
+
+ if !conf.NoUsageReport.Bool {
+ go func() {
+ u := "http://k6reports.k6.io/"
+ mime := "application/json"
+ var endTSeconds float64
+
+ if endT := engine.Executor.GetEndTime(); endT.Valid {
+ endTSeconds = time.Duration(endT.Duration).Seconds()
+ }
+
+ var stagesEndTSeconds float64
+ if stagesEndT := lib.SumStages(engine.Executor.GetStages()); stagesEndT.Valid {
+ stagesEndTSeconds = time.Duration(stagesEndT.Duration).Seconds()
+ }
+
+ body, err := json.Marshal(map[string]interface{}{
+ "k6_version": Version,
+ "vus_max": engine.Executor.GetVUsMax(),
+ "iterations": engine.Executor.GetEndIterations(),
+ "duration": endTSeconds,
+ "st_duration": stagesEndTSeconds,
+ "goos": runtime.GOOS,
+ "goarch": runtime.GOARCH,
+ })
+
+ if err != nil {
+ panic(err) // This should never happen!!
+ }
+ if _, err := http.Post(u, mime, bytes.NewBuffer(body)); err != nil {
+ log.WithError(err).Debug("Couldn't send usage blip")
+ }
+ }()
+ }
+```
+
+
diff --git a/src/data/markdown/docs/01 guides/06 Misc/02 Fine tuning OS.md b/src/data/markdown/docs/01 guides/06 Misc/02 Fine tuning OS.md
new file mode 100644
index 0000000000..2809ee4da3
--- /dev/null
+++ b/src/data/markdown/docs/01 guides/06 Misc/02 Fine tuning OS.md
@@ -0,0 +1,323 @@
+---
+title: "Fine tuning OS"
+excerpt: ""
+---
+
+## Background
+
+A number of users while running their test scripts locally will run into limits within their OS which would prevent them from making the necessary number of requests to complete the test. This limit usually manifests itself in a form of **Too Many Open Files** error. These limits, if unchanged, can be a severe bottleneck if you choose to run a somewhat bigger or complicated test locally on your machine.
+
+In this article we will show you how to inspect the OS imposed limits of your system, tweak them and scale for larger tests.
+
+Important to note here is that everything that we are covering in this article needs to be approached with a healthy dose of caution. As with any changes you are introducing to your OS, we recommend not to blindly change your system settings to a specific value. You should document ways of testing that shows a clear before/after relation. E.g. before changing MSL / TIME_WAIT period, confirm that you’re experiencing the issue (error messages, netstat, ss, etc.), change settings conservatively, re-run the test and note any improvement. This way we can gauge the impact of the optimization, any negative side-effects and come up with a range of recommended values.
+
+> Modifications below have been tested for macOS Sierra 10.12 and above, so if you are running an older version than that, the process for changing these settings may be different.
+
+## User resource limits
+
+Unix operating system derivatives like GNU/Linux, BSDs and macOS, have the capability to limit the amount of system resources available to a process to safeguard system stability. This includes the total amount of memory, CPU time or amount of open files a single process is allowed to manage.
+
+Since in Unix everything is a file, including network connections, application testing tools that heavily use the network, such as k6, might reach the configured limit of allowed open files, depending on the amount of network connections used in a particular test.
+
+As mentioned in our opening section, this results in a message like the following being shown during a test:
+
+```bash
+WARN[0127] Request Failed error="Get http://example.com/: dial tcp example.com: socket: too many open files"
+```
+
+This message means that the network resource limit has been reached, which will prevent k6 from creating new connections, thus altering the test result. In some cases this may be desired, to measure overall system performance, for example, but in most cases this will be a bottleneck towards testing the HTTP server and web application itself.
+
+Below we will look at ways to increase this resource limit, and allow k6 to run tests with hundreds or thousands of concurrent VUs from a single system.
+
+## Limit types
+
+There are two types of resource limits in Unix systems:
+
+- hard limits: these are the absolute maximum allowed for each user, and can only be configured by the root user.
+- soft limits: these can be configured by each user, but cannot be above the hard limit setting.
+
+## Viewing limits configuration
+
+**Linux**
+
+On GNU/Linux, you can see the configured limits with the ulimit command.
+
+`ulimit -Sa` will show all soft limits for the current user:
+
+```bash
+$ ulimit -Sa
+core file size (blocks, -c) 0
+data seg size (kbytes, -d) unlimited
+scheduling priority (-e) 0
+file size (blocks, -f) unlimited
+pending signals (-i) 3736
+max locked memory (kbytes, -l) 16384
+max memory size (kbytes, -m) unlimited
+open files (-n) 1024
+pipe size (512 bytes, -p) 8
+POSIX message queues (bytes, -q) 819200
+real-time priority (-r) 0
+stack size (kbytes, -s) 8192
+cpu time (seconds, -t) unlimited
+max user processes (-u) 3736
+virtual memory (kbytes, -v) unlimited
+file locks (-x) unlimited
+```
+While `ulimit -Ha` will show all hard limits for the current user:
+
+```bash
+$ ulimit -Ha
+core file size (blocks, -c) unlimited
+data seg size (kbytes, -d) unlimited
+scheduling priority (-e) 0
+file size (blocks, -f) unlimited
+pending signals (-i) 3736
+max locked memory (kbytes, -l) 16384
+max memory size (kbytes, -m) unlimited
+open files (-n) 1048576
+pipe size (512 bytes, -p) 8
+POSIX message queues (bytes, -q) 819200
+real-time priority (-r) 0
+stack size (kbytes, -s) unlimited
+cpu time (seconds, -t) unlimited
+max user processes (-u) 3736
+virtual memory (kbytes, -v) unlimited
+file locks (-x) unlimited
+```
+
+Note the difference of open files being a maximum of 1024 for the soft limit, while it's 1048576 for the hard limit.
+
+**macOS**
+
+In macOS however, you will have a couple of different system imposed limits to take into consideration.
+
+The first one is `launchctl limit maxfiles` which prints the per-process limits which are specified also as a soft limit and a hard limit. When a soft limit is exceeded a process may receive a signal (for example, if the CPU time or file size is exceeded), but it will be allowed to continue execution until it reaches the hard limit (or modifies its resource limit).
+`kern.maxfiles` is the limit of total file descriptors on the entire system - the sum total of all the open files for all processes plus all the files the kernel has open for its own purposes.
+
+`sysctl kern.maxfiles`
+
+`sysctl kern.maxfilesperproc`
+
+So, to reiterate, running commands above will show you the system limits on open files and running processes.
+
+## Changing limits configuration
+
+The first thing you should consider before changing the configuration is the amount of network connections you expect your test to require. The http_reqs metric in the k6 result summary can hint at this, but a baseline calculation of number of max. VUs * number of HTTP requests in a single VU iteration will deliver a fair approximation. Note that k6 also deals with text files and other resources that count towards the "open files" quota, but network connections are the biggest consumers.
+
+**macOS**
+
+Before we can change any system imposed limits in macOS we will need to disable a security feature put in place to prevent us in doing so. You will need to disable System Integrity Protection that was introduced in OS X El Capitan to prevent certain system-owned files and directories from being modified by processes without the proper privileges.
+
+To disable it you will need to restart your Mac and hold down `Command + R` while it boots. This will boot it into Recovery Mode.
+
+There you should navigate to `Utilities` which are located in the menu bar at the top of the screen, then open `Terminal`. Once you have it open, enter the following command:
+
+`csrutil disable`
+
+Once you press enter and close the Terminal, you can reboot your Mac normally and log into your account.
+
+## Changing soft limits
+**Linux**
+
+So, let's say that we want to run a 1000 VU test which makes 4 HTTP requests per iteration. In this case we could increase the open files limit to 5000, to account for additional non-network file usage. This can be done with the following command:
+
+```bash
+$ ulimit -n 5000
+```
+
+This changes the limit only for the current shell session.
+
+If we want to persist this change for future sessions, we can add this to a shell startup file. For Bash this would be:
+
+```bash
+$ echo "ulimit -n 5000" >> ~/.bashrc
+```
+
+**macOS**
+
+If the soft limit is too low, set the current session to (values written here are usually close to default ones) :
+
+```bash
+sudo launchctl limit maxfiles 65536 200000
+```
+
+Since sudo is needed, you are prompted for a password.
+
+## Changing hard limits
+**Linux**
+
+If the above command results in an error like cannot modify limit: Operation not permitted or value exceeds hard limit, that means that the hard limit is too low, which as mentioned before, can only be changed by the root user.
+
+This can be done by modifying the `/etc/security/limits.conf` file.
+
+For example, to set both soft and hard limits of the amount of open files per process for the alice account, open `/etc/security/limits.conf` as root in your text editor of choice and add the following lines:
+
+```bash
+alice soft nofile 5000
+alice hard nofile 1048576
+```
+
+The new limits will be in place after logging out and back in.
+
+Alternatively, * hard nofile 1048576 would apply the setting for all non-root user accounts, and root hard nofile 1048576 for the root user. See the documentation in that file or man bash for the ulimit command documentation.
+
+**macOS**
+
+Next step will be to configure your new file limits. Open terminal and paste the following command:
+
+```bash
+sudo nano /Library/LaunchDaemons/limit.maxfiles.plist
+```
+
+This will open a text editor inside your terminal window where you will be prompted to provide your user password and then paste the following:
+
+
+```xml
+
+
+
+
+ Label
+ limit.maxfiles
+ ProgramArguments
+
+ launchctl
+ limit
+ maxfiles
+ 64000
+ 524288
+
+ RunAtLoad
+
+ ServiceIPC
+
+
+
+```
+
+Pressing `Control + X` will save the changes and exit the editor. By pasting and saving this we have introduced two different limitations to your maxfiles limit. The first one (64000) is a soft limit, which if reached, will prompt your Mac to prepare to stop allowing new file opens but still let them open. If the second one is reached (524288), a hard limit, you will again start seeing your old friend, the 'too many files open' error message.
+
+We will use the same procedure to increase the processes limit next.
+
+While in Terminal create a similar file with this command:
+
+```bash
+sudo nano /Library/LaunchDaemons/limit.maxproc.plist
+```
+
+Again, after prompted for your password, you can paste the following and save and close with `Control + X`
+
+```xml
+
+
+
+
+ Label
+ limit.maxproc
+ ProgramArguments
+
+ launchctl
+ limit
+ maxproc
+ 2048
+ 4096
+
+ RunAtLoad
+
+ ServiceIPC
+
+
+
+ ```
+
+ All that is left after this is to reboot your Mac back to the Recovery Mode, open the Terminal, turn the SIP back on with `csrutil enable` and check if the limits were changed with commands we used at the beginning.
+
+In most cases these limits should be enough to run most of your simple tests locally for some time, but you can modify the files above to any values you will need in your testing.
+
+
+
+> Please be aware that all of these limitations are put in place to protect your operating system from files and applications that are poorly written and might leak memory like in huge quantities. We would suggest not going too overboard with the values, or you might find your system slowing down to a crawl if or when it runs out of RAM.
+
+
+
+## General optimizations not dependent on OS
+In this section we will go over some of the optimisations that are not necessarily dependant on your OS, but may impact your testing.
+
+## RAM usage
+
+Depending on the particular k6 test: maximum number of VUs used, number and size of JavaScript dependencies, and complexity of the test script itself, k6 can consume large amounts of system RAM during test execution. While the development is focused on reducing RAM usage as much as possible, a single test run might use tens of gigabytes of RAM under certain scenarios.
+
+As a baseline, count each VU instance to require between 5MB and 20MB of RAM, depending on your script complexity and dependencies. This is roughly between 5GB and 20GB of required system RAM for a 1,000 VU test, so make sure that sufficient physical RAM is available to meet your test demands.
+
+## Virtual memory
+In addition to physical RAM, ensure that the system is configured with an appropriate amount of virtual memory, or swap space, in case higher memory usage bursts are required.
+
+You can see the status and amount of available swap space on your system with the commands swapon or free.
+
+We won't go into swap configuration details here, but you can find several guides online.
+
+## Network performance
+Because k6 can generate and sustain large amounts of network traffic, it also stresses the network stack of modern operating systems. Under certain loads or network conditions it's possible to achieve higher throughput and better performance by tweaking some network settings of the operating system or restructuring the network conditions of the test.
+
+## TCP TIME_WAIT period
+TCP network applications, such as web clients and servers, are assigned a network socket pair (a unique combination of local address, local port, remote address, and remote port) for each incoming or outgoing connection. Typically this socket pair is used for a single HTTP request/response session, and closed soon after. However, even after a connection is successfully closed by the application, the kernel might still reserve resources for quickly reopening the same socket if a new matching TCP segment arrives. This also occurs during network congestion where some packets get lost in transmission. This places the socket in a TIME_WAIT state, and is released once the TIME_WAIT period expires. This period is typically configured between 15 seconds and 2 minutes.
+
+The problem some applications like k6 might run into is causing a high number of connections to end up in the TIME_WAIT state, which can prevent new network connections being created.
+
+In these scenarios, before making changes to the system network configuration, which might have adverse side-effects for other applications, it's preferable to first take some common testing precautions.
+Use different server ports or IPs
+
+Since sockets are uniquely created for a combination of local address, local port, remote address and remote port, a safe workaround for avoiding TIME_WAIT congestion is using different server ports or IP addresses.
+
+For example, you can configure your application to run on ports :8080, :8081, :8082, etc. and spread out your HTTP requests across these endpoints.
+
+## Increase local port range
+When creating an outgoing network connection the kernel allocates a local (source) port for the connection from a range of available ports.
+
+**GNU/Linux**
+
+On GNU/Linux you can see this range with:
+
+```bash
+$ sysctl net.ipv4.ip_local_port_range net.ipv4.ip_local_port_range = 32768 60999
+```
+
+While 28,231 ports might be sufficient for most use cases, this might be a limiting factor if you’re testing with thousands of connections. You can increase it with, for example:
+
+```bash
+sysctl -w net.ipv4.ip_local_port_range="16384 65000"
+```
+
+Be aware that this range applies to both TCP and UDP, so be conservative with the values you choose and increase as needed.
+
+To make the changes permanent, add `net.ipv4.ip_local_port_range=16384 65000` to `/etc/sysctl.conf`.
+Last resort tweaks
+If you still experience network issues with the above changes, consider enabling net.ipv4.tcp_tw_reuse:
+
+```bash
+sysctl -w net.ipv4.tcp_tw_reuse=1
+```
+
+This will enable a feature to quickly reuse connections in TIME_WAIT state, potentially yielding higher throughput.
+
+**macOS**
+
+On macOS the default ephemeral port range is 49152 to 65535, for a total of 16384 ports. You can check this with the sysctl command:
+
+```bash
+$ sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last
+
+net.inet.ip.portrange.first: 49152
+net.inet.ip.portrange.last: 65535
+```
+
+Once you run out of ephemeral ports, you will normally need to wait until the TIME_WAIT state expires (2 * maximum segment lifetime) until you can reuse a particular port number. You can double the number of ports by changing the range to start at 32768, which is the default on Linux and Solaris. (The maximum port number is 65535 so you cannot increase the high end.)
+
+```bash
+$ sudo sysctl -w net.inet.ip.portrange.first=32768
+
+net.inet.ip.portrange.first: 49152 -> 32768
+```
+
+Note that the official range designated by IANA is 49152 to 65535, and some firewalls may assume that dynamically assigned ports fall within that range. You may need to reconfigure your firewall in order to make use of a larger range outside of your local network.
diff --git a/src/data/markdown/docs/01 guides/06 Misc/03 k6 REST API.md b/src/data/markdown/docs/01 guides/06 Misc/03 k6 REST API.md
new file mode 100644
index 0000000000..85eb1d5821
--- /dev/null
+++ b/src/data/markdown/docs/01 guides/06 Misc/03 k6 REST API.md
@@ -0,0 +1,584 @@
+---
+title: 'k6 REST API'
+excerpt: ''
+hideFromSidebar: true
+---
+
+When k6 starts, it spins up an HTTP server with a REST API that can be used to control some
+parameters of the test execution. By default, that server listens on `localhost:6565`, but
+that can be modified by the `--address` CLI flag.
+
+With this API you can see and control different execution aspects like number of VUs, Max
+VUs, pause or resume the test, list groups, set and get the setup data and so on.
+
+You can also find practical usage examples in
+[this blog post](https://k6.io/blog/how-to-control-a-live-k6-test).
+
+## Get Status
+
+**GET** `http://localhost:6565/v1/status`
+
+
+
+```bash
+curl -X GET \
+ http://localhost:6565/v1/status \
+ -H 'Content-Type: application/json'
+```
+
+```json
+{
+ "data": {
+ "attributes": {
+ "paused": false,
+ "running": true,
+ "tainted": false,
+ "vus": 1,
+ "vus-max": 1
+ },
+ "id": "default",
+ "type": "status"
+ }
+}
+```
+
+
+
+## Update Status
+
+**PATCH** `http://localhost:6565/v1/status`
+
+
+
+```bash
+curl -X PATCH \
+ http://localhost:6565/v1/status \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "data": {
+ "attributes": {
+ "paused": true,
+ "vus": 1,
+ "vus-max": 1
+ },
+ "id": "default",
+ "type": "status"
+ }
+}'
+```
+
+```json
+{
+ "data": {
+ "type": "status",
+ "id": "default",
+ "attributes": {
+ "paused": true,
+ "vus": 1,
+ "vus-max": 1,
+ "running": true,
+ "tainted": false
+ }
+ }
+}
+```
+
+
+
+This endpoint lets you pause/resume a running test and set the number of `vus` and `vus-max`
+during the test.
+
+## List Metrics
+
+**GET** `http://localhost:6565/v1/metrics`
+
+
+
+```bash
+curl -X GET \
+ http://localhost:6565/v1/metrics \
+ -H 'Content-Type: application/json'
+```
+
+```json
+{
+ "data": [
+ {
+ "type": "metrics",
+ "id": "http_req_duration",
+ "attributes": {
+ "type": "trend",
+ "contains": "time",
+ "tainted": null,
+ "sample": {
+ "avg": 122.529465,
+ "max": 179.098624,
+ "med": 115.83006,
+ "min": 107.743524,
+ "p(90)": 136.9331272,
+ "p(95)": 158.01587559999996
+ }
+ }
+ },
+ {
+ "type": "metrics",
+ "id": "http_req_connecting",
+ "attributes": {
+ "type": "trend",
+ "contains": "time",
+ "tainted": null,
+ "sample": {
+ "avg": 11.2357072,
+ "max": 112.357072,
+ "med": 0,
+ "min": 0,
+ "p(90)": 11.235707199999961,
+ "p(95)": 61.796389599999884
+ }
+ }
+ },
+ {
+ "type": "metrics",
+ "id": "http_req_sending",
+ "attributes": {
+ "type": "trend",
+ "contains": "time",
+ "tainted": null,
+ "sample": {
+ "avg": 0.027994200000000004,
+ "max": 0.106594,
+ "med": 0.0192965,
+ "min": 0.017486,
+ "p(90)": 0.03165189999999997,
+ "p(95)": 0.0691229499999999
+ }
+ }
+ },
+ {
+ "type": "metrics",
+ "id": "http_req_waiting",
+ "attributes": {
+ "type": "trend",
+ "contains": "time",
+ "tainted": null,
+ "sample": {
+ "avg": 122.33937080000001,
+ "max": 179.021285,
+ "med": 115.74006299999999,
+ "min": 107.650352,
+ "p(90)": 136.8561833,
+ "p(95)": 157.93873414999996
+ }
+ }
+ },
+ {
+ "type": "metrics",
+ "id": "data_received",
+ "attributes": {
+ "type": "counter",
+ "contains": "data",
+ "tainted": null,
+ "sample": {
+ "count": 13830,
+ "rate": 1119.9222882571698
+ }
+ }
+ },
+ {
+ "type": "metrics",
+ "id": "http_req_blocked",
+ "attributes": {
+ "type": "trend",
+ "contains": "time",
+ "tainted": null,
+ "sample": {
+ "avg": 11.364957999999998,
+ "max": 113.611988,
+ "med": 0.004173,
+ "min": 0.003867,
+ "p(90)": 11.365557499999959,
+ "p(95)": 62.48877274999988
+ }
+ }
+ },
+ {
+ "type": "metrics",
+ "id": "http_req_receiving",
+ "attributes": {
+ "type": "trend",
+ "contains": "time",
+ "tainted": null,
+ "sample": {
+ "avg": 0.16209999999999997,
+ "max": 0.757392,
+ "med": 0.078622,
+ "min": 0.057306,
+ "p(90)": 0.2315264999999998,
+ "p(95)": 0.4944592499999994
+ }
+ }
+ },
+ {
+ "type": "metrics",
+ "id": "vus_max",
+ "attributes": {
+ "type": "gauge",
+ "contains": "default",
+ "tainted": null,
+ "sample": {
+ "value": 1
+ }
+ }
+ },
+ {
+ "type": "metrics",
+ "id": "http_reqs",
+ "attributes": {
+ "type": "counter",
+ "contains": "default",
+ "tainted": null,
+ "sample": {
+ "count": 10,
+ "rate": 0.8097775041628127
+ }
+ }
+ },
+ {
+ "type": "metrics",
+ "id": "http_req_tls_handshaking",
+ "attributes": {
+ "type": "trend",
+ "contains": "time",
+ "tainted": null,
+ "sample": {
+ "avg": 0,
+ "max": 0,
+ "med": 0,
+ "min": 0,
+ "p(90)": 0,
+ "p(95)": 0
+ }
+ }
+ },
+ {
+ "type": "metrics",
+ "id": "data_sent",
+ "attributes": {
+ "type": "counter",
+ "contains": "data",
+ "tainted": null,
+ "sample": {
+ "count": 860,
+ "rate": 69.64086535800189
+ }
+ }
+ },
+ {
+ "type": "metrics",
+ "id": "iteration_duration",
+ "attributes": {
+ "type": "trend",
+ "contains": "time",
+ "tainted": null,
+ "sample": {
+ "avg": 1134.89821,
+ "max": 1238.377413,
+ "med": 1118.223518,
+ "min": 1108.405498,
+ "p(90)": 1185.348477,
+ "p(95)": 1211.8629449999999
+ }
+ }
+ },
+ {
+ "type": "metrics",
+ "id": "iterations",
+ "attributes": {
+ "type": "counter",
+ "contains": "default",
+ "tainted": null,
+ "sample": {
+ "count": 10,
+ "rate": 0.8097775041628127
+ }
+ }
+ },
+ {
+ "type": "metrics",
+ "id": "vus",
+ "attributes": {
+ "type": "gauge",
+ "contains": "default",
+ "tainted": null,
+ "sample": {
+ "value": 1
+ }
+ }
+ }
+ ]
+}
+```
+
+
+
+This endpoint will give you all the metrics in the current time. You can see more details on all
+metrics available and how to create new ones in [Metrics](/using-k6/metrics).
+
+## Get Metric
+
+**GET** `http://localhost:6565/v1/metrics/id`
+
+
+
+```bash
+curl -X GET \
+ http://localhost:6565/v1/metrics/http_req_receiving \
+ -H 'Content-Type: application/json'
+```
+
+```json
+{
+ "data": {
+ "attributes": {
+ "contains": "time",
+ "sample": {
+ "avg": 0.12641856097560983,
+ "max": 1.1397,
+ "med": 0.074412,
+ "min": 0.057858,
+ "p(90)": 0.208553,
+ "p(95)": 0.218015
+ },
+ "tainted": null,
+ "type": "trend"
+ },
+ "id": "http_req_receiving",
+ "type": "metrics"
+ }
+}
+```
+
+
+
+This endpoint will give you details for the given metric in the current time.
+
+You can see more on all metrics available and how to create new ones in [Metrics](/using-k6/metrics).
+
+## List Groups
+
+**GET** `http://localhost:6565/v1/groups`
+
+
+
+```bash
+curl -X GET \
+ http://localhost:6565/v1/groups \
+ -H 'Content-Type: application/json'
+```
+
+```json
+{
+ "data": [
+ {
+ "type": "groups",
+ "id": "d41d8cd98f00b204e9800998ecf8427e",
+ "attributes": {
+ "path": "",
+ "name": "",
+ "checks": null
+ },
+ "relationships": {
+ "groups": {
+ "data": [
+ {
+ "type": "groups",
+ "id": "b0470a9324a4ae563b04e9ac49fbc9cf"
+ }
+ ]
+ },
+ "parent": {
+ "data": null
+ }
+ }
+ },
+ {
+ "type": "groups",
+ "id": "b0470a9324a4ae563b04e9ac49fbc9cf",
+ "attributes": {
+ "path": "::visit homepage",
+ "name": "visit homepage",
+ "checks": null
+ },
+ "relationships": {
+ "groups": {
+ "data": []
+ },
+ "parent": {
+ "data": {
+ "type": "groups",
+ "id": "d41d8cd98f00b204e9800998ecf8427e"
+ }
+ }
+ }
+ }
+ ]
+}
+```
+
+
+
+This endpoint returns all groups available on the test.
+
+For more details on how to create groups please go to [Tags and Groups](/using-k6/tags-and-groups).
+
+## Get Group
+
+**GET** `http://localhost:6565/v1/groups/id`
+
+
+
+```bash
+curl -X GET \
+ http://localhost:6565/v1/group/b0470a9324a4ae563b04e9ac49fbc9cf \
+ -H 'Content-Type: application/json'
+```
+
+```json
+{
+ "data": {
+ "type": "groups",
+ "id": "b0470a9324a4ae563b04e9ac49fbc9cf",
+ "attributes": {
+ "path": "::visit homepage",
+ "name": "visit homepage",
+ "checks": null
+ },
+ "relationships": {
+ "groups": {
+ "data": []
+ },
+ "parent": {
+ "data": {
+ "type": "groups",
+ "id": "d41d8cd98f00b204e9800998ecf8427e"
+ }
+ }
+ }
+ }
+}
+```
+
+
+
+This endpoint returns the Group with the given ID.
+
+For more details on how to create groups, please go to [Tags and Groups](/using-k6/tags-and-groups).
+
+## Get Setup Data
+
+**GET** `http://localhost:6565/v1/setup`
+
+
+
+```bash
+curl -X GET \
+ http://localhost:6565/v1/setup \
+ -H 'Content-Type: application/json'
+```
+
+```json
+{
+ "data": {
+ "type": "setupData",
+ "id": "default",
+ "attributes": {
+ "data": {
+ "a": 1
+ }
+ }
+ }
+}
+```
+
+
+
+This endpoint returns the current JSON-encoded setup data.
+
+For more detail about the setup stage please go to [Test life cycle](/using-k6/test-life-cycle).
+
+## Run Setup
+
+**PUT** `http://localhost:6565/v1/setup`
+
+
+
+```bash
+curl -X POST \
+ http://localhost:6565/v1/setup \
+ -H 'Content-Type: application/json'
+```
+
+```json
+{
+ "data": {
+ "type": "setupData",
+ "id": "default",
+ "attributes": {
+ "data": {
+ "a": 1
+ }
+ }
+ }
+}
+```
+
+
+
+This endpoint executes the Setup stage and returns the result.
+
+For more detail about the setup stage please go to [Test life cycle](/using-k6/test-life-cycle).
+
+## Update Setup
+
+**PATCH** `http://localhost:6565/v1/setup`
+
+
+
+```bash
+curl -X PUT \
+ http://localhost:6565/v1/setup \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "data": {
+ "attributes": {
+ "data": {
+ "a": 1,
+ "b": 2
+ }
+ },
+ "id": "default",
+ "type": "setupData"
+ }
+}'
+```
+
+```json
+{
+ "data": {
+ "type": "setupData",
+ "id": "default",
+ "attributes": {
+ "data": {
+ "a": 1,
+ "b": 2
+ }
+ }
+ }
+}
+```
+
+
+
+This endpoint parses the JSON request body and sets the result as Setup data.
+
+For more detail about the setup stage please go to [Test life cycle](/using-k6/test-life-cycle).
diff --git a/src/data/markdown/docs/01 guides/06 Misc/04 Glossary.md b/src/data/markdown/docs/01 guides/06 Misc/04 Glossary.md
new file mode 100644
index 0000000000..665a332b75
--- /dev/null
+++ b/src/data/markdown/docs/01 guides/06 Misc/04 Glossary.md
@@ -0,0 +1,136 @@
+---
+title: Glossary
+excerpt: |
+
+
+---
+
+When discussing complex topics, it is usually a good idea to define a clear, shared terminology to ensure that we leave as little room as possible for misunderstandings. Below, you'll find a list of terms commonly used within the k6 project and what we mean when we use them.
+
+## Correlation
+
+**Correlation** is a term used for describing the process of capturing dynamic data, received from the system under test, for reuse in subsequent requests. A common use case for correlation is retrieving and reusing a session id, or token, throughout the whole lifetime of a [virtual user](#virtual-user).
+
+## Dynamic Data
+
+**Dynamic data** is data that might, or will, change between test runs. Common examples are order ids, session tokens or timestamps.
+
+## Endurance Testing
+
+**Endurance testing** is a synonym for [soak testing](#soak-test).
+
+## Goja
+
+**Goja** is a javascript runtime, purely written in go, that emphasizes standard compliance and performance. We use goja to allow for test scripting without having to compromise speed, efficiency or reliability, which would have been the case using NodeJS. For more details, see the [Goja repository on GitHub](https://github.com/dop251/goja).
+
+## Horizontal Scalability
+
+**Horizontal Scalability** is a trait describing to what degree a system under test’s performance and capacity may be increased by adding more nodes (servers or computers for instance).
+
+## HTTP Archive
+
+An **HTTP Archive**, or **HAR file**, is a file containing logs of a browser interactions with the system under test. All of the included transactions are stored as JSON-formatted text. These archives may then be used to generate test scripts using, for instance, the [har-to-k6 Converter](https://github.com/loadimpact/har-to-k6). For more details, see the [HAR 1.2 Specification](http://www.softwareishard.com/blog/har-12-spec/).
+
+## k6 Cloud
+
+**k6 Cloud** is the common name for the entire cloud product, which is composed of both k6 Cloud Execution and k6 Cloud Test Results.
+
+## Load Test
+
+A **load test** is a type of test used to assess the performance of the system under test in terms of concurrent users or requests per second. See [Load Testing](/test-types/load-testing).
+
+## Metric
+
+A **metric** is a calculation that, using measurements, serves as an indicator of how the system under test performs under a given condition.
+
+## Parameterization
+
+**Parameterization** refers to the process of building a test in such a way that the values used throughout the test might be changed without having to change the actual test script.
+
+## Performance Threshold
+
+A **performance threshold** describes the limits of what is considered acceptable values for a metric produced by a performance test. In many ways, this is similar to an [SLO](#service-level-objective), although a performance threshold only concerns itself with a single metric.
+
+## Reliability
+
+**Reliability** is a trait used to describe a system under test’s ability to produce reliable results consecutively, even under pressure.
+
+## Requests per Second
+
+**Requests per Second**, or **RPS**, is the rate at which requests are executed against the system under test.
+
+## Saturation
+
+**Saturation** is reached when the system under test is fully utilized and hence, unable to handle any additional requests.
+
+## Scalability
+
+**Scalability** is a trait used to describe to what degree a system under test’s performance or capacity may be increased by adding additional resources. See [Vertical scalability](#vertical-scalability) and [Horizontal scalability](#horizontal-scalability).
+
+## Service-level Agreement
+
+A **service-level agreement**, or **SLA** is an agreement made between the one providing the service and someone, often a user of the service, promising that the availability of the service will meet a certain level during a certain period.
+
+If the service provider fails to deliver on that promise, some kind of penalty is usually applied, like a partial or full refund, or monetary compensation.
+
+## Service-level Indicator
+
+A **service-level indicator**, or **SLI** is the metric we use to measure whether a service meets the [service-level objective (SLO)](#service-level-objective). While doing performance monitoring this could, for instance, be the number of successful requests against the service during a specified period.
+
+## Service-level Objective
+
+A **service-level objective**, or **SLO** is an actual target, either internal or part of the [service-level agreement (SLA)](#service-level-agreement), for the availability of the service. This is often expressed as a percentage (99,2%, for instance). If the service meets or exceeds this target, it's deemed stable.
+
+## Smoke test
+
+A **smoke test** is a type of test used to verify that the system under test can handle a minimal amount of load without any issues. It’s commonly used as a first step, to ensure that everything works as intended under optimal conditions, before advancing to any of the other performance test types. See [Smoke Testing](/test-types/smoke-testing).
+
+## Soak test
+
+A **soak test** is a type of test used to uncover performance and reliability issues stemming from a system being under pressure for an extended period. See [Soak Testing](/test-types/soak-testing).
+
+## Stability
+
+**Stability** is a trait used to describe a system under test’s ability to withstand failures and erroneous behavior under normal usage.
+
+## Stress test
+
+A **stress test** is a type of test used to identify the limits of what the system under test is capable of handling in terms of load. See [Stress Testing](/test-types/stress-testing).
+
+## System under test
+
+**System under test** refers to the actual piece of software that we're currently testing. This could be an API, a website, infrastructure, or any combination of these.
+
+## Test Configuration
+
+The options object of a test script or configuration parameters passed via the CLI. See [Options](/using-k6/options).
+
+## Test Run
+
+An individual execution of a test script. See [Running k6](/getting-started/running-k6).
+
+## Test Script
+
+A **test script** is the actual code you run as part of your test run, as well as any (or at least most) of the configuration needed to run the code. It defines how the test will behave as well as what requests will be made. See the [Single Request example](https://k6.io/docs/examples/single-request).
+
+## User Journey
+
+**User journey** is used to describe a sequence of actions taken by either a real or simulated user.
+
+## User Scenario
+
+**User Scenario** is a synonym for [user journey](#user-journey).
+
+## Vertical Scalability
+
+**Vertical scalability** is a trait describing to what degree a system under test’s performance or capacity may be increased by adding more hardware resources to a node (RAM, cores, bandwidth, etc.).
+
+## Virtual Users
+
+**Virtual Users**, or **VUs** are used to perform separate and concurrent executions of your test script. They can make HTTP(s) and WebSocket requests against a webpage or API.
+
+Virtual Users, although emulated by k6 itself, can be used to mimic the behavior of a real user.
+
+VUs typically perform requests at a much higher rate than regular users. Because of this, there is seldom any need for anywhere near as many virtual users as the actual user target.
+
+For additional details, see [What are virtual users?](/cloud/cloud-faq/what-are-vus-virtual-users).
\ No newline at end of file
diff --git a/src/data/markdown/docs/02 javascript api/01 Init context.md b/src/data/markdown/docs/02 javascript api/01 Init context.md
new file mode 100644
index 0000000000..b952fe848c
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/01 Init context.md
@@ -0,0 +1,7 @@
+---
+title: "Init context"
+---
+The init context (aka "init code") is code in the global context that has
+access to a few functions not accessible during main script execution (aka
+"VU context" or "VU code"). For a more detailed description see
+[Running k6](/getting-started/running-k6#section-the-init-context-and-the-default-function).
diff --git a/src/data/markdown/docs/02 javascript api/01 Init context/open.md b/src/data/markdown/docs/02 javascript api/01 Init context/open.md
new file mode 100644
index 0000000000..db35c4feb6
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/01 Init context/open.md
@@ -0,0 +1,86 @@
+---
+title: "open( filePath, [mode] )"
+description: "Opens a file and reads all the contents into memory."
+excerpt: ""
+---
+Opens a file, reading all its contents into memory for use in the script. Favourably used to parameterize tests with data from CSV/JSON files etc.
+
+
+
+> ### Function only available in "init context"
+> This is a function that can only be called from the init context (aka `init code`), code in the global context that is, outside of the main export default function { ... }.
+>
+> By restricting it to the init context, we can easily determine what local files are needed to run the test and thus what we need to bundle up when distributing the test to multiple nodes in a clustered/distributed test.
+>
+> See example further down on this page. For more in-depth description see [Running k6](/getting-started/running-k6).
+
+
+
+| Parameter | Type | Description |
+| --------- | -----| ----------- |
+| filePath | string | The path to the file, absolute or relative, that will be read into memory. The file will only be loaded once, even when running with several VUs. |
+| mode | string | By default the contents of the file is read as text, but if you specify `b` the file will be read as binary data instead. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| string / bytes | The contents of the file, read as text or bytes (if `b` has been specified as the mode). |
+
+
+
+```json
+[
+ {
+ "username": "user1",
+ "password": "password1"
+ },
+ {
+ "username": "user2",
+ "password": "password2"
+ },
+ {
+ "username": "user3",
+ "password": "password3"
+ }
+]
+```
+
+
+
+
+
+```js
+import { sleep } from "k6";
+
+const users = JSON.parse(open("./users.json"));
+
+export default function() {
+ let user = users[__VU - 1];
+ console.log(`${user.username}, ${user.password}`);
+ sleep(3);
+}
+```
+
+
+
+
+
+```js
+import http from "k6/http";
+import { sleep } from "k6";
+
+let binFile = open("/path/to/file.bin", "b");
+
+export default function() {
+ var data = {
+ field: "this is a standard form field",
+ file: http.file(binFile, "test.bin")
+ };
+ var res = http.post("https://example.com/upload", data);
+ sleep(3);
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/02 k6.md b/src/data/markdown/docs/02 javascript api/02 k6.md
new file mode 100644
index 0000000000..33c5de5af5
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/02 k6.md
@@ -0,0 +1,4 @@
+---
+title: "k6"
+---
+The k6 module contains k6-specific functionality.
diff --git a/src/data/markdown/docs/02 javascript api/02 k6/check- val- sets- -tags- -.md b/src/data/markdown/docs/02 javascript api/02 k6/check- val- sets- -tags- -.md
new file mode 100644
index 0000000000..468d5bb73c
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/02 k6/check- val- sets- -tags- -.md
@@ -0,0 +1,45 @@
+---
+title: "check( val, sets, [tags] )"
+description: "Runs one or more checks on a value and generates a pass/fail result but does not throw errors or otherwise interrupt execution upon failure."
+---
+Run checks on a value. A check is a test condition that can give a truthy or
+falsy result. The `sets` parameter contains one or more checks, and the `check()`
+function will return `false` if any of them fail.
+
+Note that checks are not *asserts* in their traditional sense - a failed assertion
+will throw an error, while a check will always return with a pass or a failure.
+Failure conditions can then instead be controlled by thresholds, for more power and flexibility.
+
+| Parameter | Type | Description |
+|-----------------|--------|------------------------------------------|
+| val | any | Value to test. |
+| sets | object | Tests (checks) to run on the value. |
+| tags (optional) | object | Extra tags to attach to metrics emitted. |
+
+### Returns
+
+| Type | Description |
+|------|-------------|
+| boolean | `true` if all checks have succeeded, `false` otherwise. |
+
+
+### Example
+
+Using `check()` to verify that an HTTP response code was 200 and that body was 1234 bytes:
+
+
+
+```js
+import http from "k6/http";
+import { check } from "k6";
+
+export default function() {
+ let res = http.get("http://httpbin.org");
+ check(res, {
+ "response code was 200": (res) => res.status == 200,
+ "body size was 1234 bytes": (res) => res.body.length == 1234,
+ });
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/02 k6/fail- -err- -.md b/src/data/markdown/docs/02 javascript api/02 k6/fail- -err- -.md
new file mode 100644
index 0000000000..56404455cd
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/02 k6/fail- -err- -.md
@@ -0,0 +1,34 @@
+---
+title: "fail( [err] )"
+description: "Throws an error, failing and aborting the current VU script iteration immediately."
+---
+Immediately throw an error, aborting the current script iteration.
+
+`fail()` is a simple convenience wrapper on top of JavaScript's `throw()`,
+because the latter cannot be used as `[expr] || throw`, which is a convenient way to write k6 test code.
+
+| Parameter | Type | Description |
+|----------------|--------|--------------------------------------------|
+| err (optional) | string | Error message that gets printed to stderr. |
+
+### Example
+
+Aborting the current script iteration if a check fails:
+
+
+
+```js
+import http from "k6/http";
+import { check, fail } from "k6";
+
+export default function() {
+ let res = http.get("https://k6.io");
+ if (!check(res, {
+ "status code MUST be 200": (res) => res.status == 200,
+ })) {
+ fail("status code was *not* 200");
+ }
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/02 k6/group- name- fn -.md b/src/data/markdown/docs/02 javascript api/02 k6/group- name- fn -.md
new file mode 100644
index 0000000000..18cb516108
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/02 k6/group- name- fn -.md
@@ -0,0 +1,50 @@
+---
+title: "group( name, fn )"
+description: "Runs code inside a group. Used to organize results in a test."
+---
+Run code inside a group. Groups are used to organize results in a test.
+
+| Parameter | Type | Description |
+|-----------|----------|--------------------------------------------------------|
+| name | string | Name of the group. |
+| fn | function | Group body - code to be executed in the group context. |
+
+### Returns
+
+| Type | Description |
+|------|---------------------------|
+| any | The return value of _fn_. |
+
+
+### Example
+
+
+
+```js
+import { group, check } from "k6";
+import http from "k6/http";
+
+export default function() {
+ group("my user scenario", function() {
+ group("front page", function() {
+ let res = http.get("https://k6.io");
+ check(res, {
+ "status code is 200": (res) => res.status == 200,
+ });
+ });
+ group("features page", function() {
+ let res = http.get("https://k6.io/features");
+ check(res, {
+ "status code is 200": (res) => res.status == 200,
+ "h1 message is correct": (res) => res.html("h1").text().startsWith("Simple yet realistic load testing"),
+ });
+ });
+ });
+};
+```
+
+
+
+The above code will produce output like shown on the screenshot below,
+with check results presented separately depending on which group they were executed in:
+
diff --git a/src/data/markdown/docs/02 javascript api/02 k6/images/groups.png b/src/data/markdown/docs/02 javascript api/02 k6/images/groups.png
new file mode 100644
index 0000000000..4135d30379
Binary files /dev/null and b/src/data/markdown/docs/02 javascript api/02 k6/images/groups.png differ
diff --git a/src/data/markdown/docs/02 javascript api/02 k6/sleep- t -.md b/src/data/markdown/docs/02 javascript api/02 k6/sleep- t -.md
new file mode 100644
index 0000000000..91b9a5ebb7
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/02 k6/sleep- t -.md
@@ -0,0 +1,29 @@
+---
+title: "sleep( t )"
+description: "Suspends VU execution for the specified duration."
+---
+Suspend VU execution for the specified duration.
+
+| Parameter | Type | Description |
+|-----------|--------|-----------------------|
+| t | number | Duration, in seconds. |
+
+
+### Example
+
+Fetching two different pages with a 0-30 second random sleep in between:
+
+
+
+```js
+import { sleep } from "k6";
+import http from "k6/http";
+
+export default function() {
+ http.get("https://k6.io");
+ sleep(Math.random() * 30);
+ http.get("https://k6.io/features");
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto.md
new file mode 100644
index 0000000000..bec391b74f
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto.md
@@ -0,0 +1,5 @@
+---
+title: "k6/crypto"
+excerpt: ""
+---
+The k6/crypto module provides common hashing functionality available in the GoLang [crypto](https://golang.org/pkg/crypto/) package.
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto/01 Hasher.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto/01 Hasher.md
new file mode 100644
index 0000000000..418727a0c4
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto/01 Hasher.md
@@ -0,0 +1,37 @@
+---
+title: "Hasher"
+category: "k6api-class"
+description: "Object returned by crypto.createHash(). It allows adding more data to be hashed and to extract digests along the way."
+---
+This object is returned by [crypto.createHash()](/javascript-api/k6-crypto/createhash-algorithm)
+and allows the user to successively add more string data to be hashed, and to extract digests along the way.
+
+| Name | Type | Description |
+|------|------|-------------|
+| Hasher.update(string) | function | Add more data to the string we want to create a hash of. Takes one string argument, which is the new data we want to add. |
+| Hasher.digest(string) | function | Return a digest from the data added (using update()) to the Hasher object so far. Takes one string argument, which is the encoding format to return. This can be either "hex" or "base64". |
+
+### Example
+
+
+
+```js
+import crypto from 'k6/crypto';
+
+export default function() {
+ console.log(crypto.sha256('hello world!', 'hex'));
+ let hasher = crypto.createHash('sha256');
+ hasher.update('hello ');
+ hasher.update('world!');
+ console.log(hasher.digest('hex'));
+}
+```
+
+
+
+The above code sample should produce this in its output:
+
+```bash
+INFO[0000] 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9
+INFO[0000] 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9
+```
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto/createHMAC- algorithm- secret -.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto/createHMAC- algorithm- secret -.md
new file mode 100644
index 0000000000..8f25a9db51
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto/createHMAC- algorithm- secret -.md
@@ -0,0 +1,42 @@
+---
+title: "createHMAC( algorithm, secret )"
+description: "Create an HMAC hashing object, allowing the user to add data to hash multiple times, and extract hash digests along the way."
+---
+
+Creates a HMAC hashing object that can then be fed with data repeatedly, and from which you can extract a signed hash digest whenever you want.
+
+| Parameter | Type | Description |
+| --------- | :----: | :---------------------------------------------------------------------------------------------------------------------------------- |
+| algorithm | string | The hashing algorithm to use. One of `md4`, `md5`, `sha1`, `sha256`, `sha384`, `sha512`, `sha512_224`, `sha512_256` or `ripemd160`. |
+| secret | string | A shared secret used to sign the data. |
+
+### Returns
+
+| Type | Description |
+| ------ | :------------------------------------------------------- |
+| object | A [Hasher](/javascript-api/k6-crypto/hasher) object. |
+
+### Example
+
+
+
+```javascript
+import crypto from 'k6/crypto';
+
+export default function() {
+ console.log(crypto.hmac('sha256', 'a secret', 'my data', 'hex'));
+ let hasher = crypto.createHMAC('sha256', 'a secret');
+ hasher.update('my ');
+ hasher.update('data');
+ console.log(hasher.digest('hex'));
+}
+```
+
+
+
+The above script should result in the following being printed during execution:
+
+```shell
+INFO[0000] 82f669c8fde13aef6d6977257588dc4953dfac505428f8fd6b52e19cd96d7ea5
+INFO[0000] 82f669c8fde13aef6d6977257588dc4953dfac505428f8fd6b52e19cd96d7ea5
+```
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto/createHash- algorithm -.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto/createHash- algorithm -.md
new file mode 100644
index 0000000000..3bbb9e35fc
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto/createHash- algorithm -.md
@@ -0,0 +1,41 @@
+---
+title: "createHash( algorithm )"
+description: "Create a Hasher object, allowing the user to add data to hash multiple times, and extract hash digests along the way."
+---
+
+Creates a hashing object that can then be fed with data repeatedly, and from which you can extract a hash digest whenever you want.
+
+| Parameter | Type | Description |
+| --------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| algorithm | string | The name of the hashing algorithm you want to use. Can be any one of "md4", "md5", "sha1", "sha256", "sha384", "sha512", "sha512_224", "sha512_256", "ripemd160". |
+
+### Returns
+
+| Type | Description |
+| ------ | -------------------------------------------------------- |
+| object | A [Hasher](/javascript-api/k6-crypto/hasher) object. |
+
+### Example
+
+
+
+```js
+import crypto from 'k6/crypto';
+
+export default function() {
+ console.log(crypto.sha256('hello world!', 'hex'));
+ let hasher = crypto.createHash('sha256');
+ hasher.update('hello ');
+ hasher.update('world!');
+ console.log(hasher.digest('hex'));
+}
+```
+
+
+
+The above script should result in the following being printed during execution:
+
+```bash
+INFO[0000] 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9
+INFO[0000] 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9
+```
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto/hmac- algorithm- secret- data- outputEncoding -.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto/hmac- algorithm- secret- data- outputEncoding -.md
new file mode 100644
index 0000000000..a9f19506f2
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto/hmac- algorithm- secret- data- outputEncoding -.md
@@ -0,0 +1,44 @@
+---
+title: 'hmac( algorithm, secret, data, outputEncoding )'
+description: 'Use HMAC to sign an input string.'
+---
+
+Use [HMAC](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code) to sign a piece of data using a shared secret.
+
+| Parameter | Type | Description |
+| -------------- | :----: | :---------------------------------------------------------------------------------------------------------------------------------- |
+| algorithm | string | The hashing algorithm to use. One of `md4`, `md5`, `sha1`, `sha256`, `sha384`, `sha512`, `sha512_224`, `sha512_256` or `ripemd160`. |
+| secret | string | A shared secret used to sign the data. |
+| data | string | The data to sign. |
+| outputEncoding | string | Describes what type of encoding to use for the hash value. Can be "base64" or "hex". |
+
+### Returns
+
+| Type | Description |
+| ------ | ------------------------------ |
+| string | The string-encoded hash digest |
+
+### Example
+
+
+
+```js
+import crypto from 'k6/crypto';
+
+export default function() {
+ let hash = crypto.hmac('sha256', 'mysecret', 'hello world!', 'hex');
+ console.log(hash);
+}
+```
+
+
+
+The above script should result in the following being printed during execution:
+
+
+
+```bash
+INFO[0000] 893a72d8cab129e5ba85aea4599fd53f59bfe652cff4098a3780313228d8c20f
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto/md4- input- outputEncoding -.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto/md4- input- outputEncoding -.md
new file mode 100644
index 0000000000..81596e1670
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto/md4- input- outputEncoding -.md
@@ -0,0 +1,39 @@
+---
+title: "md4( input, outputEncoding )"
+description: "Use MD4 to hash an input string."
+---
+Use [md4](https://godoc.org/golang.org/x/crypto/md4) to hash an input string.
+
+| Parameter | Type | Description |
+| --------- |------|-------------|
+| input |string | The input string to hash. |
+|outputEncoding | string | Describes what type of encoding to use for the hash value. Can be "base64" or "hex". |
+
+
+### Returns
+
+| Type | Description |
+|------|-------------|
+| string | The string-encoded hash digest |
+
+
+### Example
+
+
+
+```js
+import crypto from 'k6/crypto';
+
+export default function() {
+ let hash = crypto.md4('hello world!', 'hex');
+ console.log(hash);
+}
+```
+
+
+
+The above script should result in the following being printed during execution:
+
+```shell
+INFO[0000] 3363b72840acd5f49f922fef598ee85d
+```
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto/md5- input- outputEncoding -.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto/md5- input- outputEncoding -.md
new file mode 100644
index 0000000000..ef3dd38124
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto/md5- input- outputEncoding -.md
@@ -0,0 +1,39 @@
+---
+title: "md5( input, outputEncoding )"
+description: "Use MD5 to hash an input string."
+---
+Use [md5](https://golang.org/pkg/crypto/md5/) to hash an input string.
+
+| Parameter | Type | Description |
+| --------- |------|-------------|
+| input | string | The input string to hash. |
+| outputEncoding | string | Describes what type of encoding to use for the hash value. Can be "base64" or "hex". |
+
+
+### Returns
+
+| Type | Description |
+|------|-------------|
+| string | The string-encoded hash digest. |
+
+
+### Example
+
+
+
+```js
+import crypto from "k6/crypto";
+
+export default function() {
+ let hash = crypto.md5("hello world!", "hex");
+ console.log(hash);
+}
+```
+
+
+
+The above script should result in the following being printed during execution:
+
+```shell
+INFO[0000] fc3ff98e8c6a0d3087d515c0473f8677
+```
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto/ripemd160- input- outputEncoding -.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto/ripemd160- input- outputEncoding -.md
new file mode 100644
index 0000000000..a349241843
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto/ripemd160- input- outputEncoding -.md
@@ -0,0 +1,39 @@
+---
+title: "ripemd160( input, outputEncoding )"
+description: "Use RIPEMD-160 to hash an input string."
+---
+Use [ripemd160](https://godoc.org/golang.org/x/crypto/ripemd160) to hash an input string.
+
+| Parameter | Type | Description |
+| --------- |------|-------------|
+| input | string | The input string to hash. |
+| outputEncoding | string | Describes what type of encoding to use for the hash value. Can be "base64" or "hex". |
+
+
+### Returns
+
+| Type | Description |
+|------|-------------|
+| string | The string-encoded hash digest. |
+
+
+### Example
+
+
+
+```js
+import crypto from 'k6/crypto';
+
+export default function() {
+ let hash = crypto.ripemd160('hello world!', 'hex');
+ console.log(hash);
+}
+```
+
+
+
+The above script should result in the following being printed during execution:
+
+```shell
+INFO[0000] dffd03137b3a333d5754813399a5f437acd694e5
+```
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha1- input- outputEncoding -.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha1- input- outputEncoding -.md
new file mode 100644
index 0000000000..555e212832
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha1- input- outputEncoding -.md
@@ -0,0 +1,38 @@
+---
+title: "sha1( input, outputEncoding )"
+description: "Use SHA-1 to hash an input string."
+---
+
+Use [sha1](https://golang.org/pkg/crypto/sha1/) to hash an input string.
+
+| Parameter | Type | Description |
+| -------------- | ------ | ------------------------------------------------------------------------------------- |
+| input | string | The input string to hash. |
+| outputEncoding | string | Describes what type of encoding to use for the hash value. Can be "base64" or "hex". |
+
+### Returns
+
+| Type | Description |
+| ------ | ------------------------------- |
+| string | The string-encoded hash digest. |
+
+### Example
+
+
+
+```js
+import crypto from 'k6/crypto';
+
+export default function() {
+ let hash = crypto.sha1('hello world!', 'hex');
+ console.log(hash);
+}
+```
+
+
+
+The above script should result in the following being printed during execution:
+
+```shell
+INFO[0000] 430ce34d020724ed75a196dfc2ad67c77772d169
+```
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha256- input- outputEncoding -.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha256- input- outputEncoding -.md
new file mode 100644
index 0000000000..9ed032f02e
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha256- input- outputEncoding -.md
@@ -0,0 +1,38 @@
+---
+title: "sha256( input, outputEncoding )"
+description: "Use SHA-256 to hash an input string."
+---
+
+Use [sha256](https://golang.org/pkg/crypto/sha256/) to hash an input string.
+
+| Parameter | Type | Description |
+| -------------- | ------ | ------------------------------------------------------------------------------------- |
+| input | string | The input string to hash. |
+| outputEncoding | string | Describes what type of encoding to use for the hash value. Can be "base64" or "hex". |
+
+### Returns
+
+| Type | Description |
+| ------ | ------------------------------- |
+| string | The string-encoded hash digest. |
+
+### Example
+
+
+
+```js
+import crypto from 'k6/crypto';
+
+export default function() {
+ let hash = crypto.sha256('hello world!', 'hex');
+ console.log(hash);
+}
+```
+
+
+
+The above script should result in the following being printed during execution:
+
+```shell
+INFO[0000] 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9
+```
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha384- input- outputEncoding -.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha384- input- outputEncoding -.md
new file mode 100644
index 0000000000..45c46c228a
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha384- input- outputEncoding -.md
@@ -0,0 +1,38 @@
+---
+title: "sha384( input, outputEncoding )"
+description: "Use SHA-384 to hash an input string."
+---
+
+Use sha384 to hash an input string.
+
+| Parameter | Type | Description |
+| -------------- | ------ | ------------------------------------------------------------------------------------- |
+| input | string | The input string to hash. |
+| outputEncoding | string | Describes what type of encoding to use for the hash value. Can be "base64" or "hex". |
+
+### Returns
+
+| Type | Description |
+| ------ | ------------------------------- |
+| string | The string-encoded hash digest. |
+
+### Example
+
+
+
+```js
+import crypto from 'k6/crypto';
+
+export default function() {
+ let hash = crypto.sha384('hello world!', 'hex');
+ console.log(hash);
+}
+```
+
+
+
+The above script should result in the following being printed during execution:
+
+```shell
+INFO[0000] d33d40f7010ce34aa86efd353630309ed5c3d7ffac66d988825cf699f4803ccdf3f033230612f0945332fb580d8af805
+```
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha512- input- outputEncoding -.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha512- input- outputEncoding -.md
new file mode 100644
index 0000000000..b4be404541
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha512- input- outputEncoding -.md
@@ -0,0 +1,38 @@
+---
+title: 'sha512( input, outputEncoding )'
+description: "Use SHA-512 to hash an input string."
+---
+
+Use [sha512](https://golang.org/pkg/crypto/sha512/) to hash an input string.
+
+| Parameter | Type | Description |
+| -------------- | ------ | ------------------------------------------------------------------------------------- |
+| input | string | The input string to hash. |
+| outputEncoding | string | Describes what type of encoding to use for the hash value. Can be "base64" or "hex". |
+
+### Returns
+
+| Type | Description |
+| ------ | ------------------------------- |
+| string | The string-encoded hash digest. |
+
+### Example
+
+
+
+```js
+import crypto from 'k6/crypto';
+
+export default function() {
+ let hash = crypto.sha512('hello world!', 'hex');
+ console.log(hash);
+}
+```
+
+
+
+The above script should result in the following being printed during execution:
+
+```shell
+INFO[0000] db9b1cd3262dee37756a09b9064973589847caa8e53d31a9d142ea2701b1b28abd97838bb9a27068ba305dc8d04a45a1fcf079de54d607666996b3cc54f6b67c
+```
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha512_224- input- outputEncoding -.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha512_224- input- outputEncoding -.md
new file mode 100644
index 0000000000..446e8c1e47
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha512_224- input- outputEncoding -.md
@@ -0,0 +1,38 @@
+---
+title: "sha512_224( input, outputEncoding )"
+description: "Use SHA-512/224 to hash an input string."
+---
+
+Use [sha512_224](https://golang.org/pkg/crypto/sha512/) to hash an input string.
+
+| Parameter | Type | Description |
+| -------------- | ------ | ------------------------------------------------------------------------------------- |
+| input | string | The input string to hash. |
+| outputEncoding | string | Describes what type of encoding to use for the hash value. Can be "base64" or "hex". |
+
+### Returns
+
+| Type | Description |
+| ------ | ------------------------------- |
+| string | The string-encoded hash digest. |
+
+### Example
+
+
+
+```js
+import crypto from 'k6/crypto';
+
+export default function() {
+ let hash = crypto.sha512_224('hello world!', 'hex');
+ console.log(hash);
+}
+```
+
+
+
+The above script should result in the following being printed during execution:
+
+```shell
+INFO[0000] bc4ed196f7ba1c20f6fb6be1f91edf8293a35b065d6e7d6fd368c890
+```
diff --git a/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha512_256- input- outputEncoding -.md b/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha512_256- input- outputEncoding -.md
new file mode 100644
index 0000000000..8ff4a3cf3d
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/03 k6-crypto/sha512_256- input- outputEncoding -.md
@@ -0,0 +1,38 @@
+---
+title: "sha512_256( input, outputEncoding )"
+description: "Use SHA-512/256 to hash an input string."
+---
+
+Use [sha512_256](https://golang.org/pkg/crypto/sha512/) to hash an input string.
+
+| Parameter | Type | Description |
+| -------------- | ------ | ------------------------------------------------------------------------------------- |
+| input | string | The input string to hash. |
+| outputEncoding | string | Describes what type of encoding to use for the hash value. Can be "base64" or "hex". |
+
+### Returns
+
+| Type | Description |
+| ------ | ------------------------------- |
+| string | The string-encoded hash digest. |
+
+### Example
+
+
+
+```js
+import crypto from 'k6/crypto';
+
+export default function() {
+ let hash = crypto.sha512_256('hello world!', 'hex');
+ console.log(hash);
+}
+```
+
+
+
+The above script should result in the following being printed during execution:
+
+```shell
+INFO[0000] 595b5926068b4828fb1c27db21281e31118b8475cb6c3ceeb09be7b685414d5f
+```
diff --git a/src/data/markdown/docs/02 javascript api/04 k6-encoding.md b/src/data/markdown/docs/02 javascript api/04 k6-encoding.md
new file mode 100644
index 0000000000..b11cf98df9
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/04 k6-encoding.md
@@ -0,0 +1,5 @@
+---
+title: "k6/encoding"
+---
+The encoding module provides [base64](https://en.wikipedia.org/wiki/Base64)
+encoding/decoding as defined by [RFC4648](https://tools.ietf.org/html/rfc4648).
diff --git a/src/data/markdown/docs/02 javascript api/04 k6-encoding/b64decode- input- -encoding- -.md b/src/data/markdown/docs/02 javascript api/04 k6-encoding/b64decode- input- -encoding- -.md
new file mode 100644
index 0000000000..abf91e140a
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/04 k6-encoding/b64decode- input- -encoding- -.md
@@ -0,0 +1,38 @@
+---
+title: "b64decode( input, [encoding] )"
+description: "Base64 decode a string."
+---
+Decode the passed base64 encoded `input` string into the unencoded original string.
+
+| Parameter | Type | Description |
+|---------------------|--------|------------------------------|
+| input | string | The string to base64 decode. |
+| encoding (optional) | string | The base64 encoding to use. Available options are: - **"std"**: the standard encoding with `=` padding chars and `+` and `/` characters in encoding alphabet. This is the default. - **"rawstd"**: like `std` but without `=` padding characters. - **"url"**: URL safe version of `std`, encoding alphabet doesn't contain `+` and `/` characters, but rather `-` and `_` characters. - **"rawurl"**: like `url` but without `=` padding characters. |
+
+
+### Returns
+
+| Type | Description |
+|--------|---------------------------------------------------|
+| string | The base64 decoded version of the `input` string. |
+
+
+### Example
+
+
+
+```js
+import { check } from "k6";
+import encoding from "k6/encoding";
+
+export default function() {
+ let str = "hello world";
+ let enc = "aGVsbG8gd29ybGQ=";
+ check(null, {
+ "is encoding correct": () => encoding.b64encode(str) === enc,
+ "is decoding correct": () => encoding.b64decode(enc) === str
+ });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/04 k6-encoding/b64encode- input- -encoding- -.md b/src/data/markdown/docs/02 javascript api/04 k6-encoding/b64encode- input- -encoding- -.md
new file mode 100644
index 0000000000..207d6cf567
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/04 k6-encoding/b64encode- input- -encoding- -.md
@@ -0,0 +1,37 @@
+---
+title: "b64encode( input, [encoding] )"
+description: "Base64 encode a string."
+---
+
+| Parameter | Type | Description |
+|-----------|--------|------------------------------|
+| input | string | The string to base64 encode. |
+| encoding (optional) | string | The base64 encoding to use. Available options are: - **"std"**: the standard encoding with `=` padding chars and `+` and `/` characters in encoding alphabet. This is the default. - **"rawstd"**: like `std` but without `=` padding characters. - **"url"**: URL safe version of `std`, encoding alphabet doesn't contain `+` and `/` characters, but rather `-` and `_` characters. - **"rawurl"**: like `url` but without `=` padding characters. |
+
+
+### Returns
+
+| Type | Description |
+|--------|---------------------------------------------------|
+| string | The base64 encoded version of the `input` string. |
+
+
+### Example
+
+
+
+```js
+import { check } from "k6";
+import encoding from "k6/encoding";
+
+export default function() {
+ let str = "hello world";
+ let enc = "aGVsbG8gd29ybGQ=";
+ check(null, {
+ "is encoding correct": () => encoding.b64encode(str) === enc,
+ "is decoding correct": () => encoding.b64decode(enc) === str
+ });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html.md b/src/data/markdown/docs/02 javascript api/05 k6-html.md
new file mode 100644
index 0000000000..e55e056400
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html.md
@@ -0,0 +1,4 @@
+---
+title: "k6/html"
+---
+The k6/html module contains functionality for HTML parsing.
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection.md
new file mode 100644
index 0000000000..80bf6f885b
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection.md
@@ -0,0 +1,66 @@
+---
+title: 'Selection'
+category: 'k6api-class'
+description: 'A jQuery-like API for accessing HTML DOM elements.'
+---
+
+Represents a set of nodes in a DOM tree.
+
+Selections have a jQuery-compatible API, but with two caveats:
+
+- CSS and screen layout are not processed, thus calls like css() and offset() are unavailable.
+- DOM trees are read-only, you can't set attributes or otherwise modify nodes.
+
+(Note that the read-only nature of the DOM trees is purely to avoid a maintenance burden on code with seemingly no practical use - if a compelling use case is presented, modification can easily be implemented.)
+
+| Method | Description |
+| --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| [Selection.attr(name)](/javascript-api/k6-html/selection/selection-attr-name) | Get the value of an attribute for the first element in the Selection. |
+| [Selection.children([selector])](/javascript-api/k6-html/selection/selection-children-selector) | Get the children of each element in the set of matched elements, optionally filtered by a selector. |
+| [Selection.closest(selector)](/javascript-api/k6-html/selection/selection-closest-selector) | Get the first element that matches the selector by testing the element itself and traversing up through its ancestors |
+| [Selection.contents()](/javascript-api/k6-html/selection/selection-contents) | Get the children of each element in the set of matched elements, including text and comment nodes. |
+| [Selection.data([key])](/javascript-api/k6-html/selection/selection-data-key) | Return the value at the named data store for the first element in the set of matched elements. |
+| [Selection.each(fn)](/javascript-api/k6-html/selection/selection-each-fn) | Iterate and execute a function for each matched element. |
+| [Selection.eq(index)](/javascript-api/k6-html/selection/selection-eq-index) | Reduce the set of matched elements to the one at the specified index. |
+| [Selection.filter(selector)](/javascript-api/k6-html/selection/selection-filter-selector) | Reduce the set of matched elements to those that match the selector or pass the function's test. |
+| [Selection.find(selector)](/javascript-api/k6-html/selection/selection-find-selector) | Find the selection descendants, filtered by a selector. |
+| [Selection.first()](/javascript-api/k6-html/selection/selection-first) | Reduce the set of matched elements to the first in the set. |
+| [Selection.get(index)](/javascript-api/k6-html/selection/selection-get-index) | Retrieve the [Element (k6/html)](/javascript-api/k6-html/element) matched by the selector |
+| [Selection.has(selector)](/javascript-api/k6-html/selection/selection-has-selector) | Reduce the set of matched elements to those that have a descendant that matches the selector |
+| [Selection.html()](/javascript-api/k6-html/selection/selection-html) | Get the HTML contents of the first element in the set of matched elements |
+| [Selection.is(selector)](/javascript-api/k6-html/selection/selection-is-selector) | Check the current matched set of elements against a selector or element and return true if at least one of these elements matches the given arguments. |
+| [Selection.last()](/javascript-api/k6-html/selection/selection-last) | Reduce the set of matched elements to the final one in the set. |
+| [Selection.map(fn)](/javascript-api/k6-html/selection/selection-map-fn) | Pass each element in the current matched set through a function, producing a new Array containing the return values. |
+| [Selection.nextAll([selector])](/javascript-api/k6-html/selection/selection-nextall-selector) | Get all following siblings of each element in the set of matched elements, optionally filtered by a selector. |
+| [Selection.next([selector])](/javascript-api/k6-html/selection/selection-next-selector) | Get the immediately following sibling of each element in the set of matched element |
+| [Selection.nextUntil([selector], [filter])](/javascript-api/k6-html/selection/selection-nextuntil-selector-filter) | Get all following siblings of each element up to but not including the element matched by the selector. |
+| [Selection.not(selector)](/javascript-api/k6-html/selection/selection-not-selector) | Remove elements from the set of matched elements |
+| [Selection.parent([selector])](/javascript-api/k6-html/selection/selection-parent-selector) | Get the parent of each element in the current set of matched elements, optionally filtered by a selector. |
+| [Selection.parents([selector])](/javascript-api/k6-html/selection/selection-parents-selector) | Get the ancestors of each element in the current set of matched elements, optionally filtered by a selector. |
+| [Selection.parentsUntil([selector], [filter])](/javascript-api/k6-html/selection/selection-parentsuntil-selector-filter) | Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector. |
+| [Selection.prevAll([selector])](/javascript-api/k6-html/selection/selection-prevall-selector) | Get all preceding siblings of each element in the set of matched elements, optionally filtered by a selector. |
+| [Selection.prev([selector])](/javascript-api/k6-html/selection/selection-prev-selector) | Get the immediately preceding sibling of each element in the set of matched elements. |
+| [Selection.prevUntil([selector], [filter])](/javascript-api/k6-html/selection/selection-prevuntil-selector-filter) | Get all preceding siblings of each element up to but not including the element matched by the selector. |
+| [Selection.size()](/javascript-api/k6-html/selection/selection-size) | Return the number of elements in the Selection. |
+| [Selection.slice(start [, end])](/javascript-api/k6-html/selection/selection-slice-start-end) | Reduce the set of matched elements to a subset specified by a range of indices. |
+| [Selection.text()](/javascript-api/k6-html/selection/selection-text) | Get the text content of the selection. |
+| [Selection.toArray()](/javascript-api/k6-html/selection/selection-toarray) | Retrieve all the elements contained in the Selection, as an array. |
+| [Selection.val()](/javascript-api/k6-html/selection/selection-val) | Get the current value of the first element in the set of matched elements. |
+
+### Example
+
+
+
+```js
+import { parseHTML } from 'k6/html';
+import http from 'k6/http';
+
+export default function() {
+ const res = http.get('https://k6.io');
+ const doc = parseHTML(res.body);
+ const pageTitle = doc.find('head title').text();
+ const langAttr = doc.find('html').attr('lang');
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-attr-name-.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-attr-name-.md
new file mode 100644
index 0000000000..02600d99cd
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-attr-name-.md
@@ -0,0 +1,36 @@
+---
+title: "Selection.attr(name)"
+excerpt: ""
+---
+Get the value of an attribute for the first element in the Selection.
+Mimics [jquery.attr](https://api.jquery.com/attr/)
+
+
+| Parameter | Type | Description |
+| ----------| ---- | ----------- |
+| name | string | The name of the attribute to get |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| string | The value of the attribute |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import http from "k6/http";
+
+export default function() {
+ const res = http.get("https://k6.io");
+ const doc = parseHTML(res.body);
+ const langAttr = doc.find('html').attr('lang');
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-children--selector--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-children--selector--.md
new file mode 100644
index 0000000000..8a5e1b7c3e
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-children--selector--.md
@@ -0,0 +1,58 @@
+---
+title: "Selection.children([selector])"
+excerpt: ""
+---
+Get the children of each element in the set of matched elements, optionally filtered by a selector.
+Mimics [jquery.children](https://api.jquery.com/children/)
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector (optional) | string | A string containing a selector expression to match elements against. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | The children Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+ const sel = doc.find('dl');
+
+ console.log(sel.children().size());
+ console.log(sel.children('dt').size());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-closest-selector-.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-closest-selector-.md
new file mode 100644
index 0000000000..b98f9b3f8e
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-closest-selector-.md
@@ -0,0 +1,57 @@
+---
+title: "Selection.closest(selector)"
+excerpt: ""
+---
+For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
+Mimics [jquery.closest](https://api.jquery.com/closest/)
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector | string | A string containing a selector expression to match elements against |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ `;
+ const doc = parseHTML(content);
+
+ const sel = doc.find('li.item-a').closest('ul');
+ console.log(sel.attr('class'));
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-contents--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-contents--.md
new file mode 100644
index 0000000000..2f06e4bf51
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-contents--.md
@@ -0,0 +1,54 @@
+---
+title: "Selection.contents()"
+excerpt: ""
+---
+Get the children of each element in the set of matched elements, including text and comment nodes.
+Mimics [jquery.contents](https://api.jquery.com/contents/).
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+
+ const sel = doc.find('dt');
+
+ console.log(sel.contents().text());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-data--key--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-data--key--.md
new file mode 100644
index 0000000000..db5912656d
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-data--key--.md
@@ -0,0 +1,44 @@
+---
+title: "Selection.data([key])"
+excerpt: ""
+---
+Return the value at the named data store for the first element in the set of matched elements.
+Mimics [jquery.data](https://api.jquery.com/data/)
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| key (optional) | string | A string naming the piece of data to set. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| string | The value at the named data store. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+ const content = `
+
Hola
+ `;
+
+ const doc = parseHTML(content);
+ const sel = doc.find('h1');
+
+ console.log(sel.data().testID);
+ console.log(sel.data('test-id'));
+ console.log(sel.data('testId'));
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-each-fn-.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-each-fn-.md
new file mode 100644
index 0000000000..1943debc3b
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-each-fn-.md
@@ -0,0 +1,52 @@
+---
+title: "Selection.each(fn)"
+excerpt: ""
+---
+Iterate over a [Selection](/javascript-api/k6-html/selection), executing a function for each matched element.
+Mimics [jquery.each](https://api.jquery.com/each/)
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| fn | function | A function to iterate all the Elements of the Collection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+ doc.find('dl').each(function(idx, el) {
+ console.log(el.innerHTML());
+ });
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-eq-index-.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-eq-index-.md
new file mode 100644
index 0000000000..7a3e3c3e14
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-eq-index-.md
@@ -0,0 +1,62 @@
+---
+title: "Selection.eq(index)"
+excerpt: ""
+---
+Reduce the set of matched elements to the one at the specified index.
+Mimics [jquery.eq](https://api.jquery.com/eq/).
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| index | Number | An integer indicating the 0-based position of the element. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | A Selection. |
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+
+ const sel = doc.find('dt');
+
+ console.log(sel.eq(0).html());
+ console.log(sel.eq(1).html());
+ console.log(sel.eq(2).html());
+
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-filter-selector-.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-filter-selector-.md
new file mode 100644
index 0000000000..6759946836
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-filter-selector-.md
@@ -0,0 +1,71 @@
+---
+title: "Selection.filter(selector)"
+excerpt: ""
+---
+Reduce the set of matched elements to those that match the selector or pass the function's test.
+Mimics [jquery.filter](https://api.jquery.com/filter/)
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector | function | A function used as a test for each element in the set. |
+| selector | string | A string containing a selector expression to match elements against. |
+| selector | [Selection](/javascript-api/k6-html/selection) | A selection to match elements against. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | The filter selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+ let sel;
+ const els = doc.find('dl').children();
+
+ sel = els.filter('#term-2');
+ console.log(sel.text());
+
+ sel = els.filter(function(idx, el) {
+ return el.text() === 'definition 3-a';
+ });
+ console.log(sel.text());
+
+ sel = els.filter(doc.find('dl dt#term-1'));
+ console.log(sel.text());
+
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-find-selector-.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-find-selector-.md
new file mode 100644
index 0000000000..009910bf5b
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-find-selector-.md
@@ -0,0 +1,36 @@
+---
+title: 'Selection.find(selector)'
+excerpt: ''
+---
+
+Find the selection descendants, filtered by a selector. It returns a [Selection](/javascript-api/k6-html/selection) object.
+Mimics [jquery.find](https://api.jquery.com/find/)
+
+| Parameter | Type | Description |
+| --------- | ------ | -------------------------------------------------------------------- |
+| selector | string | A string containing a selector expression to match elements against. |
+
+### Returns
+
+| Type | Description |
+| --------------------------------------------- | ----------------- |
+| [Selection](/javascript-api/k6-html/selection) | Selection object. |
+
+### Example
+
+
+
+```js
+import { parseHTML } from 'k6/html';
+import http from 'k6/http';
+
+export default function() {
+ const res = http.get('https://k6.io');
+ const doc = parseHTML(res.body);
+
+ const titleDoc = doc.find('head title');
+ const title = titleDoc.text();
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-first--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-first--.md
new file mode 100644
index 0000000000..9ba84180d7
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-first--.md
@@ -0,0 +1,55 @@
+---
+title: "Selection.first()"
+excerpt: ""
+---
+Reduce the set of matched elements to the first in the set.
+Mimics [jquery.first](https://api.jquery.com/first/).
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | The first element of the Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+
+ const sel = doc.find('dt');
+
+ console.log(sel.first().html());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-get-index-.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-get-index-.md
new file mode 100644
index 0000000000..29a7a56263
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-get-index-.md
@@ -0,0 +1,60 @@
+---
+title: "Selection.get(index)"
+excerpt: ""
+---
+Retrieve the Element matched by the selector.
+Mimics [jquery.get](https://api.jquery.com/get/)
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| index | Number | A zero-based integer indicating which element to retrieve. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| Element | The Element matched by the selector. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+ const sel = doc.find('dl').children();
+
+ console.log(sel.get(0).innerHTML());
+ console.log(sel.get(1).innerHTML());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-has-selector-.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-has-selector-.md
new file mode 100644
index 0000000000..98dae44d15
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-has-selector-.md
@@ -0,0 +1,55 @@
+---
+title: "Selection.has(selector)"
+excerpt: ""
+---
+Reduce the set of matched elements to those that have a descendant that matches the selector.
+Mimics [jquery.has](https://api.jquery.com/has/).
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector | string | A string containing a selector expression to match elements against. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | A Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ list item 1
+ list item 2
+
+ list item 2-a
+ list item 2-b
+
+
+ list item 3
+ list item 4
+
+ `;
+ const doc = parseHTML(content);
+
+
+ const sel = doc.find('li').has('ul');
+
+ console.log(sel.html());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-html--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-html--.md
new file mode 100644
index 0000000000..b7caa8a559
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-html--.md
@@ -0,0 +1,55 @@
+---
+title: "Selection.html()"
+excerpt: ""
+---
+Get the HTML contents of the first element in the set of matched elements.
+Mimics [jquery.html](https://api.jquery.com/html/)
+
+### Returns
+
+
+| Type | Description |
+| ---- | ----------- |
+| string | The HTML content of the first element in the set of matched elements. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+
+ const sel = doc.find('dt');
+
+ console.log(sel.html());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-is-selector-.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-is-selector-.md
new file mode 100644
index 0000000000..4d240143b6
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-is-selector-.md
@@ -0,0 +1,74 @@
+---
+title: "Selection.is(selector)"
+excerpt: ""
+---
+Check the current matched set of elements against a selector or element and return true if at least one of these elements matches the given arguments.
+Mimics [jquery.is](https://api.jquery.com/is/)
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector | function | A function used as a test for each element in the set |
+| selector | string | A string containing a selector expression to match elements against. |
+| selector | [Selection](/javascript-api/k6-html/selection) | A selection. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | The filter selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+ let result;
+
+ const els = doc.find('dl').children();
+
+
+ result = els.is('dd');
+ console.log(result);
+
+ result = els.is(function(idx, el) {
+ return el.text() === 'hola';
+ });
+ console.log(result);
+
+
+ result = els.is(els.first());
+ console.log(result);
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-last--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-last--.md
new file mode 100644
index 0000000000..533ec9311a
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-last--.md
@@ -0,0 +1,54 @@
+---
+title: "Selection.last()"
+excerpt: ""
+---
+Reduce the set of matched elements to the final one in the set.
+Mimics [jquery.last](https://api.jquery.com/last/).
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | The final element of the Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+
+ const sel = doc.find('dt');
+
+ console.log(sel.last().html());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-map-fn-.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-map-fn-.md
new file mode 100644
index 0000000000..53e22f7461
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-map-fn-.md
@@ -0,0 +1,61 @@
+---
+title: "Selection.map(fn)"
+excerpt: ""
+---
+Pass each element in the current matched set through a function, producing a new Array containing the return values.
+Mimics [jquery.each](https://api.jquery.com/each/)
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| fn | function | A function to iterate all the Elements of the Collection. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| Array | The array containing the return values. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+ const newEls = doc.find('dl').children().map(function(idx, el) {
+ return 'hola ' + el.text();
+ });
+
+ console.log(newEls);
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-next--selector--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-next--selector--.md
new file mode 100644
index 0000000000..f882604283
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-next--selector--.md
@@ -0,0 +1,51 @@
+---
+title: "Selection.next([selector])"
+excerpt: ""
+---
+Get the immediately following sibling of each element in the set of matched elements
+Mimics [jquery.next](https://api.jquery.com/next/).
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector (optional) | string | A string containing a selector expression to match elements against. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | A Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ list item 1
+ list item 2
+ list item 3
+ list item 4
+ list item 5
+
+ `;
+ const doc = parseHTML(content);
+
+
+ const sel = doc.find('li.third-item').next();
+
+ console.log(sel.html());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-nextAll--selector--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-nextAll--selector--.md
new file mode 100644
index 0000000000..10adeaea23
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-nextAll--selector--.md
@@ -0,0 +1,49 @@
+---
+title: "Selection.nextAll([selector])"
+---
+Get all following siblings of each element in the set of matched elements, optionally filtered by a selector.
+Mimics [jquery.nextAll](https://api.jquery.com/nextAll/).
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector (optional) | string | A string containing a selector expression to match elements against |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | A Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ list item 1
+ list item 2
+ list item 3
+ list item 4
+ list item 5
+
+ `;
+ const doc = parseHTML(content);
+
+
+ const sel = doc.find('li.third-item').nextAll();
+
+ console.log(sel.size());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-nextUntil-selector-filter.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-nextUntil-selector-filter.md
new file mode 100644
index 0000000000..a0e285f77a
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-nextUntil-selector-filter.md
@@ -0,0 +1,60 @@
+---
+title: "Selection.nextUntil([selector], [filter])"
+---
+Get all following siblings of each element up to but not including the element matched by the selector.
+Mimics [jquery.nextUntil](https://api.jquery.com/nextUntil/)
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector (optional) | string \| [Selection](/javascript-api/k6-html/selection) \| `null` | A selector expression or object to match elements against. |
+| filter (optional) | string \| `null` | A selector expression to filter matched elements. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | A Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+ const sel = doc.find('#term-2').nextUntil('dt');
+ console.log(sel.size());
+
+ const selFilter = doc.find('#term-1').nextUntil('#term-3', 'dd');
+ console.log(selFilter.size());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-not-selector-.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-not-selector-.md
new file mode 100644
index 0000000000..e4eea17442
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-not-selector-.md
@@ -0,0 +1,66 @@
+---
+title: "Selection.not(selector)"
+excerpt: ""
+---
+Remove elements from the set of matched elements.
+Mimics [jquery.not](https://api.jquery.com/not/)
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector | string | A string containing a selector expression to match elements against. |
+| selector | function | A function used as a test for each element in the set. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | A Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+ let sel = doc.find('dl').children();
+
+
+ console.log(sel.not('dt').size());
+
+ sel = sel.not(function(idx, item) {
+ return item.text().startsWith('definition');
+ });
+
+ console.log(sel.size());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-parent--selector--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-parent--selector--.md
new file mode 100644
index 0000000000..40a7759c83
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-parent--selector--.md
@@ -0,0 +1,52 @@
+---
+title: "Selection.parent([selector])"
+excerpt: ""
+---
+Get the parent of each element in the current set of matched elements, optionally filtered by a selector.
+Mimics [jquery.parent](https://api.jquery.com/parent/).
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector (optional) | string | A string containing a selector expression to match elements against. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | A Selection. |
+
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ list item 1
+ list item 2
+ list item 3
+ list item 4
+ list item 5
+
+ `;
+ const doc = parseHTML(content);
+
+
+ const sel = doc.find('li.third-item').parent();
+
+ console.log(sel.html());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-parents--selector--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-parents--selector--.md
new file mode 100644
index 0000000000..e48d31097c
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-parents--selector--.md
@@ -0,0 +1,51 @@
+---
+title: "Selection.parents([selector])"
+excerpt: ""
+---
+Get the ancestors of each element in the current set of matched elements, optionally filtered by a selector.
+Mimics [jquery.parents](https://api.jquery.com/parents/).
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector (optional) | string | A string containing a selector expression to match elements against. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | A Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ list item 1
+ list item 2
+ list item 3
+ list item 4
+ list item 5
+
+ `;
+ const doc = parseHTML(content);
+
+
+ const sel = doc.find('li.third-item').parents();
+
+ console.log(sel.size());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-parentsUntil-selector-filter.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-parentsUntil-selector-filter.md
new file mode 100644
index 0000000000..84781202cc
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-parentsUntil-selector-filter.md
@@ -0,0 +1,60 @@
+---
+title: "Selection.parentsUntil([selector], [filter])"
+---
+Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector.
+Mimics [jquery.parentsUntil](https://api.jquery.com/parentsUntil/)
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector (optional) | string \| [Selection](/javascript-api/k6-html/selection) \| `null` | A selector expression or object to match elements against. |
+| filter (optional) | string \| `null` | A selector expression to filter matched elements. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | A Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+ const sel = doc.find('#term-2').parentsUntil('dt');
+ console.log(sel.size());
+
+ const selFilter = doc.find('#term-3').parentsUntil('dt', 'dd');
+ console.log(selFilter.size());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-prev--selector--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-prev--selector--.md
new file mode 100644
index 0000000000..c0b922f477
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-prev--selector--.md
@@ -0,0 +1,48 @@
+---
+title: "Selection.prev([selector])"
+excerpt: ""
+---
+Get the immediately preceding sibling of each element in the set of matched elements.
+Mimics [jquery.prev](https://api.jquery.com/prev/).
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector (optional) | string | A string containing a selector expression to match elements against. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | A Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ list item 1
+ list item 2
+ list item 3
+ list item 4
+ list item 5
+
+ `;
+ const doc = parseHTML(content);
+ const sel = doc.find('li.third-item').prev();
+
+ console.log(sel.html());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-prevAll--selector--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-prevAll--selector--.md
new file mode 100644
index 0000000000..dbeb404443
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-prevAll--selector--.md
@@ -0,0 +1,51 @@
+---
+title: "Selection.prevAll([selector])"
+excerpt: ""
+---
+Get all preceding siblings of each element in the set of matched elements, optionally filtered by a selector.
+Mimics [jquery.prevAll](https://api.jquery.com/prevAll/).
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector (optional) | string | A string containing a selector expression to match elements against. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | A Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ list item 1
+ list item 2
+ list item 3
+ list item 4
+ list item 5
+
+ `;
+ const doc = parseHTML(content);
+
+
+ const sel = doc.find('li.third-item').prevAll();
+
+ console.log(sel.size());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-prevUntil-selector-filter.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-prevUntil-selector-filter.md
new file mode 100644
index 0000000000..5e544f0737
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-prevUntil-selector-filter.md
@@ -0,0 +1,61 @@
+---
+title: "Selection.prevUntil([selector], [filter])"
+---
+Get all preceding siblings of each element up to but not including the element matched by the selector.
+Mimics [jquery.prevUntil](https://api.jquery.com/prevUntil/).
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| selector (optional) | string \| [Selection](/javascript-api/k6-html/selection) \| `null` | A selector expression or object to match elements against. |
+| filter (optional) | string \| `null` | A selector expression to filter matched elements. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | A Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+ const sel = doc.find('#term-2').prevUntil('dt');
+ console.log(sel.size());
+
+ const selFilter = doc.find('#term-3').prevUntil('#term-1', 'dd');
+ console.log(selFilter.size());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-serialize--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-serialize--.md
new file mode 100644
index 0000000000..a646e4b6e7
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-serialize--.md
@@ -0,0 +1,38 @@
+---
+title: "Selection.serialize()"
+excerpt: ""
+---
+Encode a set of form elements as a string in standard URL-encoded notation for submission.
+Mimics [jquery.serialize](https://api.jquery.com/serialize/)
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| string | The URL-encoded representation of the matched form or form elements. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+ const content = `
+
+ `;
+
+ const doc = parseHTML(content);
+ const sel = doc.find('form');
+ const serialized = sel.serialize();
+
+ console.log(serialized); // "username="
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-serializeArray--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-serializeArray--.md
new file mode 100644
index 0000000000..571ae1d3f5
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-serializeArray--.md
@@ -0,0 +1,39 @@
+---
+title: "Selection.serializeArray()"
+excerpt: ""
+---
+Encode a set of form elements as an array of names and values (`[{ name: "name", value: "value" }, ...]`).
+Mimics [jquery.serializeArray](https://api.jquery.com/serializeArray/)
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| array | Array of names and values of the matched form or form elements. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+ const content = `
+
+ `;
+
+ const doc = parseHTML(content);
+ const sel = doc.find('form');
+ const serialized = sel.serializeArray();
+
+ console.log(JSON.stringify(serialized)); // [{"name": "username", "value": ""}]
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-serializeObject--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-serializeObject--.md
new file mode 100644
index 0000000000..9754e5781a
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-serializeObject--.md
@@ -0,0 +1,37 @@
+---
+title: "Selection.serializeObject()"
+excerpt: ""
+---
+Encode a set of form elements as an object (`{ "inputName": "value", "checkboxName": "value" }`).
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| object | Object representation of the matched form or form elements, key is field name and value is field value. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+ const content = `
+
+ `;
+
+ const doc = parseHTML(content);
+ const sel = doc.find('form');
+ const serialized = sel.serializeObject();
+
+ console.log(JSON.stringify(serialized)); // {"username": ""}
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-size--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-size--.md
new file mode 100644
index 0000000000..7650edc8dc
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-size--.md
@@ -0,0 +1,55 @@
+---
+title: "Selection.size()"
+excerpt: ""
+---
+Return the number of elements in the Selection.
+Mimics [jquery.size](https://api.jquery.com/size/)
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| Number | The number of elements in the Selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+
+ const sel = doc.find('dt');
+
+ console.log(sel.size());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-slice-start -- end--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-slice-start -- end--.md
new file mode 100644
index 0000000000..5955b78b3a
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-slice-start -- end--.md
@@ -0,0 +1,65 @@
+---
+title: "Selection.slice(start [, end])"
+excerpt: ""
+---
+Reduce the set of matched elements to a subset specified by a range of indices.
+Mimics [jquery.slice](https://api.jquery.com/slice/)
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| start | Number | An integer indicating the 0-based position at which the elements begin to be selected. |
+| end | Number | An integer indicating the 0-based position at which the elements stop being selected. |
+| selector | [Selection](/javascript-api/k6-html/selection) | A selection. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | The new selection. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+ const els = doc.find('dl').children();
+
+ console.log(els.slice(4).text());
+
+
+ console.log(els.slice(2, 4).text());
+
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-text--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-text--.md
new file mode 100644
index 0000000000..acc990b025
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-text--.md
@@ -0,0 +1,31 @@
+---
+title: "Selection.text()"
+excerpt: ""
+---
+Get the text content of the Selection.
+Mimics [jquery.text](https://api.jquery.com/text/).
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| string | Selection text content. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import http from "k6/http";
+
+export default function() {
+ const res = http.get("https://k6.io");
+ const doc = parseHTML(res.body);
+ const pageTitle = doc.find('head title').text();
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-toArray--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-toArray--.md
new file mode 100644
index 0000000000..107796d59b
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-toArray--.md
@@ -0,0 +1,54 @@
+---
+title: "Selection.toArray()"
+excerpt: ""
+---
+Retrieve all the elements contained in the Selection, as an array.
+Mimics [jquery.toArray](https://api.jquery.com/toArray/).
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| Array of [Selection](/javascript-api/k6-html/selection) | Array of Selection objects. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+ term 1
+ definition 1-a
+ definition 1-b
+ definition 1-c
+ definition 1-d
+
+ term 2
+ definition 2-a
+ definition 2-b
+ definition 2-c
+
+ term 3
+ definition 3-a
+ definition 3-b
+
+ `;
+ const doc = parseHTML(content);
+
+ doc.find('dl').children().toArray().forEach(function(item) {
+ console.log(item.text());
+ });
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-val--.md b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-val--.md
new file mode 100644
index 0000000000..2727050e69
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/50 Selection/Selection-val--.md
@@ -0,0 +1,56 @@
+---
+title: "Selection.val()"
+excerpt: ""
+---
+Get the current value of the first element in the set of matched elements.
+Mimics [jquery.val](https://api.jquery.com/val/).
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| string | The value of the first element in the set of matched elements. |
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+
+
+ no
+ yes
+
+
+ no text
+ yes text
+
+
+ option 1
+ option 2
+ option 3
+
+
+ `;
+ const doc = parseHTML(content);
+
+ console.log(doc.find('#text_input').val());
+ console.log(doc.find('#select_one option[selected]').val());
+ console.log(doc.find('#select_one').val());
+ console.log(doc.find('#select_text').val());
+ console.log(doc.find('#select_multi').val());
+ console.log(doc.find('#textarea').val());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/Element -k6-html-.md b/src/data/markdown/docs/02 javascript api/05 k6-html/Element -k6-html-.md
new file mode 100644
index 0000000000..073927ae7f
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/Element -k6-html-.md
@@ -0,0 +1,162 @@
+---
+title: "Element"
+category: "k6api-class"
+description: "An HTML DOM element as returned by the Selection API."
+---
+Represents a DOM element matched by a [Selection](/javascript-api/k6-html/selection),
+and provides an API to inspect the element content.
+
+Use [Selection.get(index)](/javascript-api/k6-html/selection/selection-get-index) to return an Element object.
+
+The Element object provides a similar API to the [DOM Element API](https://developer.mozilla.org/en-US/Web/API/Element) to retrieve element information.
+
+| Method | Description |
+|------------------------|--------------------------------------------------------------------|
+| nodeName | The name of the element. |
+| nodeType | The type of the element. |
+| nodeValue | The element value. |
+| id | The id of the element. |
+| innerHTML | Is a DOMString representing the markup of the element's content. |
+| textContent | The element content. |
+| ownerDocument | [Element](/javascript-api/k6-html/element) |
+| attributes | An array of attributes. |
+| firstChild | [Element](/javascript-api/k6-html/element) |
+| lastChild | [Element](/javascript-api/k6-html/element) |
+| childElementCount | The number of children elements. |
+| firstElementChild | [Element](/javascript-api/k6-html/element) |
+| lastElementChild | [Element](/javascript-api/k6-html/element) |
+| previousSibling | [Element](/javascript-api/k6-html/element) |
+| nextSibling | [Element](/javascript-api/k6-html/element) |
+| previousElementSibling | [Element](/javascript-api/k6-html/element) |
+| nextElementSibling | [Element](/javascript-api/k6-html/element) |
+| parentElement | [Element](/javascript-api/k6-html/element) |
+| parentNode | [Element](/javascript-api/k6-html/element) |
+| childNodes | Array of [Element](/javascript-api/k6-html/element) |
+| children | Array of [Element](/javascript-api/k6-html/element) |
+| classList | An array of class names. |
+| className | The class name string |
+| lang | The value of the lang attribute. |
+| toString | The element string representation. |
+| hasAttribute | Boolean |
+| getAttribute | getAttributeNode |
+| hasAttributes | Boolean |
+| hasChildNodes | Boolean |
+| isSameNode | Boolean |
+| isEqualNode | Boolean |
+| getElementsByClassName | Return an array of [Element](/javascript-api/k6-html/element). |
+| getElementsByTagName | Return an array of [Element](/javascript-api/k6-html/element). |
+| querySelector | Returns the first [Element](/javascript-api/k6-html/element) which matches the specified selector string relative to the element |
+| querySelectorAll | Returns all the [Element](/javascript-api/k6-html/element) which matches the specified selector string relative to the element |
+| contains | |
+| matches | Returns a Boolean indicating whether or not the element would be selected by the specified selector string|
+| namespaceURI | The namespace URI of the element. |
+| isDefaultNamespace | Returns a Boolean indicating whether the element has the default namespace.|
+
+
+Additionally, Element can provide more methods depending on the Element type.
+
+- **AnchorElement**: hash, host, hostname, port, username, password, origin, pathname, protocol, relist, search, text.
+
+- **ButtonElement**: form, formAction, formEnctype, formMethod, formNoValidate, formTarget, labels, name, value.
+
+- **CanvasElement**: width, height
+
+- **DataListElement**: options
+
+- **FieldSetElement**: elements, type, form
+
+- **FormElement**: elements, length, method
+
+- **InputElement**: form
+
+- **LabelElement**: control, form
+
+- **LegendElement**: form
+
+- **LinkElement**: relList
+
+- **MapElement**: areas, images
+
+- **ObjectElement**: form
+
+- **OptionElement**: disabled, form, index, label, text, value
+
+- **OutputElement**: value, labels
+
+- **ProgressElement**: max, value, position
+
+- **ScriptElement**: text
+
+- **SelectElement**: form, length, options, selectedOptions, selectedIndex, value
+
+- **StyleElement**: text
+
+- **TableElement**: caption, thead, tbody, tfoot, rows
+
+- **TableCellElement**: cellIndex, colSpan, rowSpan, headers
+
+- **TableRowElement**: cells, colSpan, sectionRowIndex, rowIndex
+
+- **VideoElement**: textTracks
+
+- **TitleElement**: text
+
+
+### Example
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+ const content = `
+
+ Value term 1
+ Value term 2
+
+ `;
+ const sel = parseHTML(content).find('dl').children();
+
+ const el1 = sel.get(0);
+ const el2 = sel.get(1);
+
+ console.log(el1.nodeName());
+ console.log(el1.id());
+ console.log(el1.textContent());
+
+ console.log(el2.nodeName());
+ console.log(el2.id());
+ console.log(el2.textContent());
+
+ sleep(1);
+};
+```
+
+
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import {sleep} from "k6";
+
+export default function() {
+
+ const content = `
+\t
6
+ `;
+ const el = parseHTML(content).find('a').get(0);
+
+ console.log(el.nodeName());
+ console.log(el.innerHTML());
+ console.log(el.host());
+ console.log(el.hostname());
+ console.log(el.protocol());
+
+ sleep(1);
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/05 k6-html/parseHTML- src -.md b/src/data/markdown/docs/02 javascript api/05 k6-html/parseHTML- src -.md
new file mode 100644
index 0000000000..84a8c39204
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/05 k6-html/parseHTML- src -.md
@@ -0,0 +1,37 @@
+---
+title: "parseHTML( src )"
+description: "Parse an HTML string and populate a Selection object."
+---
+Parse an HTML string and populate a [Selection](/javascript-api/k6-html/selection) object.
+
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| src | string | HTML source. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Selection](/javascript-api/k6-html/selection) | A Selection object. |
+
+
+### Example
+
+
+
+
+```js
+import {parseHTML} from "k6/html";
+import http from "k6/http";
+
+export default function() {
+ const res = http.get("https://k6.io");
+ const doc = parseHTML(res.body); // equivalent to res.html()
+ const pageTitle = doc.find('head title').text();
+ const langAttr = doc.find('html').attr('lang');
+};
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http.md b/src/data/markdown/docs/02 javascript api/06 k6-http.md
new file mode 100644
index 0000000000..671d60548d
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http.md
@@ -0,0 +1,5 @@
+---
+title: 'k6/http'
+---
+
+The k6/http module contains functionality for performing HTTP transactions.
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/60 CookieJar.md b/src/data/markdown/docs/02 javascript api/06 k6-http/60 CookieJar.md
new file mode 100644
index 0000000000..ab7e856306
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/60 CookieJar.md
@@ -0,0 +1,34 @@
+---
+title: "CookieJar (k6/http)"
+category: "k6api-class"
+description: "Used for storing cookies, set by the server and/or added by the client."
+---
+
+_CookieJar_ is an object for storing cookies, set by the server and/or added by the client. As described in the how-to guide on using [Cookies](/using-k6/cookies), k6 handles cookies automatically by default. If you need more control over cookies you can however create your own cookie jar and select it as the active jar (instead of the default one created by k6) for one or more requests.
+
+| Method | Description |
+| ---------------- | ----------- |
+| [cookiesForURL(url)](/javascript-api/k6-http/cookiejar/cookiejar-cookiesforurl-url) | Get Object of cookies where the key is the cookie name and the value is an array. |
+| [set(name, value, [options])](/javascript-api/k6-http/cookiejar/cookiejar-set-name-value-options) | Set a cookie in the jar by specifying name, value and some other optional settings like domain, path etc. |
+
+
+### Example
+
+
+
+```js
+import http from "k6/http";
+import { check } from "k6";
+
+export default function() {
+ let res = http.get("https://httpbin.org/cookies/set?my_cookie=hello%20world", { redirects: 0 });
+ let jar = http.cookieJar();
+ let cookies = jar.cookiesForURL("http://httpbin.org/");
+ check(res, {
+ "has cookie 'my_cookie'": (r) => cookies.my_cookie.length > 0,
+ "cookie has correct value": (r) => cookies.my_cookie[0] === "hello world"
+ });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/60 CookieJar/CookieJar-cookiesForUrl-url-.md b/src/data/markdown/docs/02 javascript api/06 k6-http/60 CookieJar/CookieJar-cookiesForUrl-url-.md
new file mode 100644
index 0000000000..cd1e931869
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/60 CookieJar/CookieJar-cookiesForUrl-url-.md
@@ -0,0 +1,40 @@
+---
+title: 'CookieJar.cookiesForUrl(url)'
+excerpt: ''
+---
+
+| Parameter | Type | Description |
+| --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| url | string | The URL for which to get cookies. |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| object | Object of cookies where the key is the cookie name and the value is an array. |
+
+
+### Example
+
+
+
+```js
+import http from 'k6/http';
+import { check } from 'k6';
+
+export default function() {
+ let res = http.get(
+ 'https://httpbin.org/cookies/set?my_cookie=hello%20world',
+ { redirects: 0 },
+ );
+ let jar = http.cookieJar();
+ let cookies = jar.cookiesForURL('http://httpbin.org/');
+ check(res, {
+ "has cookie 'my_cookie'": r => cookies.my_cookie.length > 0,
+ 'cookie has correct value': r => cookies.my_cookie[0] === 'hello world',
+ });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/60 CookieJar/CookieJar-set-name- value- -options--.md b/src/data/markdown/docs/02 javascript api/06 k6-http/60 CookieJar/CookieJar-set-name- value- -options--.md
new file mode 100644
index 0000000000..6ffce5fd61
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/60 CookieJar/CookieJar-set-name- value- -options--.md
@@ -0,0 +1,40 @@
+---
+title: 'CookieJar.set(name, value, [options])'
+---
+
+Set a cookie in the jar by specifying name, value and some other optional settings like domain, path, etc.
+
+| Parameter | Type | Description |
+| --------- | -------- | ------------ |
+| name | string | Cookie name |
+| value | string | Cookie value |
+| options (optional) | object | Specific cookie settings: `domain`, `path`, `expires`, `max_age`, `secure` and `http_only`. |
+
+
+### Example
+
+
+
+```js
+import http from 'k6/http';
+import { check } from 'k6';
+
+export default function() {
+ let jar = http.cookieJar();
+ jar.set('https://httpbin.org/cookies', 'my_cookie', 'hello world', {
+ domain: 'httpbin.org',
+ path: '/cookies',
+ secure: true,
+ max_age: 600,
+ });
+ let res = http.get('https://httpbin.org/cookies');
+ check(res, {
+ 'has status 200': r => r.status === 200,
+ "has cookie 'my_cookie'": r => r.json().cookies.my_cookie !== null,
+ 'cookie has correct value': r =>
+ r.json().cookies.my_cookie == 'hello world',
+ });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/61 Response.md b/src/data/markdown/docs/02 javascript api/06 k6-http/61 Response.md
new file mode 100644
index 0000000000..5920e8dd20
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/61 Response.md
@@ -0,0 +1,74 @@
+---
+title: 'Response (k6/http)'
+category: 'k6api-class'
+description: 'Returned by the http.* methods that generate HTTP requests.'
+---
+
+Response is used by the http.\* methods that generate HTTP request. Those methods return one (or more, in the case of `http.batch()`) Response objects that contain HTTP response contents and performance timing measurements.
+
+Note that in the case of redirects, all the information in the Response object will pertain to the last request (the one that doesn't get redirected).
+
+| Name | Type | Description |
+| ------------------------------------------------------------------------------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `Response.body` | string | Response body content. See [Params.responseType](/javascript-api/k6-http/params-k6-http) and [options.discardResponseBodies](/using-k6/options) for how to discard the body when it's not needed (and to save memory) or when handling bodies with binary data. |
+| `Response.cookies` | object | Response cookies. The object properties are the cookie names and the value is an array of cookie objects (with `name`, `value`, `domain`, `path`, `httpOnly`, `secure`, `maxAge` and `expires` fields). |
+| `Response.error` | string | Error message if there was a non-HTTP error while trying to send the request. |
+| `Response.error_code` | number | [Error code](/javascript-api/error-codes) if there was a non-HTTP error or 4xx or 5xx HTTP error it will be set to a specific code that describes the error. (Added in 0.24.0) |
+| `Response.headers` | object | Key-value pairs representing all HTTP headers sent by the server. |
+| `Response.ocsp.produced_at` | number | If a stapled OSCP response was provided by server, the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC, representing the time when this OCSP stapled response was signed by CA (or by CA entrusted OCSP responder) |
+| `Response.ocsp.this_update` | number | If a stapled OSCP response was provided by server, the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC, representing the time at which the status being indicated was known to be correct. |
+| `Response.ocsp.next_update` | number | If a stapled OSCP response was provided by server, the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC, representing the time when this OCSP stapled response will be refreshed with CA (or by CA entrusted OCSP responder). |
+| `Response.ocsp.revocation_reason` | string | The reason for revocation of the certificate (if that is the status), one of the following constants: `http.OCSP_REASON_UNSPECIFIED`, `http.OCSP_REASON_KEY_COMPROMISE`, `http.OCSP_REASON_CA_COMPROMISE`, `http.OCSP_REASON_AFFILIATION_CHANGED`, `http.OCSP_REASON_SUPERSEDED`, `http.OCSP_REASON_CESSATION_OF_OPERATION`, `http.OCSP_REASON_CERTIFICATE_HOLD`, `http.OCSP_REASON_REMOVE_FROM_CRL`, `http.OCSP_REASON_PRIVILEGE_WITHDRAWN` or `http.OCSP_REASON_AA_COMPROMISE`. |
+| `Response.ocsp.revoked_at` | number | If a stapled OSCP response was provided by server, the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC, representing the time when this certificate was revoked (if that is the status). |
+| `Response.ocsp.status` | string | The status of the certificate, one of the following constants: `http.OCSP_STATUS_GOOD`, `http.OCSP_STATUS_REVOKED`, `http.OCSP_STATUS_UNKNOWN` or `http.OCSP_STATUS_SERVER_FAILED`. |
+| `Response.proto` | string | Protocol used to perform the transfer. Possible values are "HTTP/1.0", "HTTP/1.1", or "HTTP/2.0". |
+| `Response.remote_ip` | string | The IP address of the server handling the request. |
+| `Response.remote_port` | number | The port that was connected to on the server side. |
+| `Response.request.body` | string | Request body content. |
+| `Response.request.cookies` | object | Request cookies. The object properties are the cookie names and the value is an array of cookie objects (with `name`, `value` and `replace` fields). |
+| `Response.request.headers` | object | Request headers. |
+| `Response.request.method` | string | Request HTTP method. |
+| `Response.request.url` | string | Request URL. |
+| `Response.status` | number | HTTP status code returned by server. |
+| `Response.timings` | object | Performance timing information for the HTTP request. |
+| `Response.timings.blocked` | float | Containing time (ms) spent blocked before initiating request. |
+| `Response.timings.looking_up` (currently unavailable) | float | Containing time (ms) spent looking up host name in DNS. |
+| `Response.timings.connecting` | float | Containing time (ms) spent setting up TCP connection to host. |
+| `Response.timings.tls_handshaking` | float | Containing time (ms) spent handshaking TLS session with host. |
+| `Response.timings.sending` | float | Containing time (ms) spent sending request. |
+| `Response.timings.waiting` | float | Containing time (ms) spent waiting for server response. |
+| `Response.timings.receiving` | float | Containing time (ms) spent receiving response data. |
+| `Response.timings.duration` | float | Total time for the request (ms). It's equal to `sending + waiting + receiving`, i.e. how long did the remote server take to process the request and respond, without the initial DNS lookup/connection times. |
+| `Response.tls_cipher_suite` | string | If a TLS session was established, the cipher suite that was used. |
+| `Response.tls_version` | string | If a TLS session was established, the version of SSL/TLS that was used. |
+| `Response.url` | string | The URL that was ultimately fetched (i.e. after any potential redirects). |
+| `[Response.clickLink( [params] )](/javascript-api/k6-http/response/response-clicklink-params)` | function | Parses response as HTML, looks for a specific link and does the request-level equivalent of a click on that link. |
+| `Response.html()` | function | Returns an object that supports [Selection.find(selector)](/javascript-api/k6-html/selection/selection-find-selector). |
+| `Response.json([selector])` | function | Parses the response body data as JSON and returns a JS object or array. This call caches the deserialized JSON data, additional calls will return the cached data. An optional selector can be specified to extract a specific part of the data, see [here for selector syntax](https://github.com/tidwall/gjson#path-syntax). |
+| `[Response.submitForm( [params] )](/javascript-api/k6-http/response/response-submitform-params)` | function | Parses response as HTML, parses the specified form (defaults to looking for a "form" element) with option to override fields and then submits form taking form's `method` and `action` into account. |
+
+### Example
+
+
+
+```js
+import { check } from 'k6';
+import http from 'k6/http';
+
+export default function() {
+ let res = http.get('https://k6.io');
+ for (var p in res.headers) {
+ if (res.headers.hasOwnProperty(p)) {
+ console.log(p + ' : ' + res.headers[p]);
+ }
+ }
+ check(res, {
+ 'status is 200': r => r.status === 200,
+ 'caption is correct': r => r.html('h1').text() == 'Example Domain',
+ });
+}
+```
+
+
+
+_A k6 script that will make an HTTP request and print all HTTP response headers_
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/61 Response/Response-clickLink- -params- -.md b/src/data/markdown/docs/02 javascript api/06 k6-http/61 Response/Response-clickLink- -params- -.md
new file mode 100644
index 0000000000..b94d9aa5de
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/61 Response/Response-clickLink- -params- -.md
@@ -0,0 +1,37 @@
+---
+title: 'Response.clickLink( [params] )'
+excerpt: ''
+---
+
+Create and make a request corresponding to a link, found in the HTML of response, being clicked. By default it will look for the first `a` tag with a `href` attribute in the HTML, but this can be overridden using the `selector` option.
+
+This method takes an object argument where the following properties can be set:
+
+| Param | Type | Description |
+| -------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| selector | string | A selector string passed to [Selection.find(selector)](/javascript-api/k6-html/selection/selection-find-selector) to locate the link to click. By default this is `"a[href]"`. |
+| params | object | A [Params](/javascript-api/k6-http/params-k6-http) object that will be forwarded to the link click request. Can be used to set headers, cookies etc. |
+
+### Returns
+
+| Type | Description |
+| -------------------------------------------------------- | ----------------------- |
+| [Response](/javascript-api/k6-http/response-k6-http) | The link click response |
+
+### Example
+
+
+
+```js
+import http from 'k6/http';
+
+export default function() {
+ // Request page with links
+ let res = http.get('https://httpbin.org/links/10/0');
+
+ // Now, click the 4th link on the page
+ res = res.clickLink({ selector: 'a:nth-child(4)' });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/61 Response/Response-submitForm- -params- -.md b/src/data/markdown/docs/02 javascript api/06 k6-http/61 Response/Response-submitForm- -params- -.md
new file mode 100644
index 0000000000..0e35d058eb
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/61 Response/Response-submitForm- -params- -.md
@@ -0,0 +1,43 @@
+---
+title: 'Response.submitForm( [params] )'
+excerpt: ''
+---
+
+Fill in and submit form found in HTML of response. By default it will look for the first `form` tag in the HTML, but this can be overridden using the `formSelector` option. To set/override the form fields you set properties of an object in the `fields` option.
+
+This method takes an object argument where the following properties can be set:
+
+| Param | Type | Description |
+| -------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| formSelector | string | A selector string passed to [Selection.find(selector)](/javascript-api/k6-html/selection/selection-find-selector) to locate the form to fill in and submit. By default this is `"form"`. |
+| fields | object | The form fields to set/override. The keys are the form fields names and the values are the form field values. |
+| submitSelector | string | A selector string used to locate the submit button in the form. By default this is `'[type="submit"]'`. |
+| params | object | A [Params (k6/http)](/javascript-api/k6-http/params-k6-http) object that will be forwarded to the form submit request. Can be used to set headers, cookies etc. |
+
+### Returns
+
+| Type | Description |
+| ------------------------------------------------------------------ | ------------------------- |
+| [Response (k6/http)](/javascript-api/k6-http/response-k6-http) | The form submit response. |
+
+### Example
+
+
+
+```js
+import http from 'k6/http';
+
+export default function() {
+ // Request page containing a form
+ let res = http.get('https://httpbin.org/forms/post');
+
+ // Now, submit form setting/overriding some fields of the form
+ res = res.submitForm({
+ formSelector: '//form[@action="/post"]',
+ fields: { custname: 'test', extradata: 'test2' },
+ submitSelector: 'mySubmit',
+ });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/FileData -k6-http-.md b/src/data/markdown/docs/02 javascript api/06 k6-http/FileData -k6-http-.md
new file mode 100644
index 0000000000..fe69c54f78
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/FileData -k6-http-.md
@@ -0,0 +1,35 @@
+---
+title: "FileData (k6/http)"
+category: "k6api-class"
+description: "Used for wrapping data representing a file when doing multipart requests (file uploads)."
+---
+
+_FileData_ is an object for wrapping data representing a file when doing [multipart requests (file uploads)](/using-k6/multipart-requests-file-uploads). You create it by calling [http.file( data, [filename], [contentType] )](/javascript-api/k6-http/file-data-filename-contenttype).
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| FileData.data | string / bytes | The bytes representing the file contents. |
+| FileData.content_type | string | The content type that will be specified in the multipart request. |
+| FileData.filename | string | The filename that will be specified in the multipart request. |
+
+
+### Example
+
+
+
+```js
+import { sleep } from "k6";
+import { md5 } from "k6/crypto";
+import http from "k6/http";
+
+let binFile = open("/path/to/file.bin", "b");
+
+export default function() {
+ let f = http.file(binFile, "test.bin");
+ console.log(md5(f.data, "hex"));
+ console.log(f.filename);
+ console.log(f.content_type);
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/Params -k6-http-.md b/src/data/markdown/docs/02 javascript api/06 k6-http/Params -k6-http-.md
new file mode 100644
index 0000000000..bd0354e715
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/Params -k6-http-.md
@@ -0,0 +1,119 @@
+---
+title: 'Params (k6/http)'
+category: 'k6api-class'
+description: 'Used for setting various HTTP request-specific parameters such as headers, cookies, etc.'
+---
+
+_Params_ is an object used by the http.\* methods that generate HTTP requests. _Params_ contains request-specific options like e.g. HTTP headers that should be inserted into the request.
+
+| Name | Type | Description |
+| --------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `Params.auth` | string | The authentication method used for the request. It currently supports `digest`, `ntlm`, and `basic` authentication methods. |
+| `Params.cookies` | object | Object with key-value pairs representing request scoped cookies (they won't be added to VU cookie jar) `{cookies: { key: "val", key2: "val2" }}` You also have the option to say that a request scoped cookie should override a cookie in the VU cookie jar: `{cookies: { key: { value: "val", replace: true }}}` |
+| `Params.headers` | object | Object with key-value pairs representing custom HTTP headers the user would like to add to the request. |
+| `Params.jar` | object | http.CookieJar object to override default VU cookie jar with. Cookies added to request will be sourced from this jar and cookies set by server will be added to this jar. |
+| `Params.redirects` | number | The number of redirects to follow for this request. Overrides the global test option [`maxRedirects`](/using-k6/options). |
+| `Params.tags` | object | Key-value pairs where the keys are names of tags and the values are tag values. Response time metrics generated as a result of the request will have these tags added to them, allowing the user to filter out those results specifically, when looking at results data. |
+| `Params.timeout` | number | Request timeout to use in milliseconds. Default timeout is 60000ms(60 seconds). |
+| `Params.compression` | string | Sets whether the request body should be compressed. If set to `gzip` it will use gzip to compress the body and set the appropriate `Content-Length` and `Content-Encoding` headers. Possible values: `gzip`, `deflate`, `br`, `zstd`, and any comma-separated combination of them (for stacked compression) |
+| `Params.responseType` | string | ResponseType is used to specify how to treat the body of the response. The three options are: - text: k6 will return it as a string. This might not be what you want in cases of binary data as the conversation to UTF-16 will probably break it. This is also the default if [discardResponseBodies](/using-k6/options) is set to false or not set at all. - `binary`: k6 will return an array of ints/bytes - `none`: k6 will return null as the body. The whole body will be ignored. This is the default when [discardResponseBodies](/using-k6/options) is set to true. |
+
+### Example of custom HTTP headers and tags
+
+_A k6 script that will make an HTTP request with a custom HTTP header and tag results data with a specific tag_
+
+
+
+```js
+import http from 'k6/http';
+
+export default function() {
+ let params = {
+ cookies: { my_cookie: 'value' },
+ headers: { 'X-MyHeader': 'k6test' },
+ redirects: 5,
+ tags: { k6test: 'yes' },
+ };
+ let res = http.get('https://k6.io', params);
+}
+```
+
+
+
+### Example using http.batch() with Params
+
+Here is another example using [http.batch()](/javascript-api/k6-http/batch-requests) with a `Params` argument:
+
+
+
+```js
+import http from 'k6/http';
+
+let url1 = 'https://api.k6.io/v3/account/me';
+let url2 = 'http://httpbin.org/get';
+let apiToken =
+ 'f232831bda15dd233c53b9c548732c0197619a3d3c451134d9abded7eb5bb195';
+let requestHeaders = {
+ 'User-Agent': 'k6',
+ Authorization: 'Token ' + apiToken,
+};
+
+export default function() {
+ let res = http.batch([
+ { method: 'GET', url: url1, params: { headers: requestHeaders } },
+ { method: 'GET', url: url2 },
+ ]);
+}
+```
+
+
+
+### Example of Digest Authentication
+
+Here is one example of how to use the `Params` to Digest Authentication.
+
+
+
+```js
+import http from 'k6/http';
+import { check } from 'k6';
+
+export default function() {
+ // Passing username and password as part of URL plus the auth option will authenticate using HTTP Digest authentication
+ let res = http.get(
+ 'http://user:passwd@httpbin.org/digest-auth/auth/user/passwd',
+ { auth: 'digest' },
+ );
+
+ // Verify response
+ check(res, {
+ 'status is 200': r => r.status === 200,
+ 'is authenticated': r => r.json().authenticated === true,
+ 'is correct user': r => r.json().user === 'user',
+ });
+}
+```
+
+
+Example how to overwrite discardResponseBodies:
+
+### Example of overwrite discardResponseBodies
+
+
+
+```js
+import http from 'k6/http';
+
+export var options = { discardResponseBodies: true };
+export default function() {}
+export function setup() {
+ // Get 10 random bytes in as an array of ints/bytes, without the responseType the body will be null
+ const response = http.get('https://httpbin.org/bytes/10', {
+ responseType: 'binary',
+ });
+ // Print the array. Will look something like `176,61,15,66,233,98,223,196,43,1`
+ console.log(response.body);
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/batch- requests -.md b/src/data/markdown/docs/02 javascript api/06 k6-http/batch- requests -.md
new file mode 100644
index 0000000000..0f85e00b52
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/batch- requests -.md
@@ -0,0 +1,164 @@
+---
+title: "batch( requests )"
+description: "Issue multiple HTTP requests in parallel (like e.g. browsers tend to do)."
+---
+
+Batch multiple HTTP requests together, to issue them in parallel over multiple TCP connections.
+
+| Parameter | Type | Description |
+| --------- | -------------- | ---------------------------------------------------------------- |
+| requests | array \| object | An array or object containing requests, in string or object form |
+
+When each request is specified as an array, the order of the arguments for each request is as follows:
+
+
+### Returns
+
+| Type | Description |
+| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| object | An object containing [Response](/javascript-api/k6-http/response-k6-http) objects. It has integer keys when users pass an array as `requests` and string keys when named requests are used (see below). |
+
+
+| Position | Name | Type | Description |
+| -------- | ---- | ---- | ----------- |
+| 1 | method | string | Mandatory. The HTTP method of the request. One of GET, POST, PUT, PATCH, DELETE, HEAD or OPTION. |
+| 2 | url | string | Mandatory. The URL to request. |
+| 3 | body (optional) | string \| object | The body of the request if relevant. Can be set to `null` if not applicable but you want to set the last `params` argument. |
+| 4 | params (optional) | object | [Params](/javascript-api/k6-http/params-k6-http) like auth, custom headers and tags. |
+
+
+### Example with request as an array
+
+
+
+```js
+import http from 'k6/http';
+import { check } from 'k6';
+
+export default function() {
+ let responses = http.batch([
+ ['GET', 'https://test.k6.io', null, { tags: { ctype: 'html' } }],
+ [
+ 'GET',
+ 'https://test.k6.io/style.css',
+ null,
+ { tags: { ctype: 'css' } },
+ ],
+ [
+ 'GET',
+ 'https://test.k6.io/images/logo.png',
+ null,
+ { tags: { ctype: 'images' } },
+ ],
+ ]);
+ check(responses[0], {
+ 'main page status was 200': res => res.status === 200,
+ });
+}
+```
+
+
+
+### Example batching three URLs for parallel fetching
+
+
+
+```js
+import http from 'k6/http';
+import { check } from 'k6';
+
+export default function() {
+ let responses = http.batch([
+ ['GET', 'https://test.k6.io', null, { tags: { ctype: 'html' } }],
+ [
+ 'GET',
+ 'https://test.k6.io/style.css',
+ null,
+ { tags: { ctype: 'css' } },
+ ],
+ [
+ 'GET',
+ 'https://test.k6.io/images/logo.png',
+ null,
+ { tags: { ctype: 'images' } },
+ ],
+ ]);
+ check(responses[0], {
+ 'main page status was 200': res => res.status === 200,
+ });
+}
+```
+
+
+
+### Example with request objects
+
+You can also use objects to hold information about a request. Here is an example where we do that in order to send a POST request, plus use custom HTTP headers by adding a [Params](/javascript-api/k6-http/params-k6-http) object to the request:
+
+
+
+```js
+import http from 'k6/http';
+import { check } from 'k6';
+
+export default function() {
+ let req1 = {
+ method: 'GET',
+ url: 'https://httpbin.org/get',
+ };
+ let req2 = {
+ method: 'GET',
+ url: 'https://test.k6.io',
+ };
+ let req3 = {
+ method: 'POST',
+ url: 'https://httpbin.org/post',
+ body: {
+ hello: 'world!',
+ },
+ params: {
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ },
+ };
+ let responses = http.batch([req1, req2, req3]);
+ // httpbin.org should return our POST data in the response body, so
+ // we check the third response object to see that the POST worked.
+ check(responses[2], {
+ 'form data OK': res => JSON.parse(res.body)['form']['hello'] == 'world!',
+ });
+}
+```
+
+
+
+_Note that the requests in the example above may happen in any order, or simultaneously. There is no guarantee that e.g. req1 will happen before req2 or req3_
+
+### Example with named requests
+
+Finally, you can also send in named requests by using an object instead of an array as the parameter to http.batch(). In the following example we do this, and we also show that it is possible to mix string URLs and request objects
+
+
+
+```js
+import http from 'k6/http';
+import { check } from 'k6';
+
+export default function() {
+ let requests = {
+ 'front page': 'https://k6.io',
+ 'features page': {
+ method: 'GET',
+ url: 'https://k6.io/features',
+ params: { headers: { 'User-Agent': 'k6' } },
+ },
+ };
+ let responses = http.batch(requests);
+ // when accessing results, we use the name of the request as index
+ // in order to find the corresponding Response object
+ check(responses['front page'], {
+ 'front page status was 200': res => res.status === 200,
+ });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/cookieJar--.md b/src/data/markdown/docs/02 javascript api/06 k6-http/cookieJar--.md
new file mode 100644
index 0000000000..4f901d2deb
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/cookieJar--.md
@@ -0,0 +1,24 @@
+---
+title: "cookieJar()"
+description: "Get active HTTP Cookie jar."
+---
+
+Get the active cookie jar.
+
+| Type | Description |
+| ---------------------------------------------------------- | ------------------- |
+| [CookieJar](/javascript-api/k6-http/cookiejar-k6-http) | A CookieJar object. |
+
+### Example
+
+
+
+```js
+import http from 'k6/http';
+
+export default function() {
+ let jar = http.cookieJar();
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/del- url- -body-- -params- -.md b/src/data/markdown/docs/02 javascript api/06 k6-http/del- url- -body-- -params- -.md
new file mode 100644
index 0000000000..aaeb79ab10
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/del- url- -body-- -params- -.md
@@ -0,0 +1,18 @@
+---
+title: "del( url, [body], [params] )"
+description: "Issue an HTTP DELETE request."
+---
+
+Make a DELETE request.
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| url | string | Request URL (e.g. `http://example.com`). |
+| body (optional, discouraged) | string | object | Request body; objects will be `x-www-form-urlencoded`. This is discouraged, because sending a DELETE request with a body has [no defined semantics](https://tools.ietf.org/html/rfc7231#section-4.3.5) and may cause some servers to reject it. |
+| params (optional) | object | [Params](/javascript-api/k6-http/params-k6-http) object containing additional request parameters. |
+
+### Returns
+
+| Type | Description |
+| -------- | --------------------------------------------------------------------- |
+| Response | HTTP [Response](/javascript-api/k6-http/response-k6-http) object. |
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/file- data- -filename-- -contentType- -.md b/src/data/markdown/docs/02 javascript api/06 k6-http/file- data- -filename-- -contentType- -.md
new file mode 100644
index 0000000000..48293aaab4
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/file- data- -filename-- -contentType- -.md
@@ -0,0 +1,40 @@
+---
+title: "file( data, [filename], [contentType] )"
+description: "Create a file object that is used for building multi-part requests."
+---
+
+Create a file object that is used for building [Multipart requests (file uploads)](/using-k6/multipart-requests-file-uploads).
+
+| Parameter | Type | Description |
+| ----------- | -------------- | -------------------------------------------------------------------------------- |
+| data | string / bytes | An array or object containing requests, in string or object form. |
+| filename | string | The filename to specify for this field (or "part") of the multipart request. |
+| contentType | string | The content type to specify for this field (or "part") of the multipart request. |
+
+
+### Returns
+
+| Type | Description |
+| -------- | ------------------------------------------------------------------ |
+| [FileData](/javascript-api/k6-http/filedata-k6-http) | A FileData object. |
+
+### Example
+
+
+
+```js
+import { sleep } from 'k6';
+import { md5 } from 'k6/crypto';
+import http from 'k6/http';
+
+let binFile = open('/path/to/file.bin', 'b');
+
+export default function() {
+ let f = http.file(binFile, 'test.bin');
+ console.log(md5(f.data, 'hex'));
+ console.log(f.filename);
+ console.log(f.content_type);
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/get- url- -params- -.md b/src/data/markdown/docs/02 javascript api/06 k6-http/get- url- -params- -.md
new file mode 100644
index 0000000000..6635aadba0
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/get- url- -params- -.md
@@ -0,0 +1,32 @@
+---
+title: "get( url, [params] )"
+description: "Issue an HTTP GET request."
+---
+
+Make a GET request.
+
+| Parameter | Type | Description |
+| ----------------- | ------ | ----------------------------------------------------------------------------------------------------- |
+| url | string | Request URL (e.g. `http://example.com`). |
+| params (optional) | object | [Params](/javascript-api/k6-http/params-k6-http) object containing additional request parameters. |
+
+### Returns
+
+| Type | Description |
+| -------- | ----------- |
+| [Response](/javascript-api/k6-http/response-k6-http) | HTTP Response object. |
+
+
+### Example fetching a URL
+
+
+
+```js
+import http from 'k6/http';
+
+export default function() {
+ let res = http.get('https://k6.io');
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/options- url- -body-- -params- -.md b/src/data/markdown/docs/02 javascript api/06 k6-http/options- url- -body-- -params- -.md
new file mode 100644
index 0000000000..c4543cd4a8
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/options- url- -body-- -params- -.md
@@ -0,0 +1,17 @@
+---
+title: "options( url, [body], [params] )"
+description: "Issue an HTTP GET request."
+---
+
+| Parameter | Type | Description |
+| ----------------- | --------------- | ----------------------------------------------------------------------------------------------------- |
+| url | string | Request URL (e.g. `http://example.com`). |
+| body (optional) | string / object | Request body; objects will be `x-www-form-urlencoded`. |
+| params (optional) | object | [Params](/javascript-api/k6-http/params-k6-http) object containing additional request parameters. |
+
+
+### Returns
+
+| Type | Description |
+| -------- | --------------------------------------------------------------------- |
+| Response | HTTP [Response](/javascript-api/k6-http/response-k6-http) object. |
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/patch- url- -body-- -params- -.md b/src/data/markdown/docs/02 javascript api/06 k6-http/patch- url- -body-- -params- -.md
new file mode 100644
index 0000000000..8112a389c0
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/patch- url- -body-- -params- -.md
@@ -0,0 +1,17 @@
+---
+title: "patch( url, [body], [params] )"
+description: "Issue an HTTP PATCH request."
+---
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| url | string | Request URL (e.g. `http://example.com`). |
+| body (optional) | string / object | Request body; objects will be `x-www-form-urlencoded`. |
+| params (optional) | object | [Params](/javascript-api/k6-http/params-k6-http) object containing additional request parameters |
+
+
+### Returns
+
+| Type | Description |
+| ---- | ----------- |
+| [Response](/javascript-api/k6-http/response-k6-http) | HTTP Response object. |
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/post- url- -body-- -params- -.md b/src/data/markdown/docs/02 javascript api/06 k6-http/post- url- -body-- -params- -.md
new file mode 100644
index 0000000000..2978e62279
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/post- url- -body-- -params- -.md
@@ -0,0 +1,55 @@
+---
+title: 'post( url, [body], [params] )'
+description: 'Issue an HTTP POST request.'
+---
+
+| Parameter | Type | Description |
+| ------------------- | --------------- | ------------------------------------------------------------------------------------------------ |
+| `url` | string | Request URL (e.g. `http://example.com`). |
+| `body` | string / object | Request body; objects will be `x-www-form-urlencoded`. |
+| `params` (optional) | object | [Params](/javascript-api/k6-http/params-k6-http) object containing additional request parameters |
+
+### Returns
+
+| Type | Description |
+| ---------- | ----------------------------------------------------------------- |
+| `Response` | HTTP [Response](/javascript-api/k6-http/response-k6-http) object. |
+
+### Example
+
+Using http.post() to perform a user login at an E-commerce site:
+
+
+
+```js
+import http from 'k6/http';
+import { check, fail } from 'k6';
+
+export let options = { maxRedirects: 10 };
+
+const baseURL = 'https://dev-li-david.pantheonsite.io';
+
+export default function() {
+ // Fetch the login page, with the login HTML form
+ let res = http.get(baseURL + '/user/login');
+ // Extract hidden value needed to POST form
+ let formBuildID = res.body.match('name="form_build_id" value="(.*)"')[1];
+ // Create an Object containing the form data
+ let formdata = {
+ name: 'testuser1',
+ pass: 'testuser1',
+ form_build_id: formBuildID,
+ form_id: 'user_login',
+ op: 'Log in',
+ };
+ let headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
+ // Send login request
+ res = http.post(baseURL + '/user/login', formdata, { headers: headers });
+ // Verify that we ended up on the user page
+ check(res, {
+ 'login succeeded': res => res.url == `${baseURL}/users/testuser1`,
+ }) || fail('login failed');
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/put- url- -body-- -params- -.md b/src/data/markdown/docs/02 javascript api/06 k6-http/put- url- -body-- -params- -.md
new file mode 100644
index 0000000000..69e33092a9
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/put- url- -body-- -params- -.md
@@ -0,0 +1,17 @@
+---
+title: "put( url, [body], [params] )"
+description: "Issue an HTTP PUT request."
+---
+
+| Parameter | Type | Description |
+| ----------------- | ------ | ----------------------------------------------------------------------------------------------------- |
+| url | string | Request URL (e.g. `http://example.com`). |
+| body (optional) | string | object | Request body; objects will be `x-www-form-urlencoded`. |
+| params (optional) | object | [Params](/javascript-api/k6-http/params-k6-http) object containing additional request parameters. |
+
+
+### Returns
+
+| Type | Description |
+| -------- | --------------------------------------------------------------------- |
+| Response | HTTP [Response](/javascript-api/k6-http/response-k6-http) object. |
diff --git a/src/data/markdown/docs/02 javascript api/06 k6-http/request- method- url- -body-- -params- -.md b/src/data/markdown/docs/02 javascript api/06 k6-http/request- method- url- -body-- -params- -.md
new file mode 100644
index 0000000000..5fd5883b67
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/06 k6-http/request- method- url- -body-- -params- -.md
@@ -0,0 +1,57 @@
+---
+title: "request( method, url, [body], [params] )"
+description: "Issue any type of HTTP request."
+---
+
+| Parameter | Type | Description |
+| ----------------- | --------------- | ----------------------------------------------------------------------------------------------------- |
+| url | string | Request URL (e.g. `http://example.com`). |
+| body (optional) | string / object | Request body; objects will be `x-www-form-urlencoded`. |
+| params (optional) | object | [Params](/javascript-api/k6-http/params-k6-http) object containing additional request parameters. |
+
+
+### Returns
+
+| Type | Description |
+| -------- | --------------------------------------------------------------------- |
+| Response | HTTP [Response](/javascript-api/k6-http/response-k6-http) object. |
+
+### Example
+
+Using http.request() to issue a POST request, logging in to an e-commerce site:
+
+
+
+```js
+import http from 'k6/http';
+import { check, fail } from 'k6';
+
+export let options = { maxRedirects: 10 };
+
+const baseURL = 'https://dev-li-david.pantheonsite.io';
+
+export default function() {
+ // Fetch the login page, with the login HTML form
+ let res = http.get(baseURL + '/user/login');
+ // Extract hidden value needed to POST form
+ let formBuildID = res.body.match('name="form_build_id" value="(.*)"')[1];
+ // Create an Object containing the form data
+ let formdata = {
+ name: 'testuser1',
+ pass: 'testuser1',
+ form_build_id: formBuildID,
+ form_id: 'user_login',
+ op: 'Log in',
+ };
+ // Use http.request to send login POST request
+ res = http.request('POST', baseURL + '/usr/login', formdata, {
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ });
+ // Verify that we ended up on the user page
+ check(res, {
+ 'login succeeded': res => res.url == `${baseURL}/users/testuser1`,
+ }) || fail('login failed');
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/07 k6-metrics.md b/src/data/markdown/docs/02 javascript api/07 k6-metrics.md
new file mode 100644
index 0000000000..64dea311c8
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/07 k6-metrics.md
@@ -0,0 +1,45 @@
+---
+title: 'k6/metrics'
+excerpt: 'k6 Custom Metrics API'
+---
+
+The metrics module provides functionality to create [custom metrics](/using-k6/metrics) of various types. All metrics (both the built-in ones and the custom ones) have a type. There are four different metrics types, and they are: `Counter`, `Gauge`, `Rate` and `Trend`.
+
+All values added to a custom metric can optionally be [tagged](/using-k6/tags-and-groups) which can be very useful when analysing the results after a test run.
+
+| Metric type | Description |
+| ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
+| [Counter](/javascript-api/k6-metrics/counter-k6-metrics) | A metric that cumulatively sums added values. |
+| [Gauge](/javascript-api/k6-metrics/gauge-k6-metrics) | A metric that stores the min, max and last values added to it. |
+| [Rate](/javascript-api/k6-metrics/rate-k6-metrics) | A metric that tracks the percentage of added values that are non-zero. |
+| [Trend](/javascript-api/k6-metrics/trend-k6-metrics) | A metric that allows for calculating statistics on the added values (min, max, average and percentiles). |
+
+### Example
+
+
+
+```js
+import { Counter, Gauge, Rate, Trend } from 'k6/metrics';
+import { check } from 'k6';
+
+var myCounter = new Counter('my_counter');
+var myGauge = new Gauge('my_gauge');
+var myRate = new Rate('my_rate');
+var myTrend = new Trend('my_trend');
+
+export default function() {
+ myCounter.add(1);
+ myCounter.add(2, { tag1: 'myValue', tag2: 'myValue2' });
+
+ myGauge.add(123);
+ myGauge.add(456, { tag1: 'myValue', tag2: 'myValue2' });
+
+ myRate.add(true);
+ myRate.add(false, { tag1: 'value', tag2: 'value2' });
+
+ myTrend.add(1);
+ myTrend.add(2, { tag1: 'value', tag2: 'value2' });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/07 k6-metrics/70 Counter.md b/src/data/markdown/docs/02 javascript api/07 k6-metrics/70 Counter.md
new file mode 100644
index 0000000000..0a9e9acc41
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/07 k6-metrics/70 Counter.md
@@ -0,0 +1,96 @@
+---
+title: "Counter (k6/metrics)"
+---
+
+_Counter_ is an object for representing a custom cumulative counter metric. It is one of the four custom metric types.
+
+| Parameter | Type | Description |
+| --------- | ------- | ------------------------------ |
+| `name` | string | The name of the custom metric. |
+
+| Method | Description |
+| -------------------------------------------------------------------------------------------- | ---------------------------------- |
+| [Counter.add(value, [tags])](/javascript-api/k6-metrics/counter/counter-add-value-tags) | Add a value to the counter metric. |
+
+## Counter usage in Thresholds
+
+When `Counter` is used in a threshold expression, the variable must be called `count` or `rate` (lower case).
+For example:
+ - `count >= 200` // value of the counter must be larger or equal to 200
+ - `count < 10` // less than 10.
+
+### Examples
+
+
+
+```javaScript
+import { Counter } from 'k6/metrics';
+
+var myCounter = new Counter('my_counter');
+
+export default function() {
+ myCounter.add(1);
+ myCounter.add(3);
+}
+```
+
+
+
+
+
+```javaScript
+import http from "k6/http";
+import { Counter } from "k6/metrics";
+
+let CounterErrors = new Counter("Errors");
+
+export let options = { thresholds: { "Errors": ["count<100"] } };
+
+export default function() {
+ let res = http.get('https://test-api.k6.io/public/crocodiles/1/');
+ let contentOK = res.json("name") === 'Bert';
+ CounterErrors.add(!contentOK);
+};
+```
+
+
+
+
+
+```javaScript
+import { Counter } from 'k6/metrics';
+import { sleep } from 'k6';
+import http from 'k6/http';
+
+let allErrors = new Counter('error_counter');
+
+export let options = {
+ vus: 1,
+ duration: '1m',
+ thresholds: {
+ 'error_counter': [
+ 'count < 10', // 10 or fewer total errors are tolerated
+ ],
+ 'error_counter{errorType:authError}': [ // Threshold on a sub-metric (tagged values)
+ 'count <= 2', // max 2 authentication errors are tolerated
+ ]
+ }
+};
+
+export default function () {
+ let auth_resp = http.post('https://test-api.k6.io/auth/token/login/',
+ {username: 'test-user', 'password': 'supersecure'});
+
+ if (auth_resp.status >= 400){
+ allErrors.add(1, { errorType: 'authError' }); // tagged value creates submetric (useful for making thresholds specific)
+ }
+
+ let other_resp = http.get('https://test-api.k6.io/public/crocodiles/1/');
+ if (other_resp.status >= 400) {
+ allErrors.add(1); // untagged value
+ }
+
+ sleep(1);
+}
+```
+
diff --git a/src/data/markdown/docs/02 javascript api/07 k6-metrics/70 Counter/Counter-add-value- -tags--.md b/src/data/markdown/docs/02 javascript api/07 k6-metrics/70 Counter/Counter-add-value- -tags--.md
new file mode 100644
index 0000000000..a42948438d
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/07 k6-metrics/70 Counter/Counter-add-value- -tags--.md
@@ -0,0 +1,28 @@
+---
+title: "Counter.add(value, [tags])"
+---
+
+Add a value to the `Counter` metric.
+
+| Parameter | Type | Description |
+| --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| value | number | The value to add to the counter. |
+| tags | object | Set of [tags](/using-k6/tags-and-groups) that will be tagged to the added data point (note that tags are added per data point and not for the entire metric). |
+
+
+### Example
+
+
+
+```js
+import { Counter } from 'k6/metrics';
+
+var myCounter = new Counter('my_counter');
+
+export default function() {
+ myCounter.add(1);
+ myCounter.add(2, { tag1: 'myValue', tag2: 'myValue2' });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/07 k6-metrics/71 Gauge.md b/src/data/markdown/docs/02 javascript api/07 k6-metrics/71 Gauge.md
new file mode 100644
index 0000000000..15eaa133ff
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/07 k6-metrics/71 Gauge.md
@@ -0,0 +1,64 @@
+---
+title: 'Gauge (k6/metrics)'
+---
+
+_Gauge_ is an object for representing a custom metric holding only the latest value added. It is one of the four [custom metrics](/javascript-api/k6-metrics).
+
+| Parameter | Type | Description |
+| --------- | ------- | --------------------------------------------------------------------------------------------------- |
+| `name` | string | The name of the custom metric. |
+| `isTime` | boolean | A boolean indicating whether the values added to the metric are time values or just untyped values. |
+
+| Method | Description |
+| --------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
+| [Gauge.add(value, [tags])](/javascript-api/k6-metrics/gauge/gauge-add-value-tags) | Add a value to the gauge metric. Only the latest value added will be kept. |
+
+## Gauge usage in Thresholds
+
+When gauge is used in a threshold expression, the variable must be called `value` (lower case).
+For example:
+ - `value < 200`
+ - `value > 1`
+
+### Example
+
+
+
+```js
+import { Gauge } from 'k6/metrics';
+
+var myGauge = new Gauge('my_gauge');
+
+export default function() {
+ myGauge.add(3);
+ myGauge.add(1);
+ myGauge.add(2, { tag1: 'value', tag2: 'value2' });
+}
+```
+
+
+
+
+
+```js
+import http from "k6/http";
+import { sleep } from 'k6';
+import { Gauge } from "k6/metrics";
+
+let GaugeContentSize = new Gauge("ContentSize");
+
+export let options = {
+ thresholds: {
+ "ContentSize": ["value<4000"],
+ }
+};
+
+export default function() {
+ let res = http.get("https://test-api.k6.io/public/crocodiles/1/");
+ GaugeContentSize.add(res.body.length);
+ sleep(1);
+}
+
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/07 k6-metrics/71 Gauge/Gauge-add-value- -tags--.md b/src/data/markdown/docs/02 javascript api/07 k6-metrics/71 Gauge/Gauge-add-value- -tags--.md
new file mode 100644
index 0000000000..e308b3be45
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/07 k6-metrics/71 Gauge/Gauge-add-value- -tags--.md
@@ -0,0 +1,29 @@
+---
+title: "Gauge.add(value, [tags])"
+---
+
+Set the value of the `Gauge` metric.
+
+| Parameter | Type | Description |
+| --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| value | number | The value to set the gauge to. |
+| tags | object | Set of [tags](/using-k6/tags-and-groups) that will be tagged to the added data point (note that tags are added per data point and not for the entire metric). |
+
+
+### Example
+
+
+
+```js
+import { Gauge } from 'k6/metrics';
+
+var myGauge = new Gauge('my_gauge');
+
+export default function() {
+ myGauge.add(3);
+ myGauge.add(1);
+ myGauge.add(2, { tag1: 'value', tag2: 'value2' });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/07 k6-metrics/72 Rate.md b/src/data/markdown/docs/02 javascript api/07 k6-metrics/72 Rate.md
new file mode 100644
index 0000000000..5ca5609ab4
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/07 k6-metrics/72 Rate.md
@@ -0,0 +1,73 @@
+---
+title: 'Rate (k6/metrics)'
+---
+
+_Rate_ is an object for representing a custom metric keeping track of the percentage of added values that are non-zero. It is one of the four [custom metrics](/javascript-api/k6-metrics).
+
+| Parameter | Type | Description |
+| --------- | ------- | --------------------------------------------------------------------------------------------------- |
+| `name` | string | The name of the custom metric. |
+
+| Method | Description |
+| -------------------------------------------------------------------------------- | ------------------------------- |
+| [Rate.add(value, [tags])](/javascript-api/k6-metrics/rate/rate-add-value-tags) ] | Add a value to the rate metric. |
+
+
+## Rate usage in Thresholds
+
+When `Rate` is used in a threshold expression, the variable must be called `rate` (lower case).
+For example:
+ - `rate < 0.1` // less than 10%
+ - `rate >= 0.9` // more or equal to 90%
+
+The value of the `rate` variable ranges between `0.00` and `1.00`.
+
+### Examples
+
+
+
+```javaScript
+import { Rate } from 'k6/metrics';
+
+var myRate = new Rate('my_rate');
+
+export default function() {
+ myRate.add(true);
+ myRate.add(false);
+ myRate.add(1);
+ myRate.add(0, { tag1: 'value', tag2: 'value2' });
+}
+```
+
+
+
+
+
+
+```javaScript
+import { Rate } from 'k6/metrics';
+import { sleep } from 'k6';
+import http from 'k6/http';
+
+let errorRate = new Rate('errorRate');
+
+export let options = {
+ vus: 1,
+ duration: '5m',
+ thresholds: {
+ 'errorRate': [
+ // more than 10% of errors will abort the test
+ { threshold: 'rate < 0.1', abortOnFail: true, delayAbortEval: '1m' }
+ ]
+ }
+};
+
+export default function () {
+ let resp = http.get('https://test-api.k6.io/public/crocodiles/1/');
+
+ errorRate.add(resp.status >= 400);
+
+ sleep(1);
+}
+```
+
diff --git a/src/data/markdown/docs/02 javascript api/07 k6-metrics/72 Rate/Rate-add-value- -tags--.md b/src/data/markdown/docs/02 javascript api/07 k6-metrics/72 Rate/Rate-add-value- -tags--.md
new file mode 100644
index 0000000000..8abcafa4d7
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/07 k6-metrics/72 Rate/Rate-add-value- -tags--.md
@@ -0,0 +1,30 @@
+---
+title: "Rate.add(value, [tags])"
+---
+
+Set the value of the `Rate` metric.
+
+| Parameter | Type | Description |
+| --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| value | number | The value to add to the rate metric. |
+| tags | object | Set of [tags](/using-k6/tags-and-groups) that will be tagged to the added data point (note that tags are added per data point and not for the entire metric). |
+
+
+### Example
+
+
+
+```js
+import { Rate } from 'k6/metrics';
+
+var myRate = new Rate('my_rate');
+
+export default function() {
+ myRate.add(true);
+ myRate.add(false);
+ myRate.add(1);
+ myRate.add(0, { tag1: 'value', tag2: 'value2' });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/07 k6-metrics/73 Trend.md b/src/data/markdown/docs/02 javascript api/07 k6-metrics/73 Trend.md
new file mode 100644
index 0000000000..bcac287c00
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/07 k6-metrics/73 Trend.md
@@ -0,0 +1,86 @@
+---
+title: 'Trend (k6/metrics)'
+---
+
+_Trend_ is an object for representing a custom metric that allows for calculating different statistics on the added values (min, max, average or percentiles). It is one of the four [custom metrics](/javascript-api/k6-metrics).
+
+| Parameter | Type | Description |
+| --------- | ------- | --------------------------------------------------------------------------------------------------- |
+| `name` | string | The name of the custom metric. |
+| `isTime` | boolean | A boolean indicating whether the values added to the metric are time values or just untyped values. |
+
+| Method | Description |
+| --------------------------------------------------------------------------------- | -------------------------------- |
+| [Trend.add(value, [tags])](/javascript-api/k6-metrics/trend/trend-add-value-tags) | Add a value to the trend metric. |
+
+
+## Trend usage in Thresholds
+
+When `Trend` is used in a threshold expression, there are a range of variables that can be used.
+ - `avg` for average
+ - `min` for minimum
+ - `max` for maximum
+ - `med` for median
+ - `p(N)` for specific percentile. `N` is a number between `0.0` and `100.0` meaning the percentile value to look at, eg. `p(99.99)` means the 99.99th percentile.
+
+The unit of these variables and functions are all in milliseconds.
+
+### Example threshold expressions:
+
+ - `p(95) < 400` // 95% of requests must finish below 400ms
+ - `p(99) < 1000` // 99% of requests must finish within 1s.
+ - `p(50) < 200` // half of requests must finish within 200ms.
+ - `max < 3000` // the slowest request must finish within 3s.
+
+
+
+> ### ⚠️ Don't use `min` and `max` in thresholds.
+> We don't recommend using `min` and `max` for specifying thresholds because these
+> values represent outliers. Use percentiles instead.
+
+
+
+
+### Examples
+
+
+
+```javaScript
+import { Trend } from 'k6/metrics';
+
+var myTrend = new Trend('my_trend');
+
+export default function() {
+ myTrend.add(1);
+ myTrend.add(2, { tag1: 'value', tag2: 'value2' });
+}
+```
+
+
+
+
+
+```javaScript
+import { Trend } from 'k6/metrics';
+import { sleep } from 'k6';
+import http from 'k6/http';
+
+let serverWaitingTimeOnLogin = new Trend('serverWaitingTimeOnLogin', true);
+
+export let options = {
+ vus: 1,
+ duration: '1m',
+ thresholds: {
+ 'serverWaitingTimeOnLogin': [
+ 'p(95) < 200',
+ ],
+ }
+};
+
+export default function () {
+ let resp = http.post('https://test-api.k6.io/auth/token/login/', { username: 'test-user', 'password': 'supersecure' });
+
+ serverWaitingTimeOnLogin.add(resp.timings.waiting);
+ sleep(1);
+}
+```
\ No newline at end of file
diff --git a/src/data/markdown/docs/02 javascript api/07 k6-metrics/73 Trend/Trend-add-value- -tags--.md b/src/data/markdown/docs/02 javascript api/07 k6-metrics/73 Trend/Trend-add-value- -tags--.md
new file mode 100644
index 0000000000..22b5370bb6
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/07 k6-metrics/73 Trend/Trend-add-value- -tags--.md
@@ -0,0 +1,28 @@
+---
+title: "Trend.add(value, [tags])"
+---
+
+Add a value to the `Trend` metric.
+
+| Parameter | Type | Description |
+| --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| value | number | The value to add to the trend metric. |
+| tags | object | Set of [tags](/using-k6/tags-and-groups) that will be tagged to the added data point (note that tags are added per data point and not for the entire metric). |
+
+
+### Example
+
+
+
+```js
+import { Trend } from 'k6/metrics';
+
+var myTrend = new Trend('my_trend');
+
+export default function() {
+ myTrend.add(1);
+ myTrend.add(2, { tag1: 'value', tag2: 'value2' });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/08 k6-ws.md b/src/data/markdown/docs/02 javascript api/08 k6-ws.md
new file mode 100644
index 0000000000..c875fcdd94
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/08 k6-ws.md
@@ -0,0 +1,94 @@
+---
+title: "k6/ws"
+excerpt: "k6 WebSocket API"
+---
+
+The ws module provides a [WebSocket](https://en.wikipedia.org/wiki/WebSocket) client implementing the [WebSocket protocol](http://www.rfc-editor.org/rfc/rfc6455.txt).
+
+| Class | Description |
+| ----- | ----------- |
+| [Socket](/javascript-api/k6-ws/socket) | WebSocket client used to interact with a WS connection. |
+
+
+| Function | Description |
+| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [connect( url, params, callback )](/javascript-api/k6-ws/connect-url-params-callback) | Create a WebSocket connection, and provides a [Socket](/javascript-api/k6-ws/socket) client to interact with the service. The method returns a [Response](/javascript-api/k6-http/response-k6-http) object and blocks the test finalization until the connection is closed. |
+
+
+### WebSocket built-in metrics
+
+`k6` will automatically collect some metrics when interacting with a WebSocket service through the `k6/ws` API.
+
+| Metric name | Type | Description |
+| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
+| ws\_connecting | Trend | Total duration for the WebSocket connection request. |
+| ws\_session\_duration | Trend | Duration of the WebSocket session. Time between the start of the connection and the end of the VU execution. |
+| ws\_sessions | Counter | Total number of started WebSocket sessions. |
+| ws\_ping | Trend | Duration between a ping request and its pong reception |
+| ws\_msgs\_sent | Counter | Total number of messages sent through [Socket.send(data)](/javascript-api/k6-ws/socket/socket-send-data) |
+| ws\_msgs\_received | Counter | Total number of received messages [Socket.on('message', callback)](/javascript-api/k6-ws/socket/socket-on-event-callback). |
+
+Check out the [Results output article](/getting-started/results-output) for more information about how to process the metric information.
+
+### Example
+
+
+
+```js
+import ws from 'k6/ws';
+import { check } from 'k6';
+
+export default function() {
+ var url = 'ws://echo.websocket.org';
+ var params = { tags: { my_tag: 'hello' } };
+
+ var response = ws.connect(url, params, function(socket) {
+ socket.on('open', function open() {
+ console.log('connected');
+ socket.send(Date.now());
+
+ socket.setInterval(function timeout() {
+ socket.ping();
+ console.log('Pinging every 1sec (setInterval test)');
+ }, 1000);
+ });
+
+ socket.on('ping', function() {
+ console.log('PING!');
+ });
+
+ socket.on('pong', function() {
+ console.log('PONG!');
+ });
+
+ socket.on('pong', function() {
+ // Multiple event handlers on the same event
+ console.log('OTHER PONG!');
+ });
+
+ socket.on('message', function(message) {
+ console.log(`Received message: ${message}`);
+ });
+
+ socket.on('close', function() {
+ console.log('disconnected');
+ });
+
+ socket.on('error', function(e) {
+ if (e.error() != 'websocket: close sent') {
+ console.log('An unexpected error occured: ', e.error());
+ }
+ });
+
+ socket.setTimeout(function() {
+ console.log('2 seconds passed, closing the socket');
+ socket.close();
+ }, 2000);
+ });
+
+ check(response, { 'status is 101': r => r && r.status === 101 });
+}
+//VU execution won't be completely finished until the connection is closed.
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket.md b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket.md
new file mode 100644
index 0000000000..73e291faf1
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket.md
@@ -0,0 +1,14 @@
+---
+title: "Socket"
+---
+
+`Socket` is a WebSocket client to interact with a WebSocket connection. You can use it to listen various events happening on the WebSocket connection and send messages to the server. Additionally, you can use socket.setTimeout() and socket.setInterval() to execute code in the background, or repeatedly, while the WebSocket connection is open.
+
+| Method | Description |
+| ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [Socket.close()](/javascript-api/k6-ws/socket/socket-close) | Close the WebSocket connection. |
+| [Socket.on(event, callback)](/javascript-api/k6-ws/socket/socket-on-event-callback) | Set up an event listener on the connection for any of the following events:
- open
- message
- ping
- pong
- close
- error. |
+| [Socket.ping()](/javascript-api/k6-ws/socket/socket-ping) | Send a ping. |
+| [Socket.send(data)](/javascript-api/k6-ws/socket/socket-send-data) | Send data. |
+| [Socket.setInterval(callback, interval)](/javascript-api/k6-ws/socket/socket-setinterval-callback-interval) | Call a function repeatedly at certain intervals, while the connection is open. |
+| [Socket.setTimeout(callback, period)](/javascript-api/k6-ws/socket/socket-settimeout-callback-delay) | Call a function with a delay, if the connection is open. |
diff --git a/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-close--.md b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-close--.md
new file mode 100644
index 0000000000..4b742c7d0e
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-close--.md
@@ -0,0 +1,25 @@
+---
+title: "Socket.close()"
+---
+
+Close the WebSocket connection.
+
+
+### Example
+
+
+
+```js
+import ws from 'k6/ws';
+
+export default function() {
+ var url = 'ws://echo.websocket.org';
+ var response = ws.connect(url, null, function(socket) {
+ socket.on('open', function() {
+ socket.close();
+ });
+ });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-on-event- callback-.md b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-on-event- callback-.md
new file mode 100644
index 0000000000..4c32bfa171
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-on-event- callback-.md
@@ -0,0 +1,82 @@
+---
+title: "Socket.on(event, callback)"
+---
+
+Set up callback functions for various events on the WebSocket connection. Multiple handlers can be defined for the same event.
+
+
+| Parameter | Type | Description |
+| --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| event | string | The event name to define a callback for. |
+| callback | function | The function to call when the event happens. |
+
+| Event name | Description |
+| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| open | Emitted when the connection is established |
+| message | Emitted when a message is received from the server. |
+| ping | Emitted when a ping is received from the server. The client will automatically send back a `pong`. |
+| pong | Emitted when a pong is received from the server. |
+| close | Emitted when the connection is closed by the client [Socket.close()](/javascript-api/k6-ws/socket/socket-close) or when the server sends the `close` event with code status 1000 (normal closure). |
+| error | Emitted when an error occurs. Non-normal closure errors will be forwarded. |
+
+### Example
+
+
+
+```js
+import ws from 'k6/ws';
+import { check } from 'k6';
+
+export default function() {
+ var url = 'ws://echo.websocket.org';
+ var params = { tags: { my_tag: 'hello' } };
+
+ var response = ws.connect(url, params, function(socket) {
+ socket.on('open', function open() {
+ console.log('connected');
+ socket.send(Date.now());
+
+ socket.setInterval(function timeout() {
+ socket.ping();
+ console.log('Pinging every 1sec (setInterval test)');
+ }, 1000);
+ });
+
+ socket.on('ping', function() {
+ console.log('PING!');
+ });
+
+ socket.on('pong', function() {
+ console.log('PONG!');
+ });
+
+ socket.on('pong', function() {
+ // Multiple event handlers on the same event
+ console.log('OTHER PONG!');
+ });
+
+ socket.on('message', function(message) {
+ console.log(`Received message: ${message}`);
+ });
+
+ socket.on('close', function() {
+ console.log('disconnected');
+ });
+
+ socket.on('error', function(e) {
+ if (e.error() != 'websocket: close sent') {
+ console.log('An unexpected error occured: ', e.error());
+ }
+ });
+
+ socket.setTimeout(function() {
+ console.log('2 seconds passed, closing the socket');
+ socket.close();
+ }, 2000);
+ });
+
+ check(response, { 'status is 101': r => r && r.status === 101 });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-ping--.md b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-ping--.md
new file mode 100644
index 0000000000..6edaf7f305
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-ping--.md
@@ -0,0 +1,30 @@
+---
+title: "Socket.ping()"
+---
+
+Send a ping. Ping messages can be used to verify that the remote endpoint is responsive.
+
+
+### Example
+
+
+
+```js
+import ws from 'k6/ws';
+
+export default function() {
+ var url = 'ws://echo.websocket.org';
+ var response = ws.connect(url, null, function(socket) {
+ socket.on('open', function() {
+ socket.on('pong', function() {
+ // As required by the spec, when the ping is received, the recipient must send back a pong.
+ console.log('connection is alive');
+ });
+
+ socket.ping();
+ });
+ });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-send-data-.md b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-send-data-.md
new file mode 100644
index 0000000000..7dc1187b11
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-send-data-.md
@@ -0,0 +1,31 @@
+---
+title: "Socket.send(data)"
+---
+
+Send a data string through the connection. Binary data is not currently supported.
+You can use `JSON.stringify` to convert a JSON or JavaScript values to a JSON string.
+
+
+| Parameter | Type | Description |
+| --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| data | string | The data to send. |
+
+### Example
+
+
+
+```js
+import ws from 'k6/ws';
+
+export default function() {
+ var url = 'ws://echo.websocket.org';
+ var response = ws.connect(url, null, function(socket) {
+ socket.on('open', function() {
+ socket.send('my-message');
+ socket.send(JSON.stringify({ data: 'hola' }));
+ });
+ });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-setInterval-callback- interval-.md b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-setInterval-callback- interval-.md
new file mode 100644
index 0000000000..882e5a4142
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-setInterval-callback- interval-.md
@@ -0,0 +1,44 @@
+---
+title: "Socket.setInterval(callback, interval)"
+---
+
+Call a function repeatedly, while the WebSocket connection is open.
+
+
+| Parameter | Type | Description |
+| --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| callback | function | The function to call every `interval` milliseconds. |
+| interval | number | The number of milliseconds between two calls to `callback`. |
+
+### Example
+
+
+
+```js
+import ws from 'k6/ws';
+import { check } from 'k6';
+
+export default function() {
+ var url = 'ws://echo.websocket.org';
+ var params = { tags: { my_tag: 'hello' } };
+
+ var res = ws.connect(url, params, function(socket) {
+ socket.on('open', function open() {
+ console.log('connected');
+
+ socket.setInterval(function timeout() {
+ socket.ping();
+ console.log('Pinging every 1sec (setInterval test)');
+ }, 1000);
+ });
+
+ socket.on('pong', function() {
+ console.log('PONG!');
+ });
+ });
+
+ check(res, { 'status is 101': r => r && r.status === 101 });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-setTimeout-callback- delay-.md b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-setTimeout-callback- delay-.md
new file mode 100644
index 0000000000..9ed5e8546c
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/08 k6-ws/80 Socket/Socket-setTimeout-callback- delay-.md
@@ -0,0 +1,44 @@
+---
+title: "Socket.setTimeout(callback, delay)"
+---
+
+Call a function at a later time, if the WebSocket connection is still open then.
+
+
+| Parameter | Type | Description |
+| --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| callback | function | The function to call when `delay` has expired. |
+| delay | number | The delay time, in milliseconds. |
+
+
+### Example
+
+
+
+```js
+import ws from 'k6/ws';
+import { sleep } from 'k6';
+
+export default function() {
+ console.log('T0: Script started');
+ var url = 'ws://echo.websocket.org';
+ var response = ws.connect(url, null, function(socket) {
+ console.log('T0: Entered WebSockets run loop');
+ socket.setTimeout(function() {
+ console.log('T0+1: This is printed');
+ }, 1000);
+ socket.setTimeout(function() {
+ console.log('T0+2: Closing socket');
+ socket.close();
+ }, 2000);
+ socket.setTimeout(function() {
+ console.log('T0+3: This is not printed, because socket is closed');
+ }, 3000);
+ });
+ console.log('T0+2: Exited WebSockets run loop');
+ sleep(2);
+ console.log('T0+4: Script finished');
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/08 k6-ws/connect- url- params- callback -.md b/src/data/markdown/docs/02 javascript api/08 k6-ws/connect- url- params- callback -.md
new file mode 100644
index 0000000000..20bd2d4c8a
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/08 k6-ws/connect- url- params- callback -.md
@@ -0,0 +1,46 @@
+---
+title: "connect( url, params, callback )"
+description: "Create a WebSocket connection, and provides a Socket client to interact with the service."
+---
+
+Initiate a WebSocket connection to a remote host.
+
+Calling connect will block the VU finalization until the WebSocket connection is closed. Instead of continuously looping the main function (`export default function() { ... }`) over an over, each VU will be halted listening to async events and executing their event handlers until the connection is closed.
+
+The following events can close the connection:
+
+- remote host close event.
+- [Socket.close()](/javascript-api/k6-ws/socket/socket-close).
+- k6 VU interruption based on test configuration or CLI commands.
+
+| Parameter | Type | Description |
+| --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| url | string | Request URL (e.g. "ws://echo.websocket.org"). |
+| params | object | [Params](/javascript-api/k6-http/params-k6-http) object containing additional request parameters. |
+| callback | function | The callback function that will be called when the WebSocket connection is initiated. A [Socket](/javascript-api/k6-ws/socket) object will be passed to the function, and this object can be used to set up callbacks etc when things happen on the WebSocket connection |
+
+### Returns
+
+| Type | Description |
+| -------- | --------------------------------------------------------------------- |
+| [Response](/javascript-api/k6-http/response-k6-http) | HTTP Response object. |
+
+### Example
+
+
+
+```js
+import ws from 'k6/ws';
+
+export default function() {
+ var url = 'ws://echo.websocket.org';
+ var resp = ws.connect(url, null, function(socket) {
+ socket.on('open', function() {
+ console.log('WebSocket connection established!');
+ socket.close();
+ });
+ });
+}
+```
+
+
diff --git a/src/data/markdown/docs/02 javascript api/09 Error Codes.md b/src/data/markdown/docs/02 javascript api/09 Error Codes.md
new file mode 100644
index 0000000000..6cb427ce21
--- /dev/null
+++ b/src/data/markdown/docs/02 javascript api/09 Error Codes.md
@@ -0,0 +1,45 @@
+---
+title: 'Error Codes'
+excerpt: ''
+---
+
+Error codes, introduced in k6 0.24.0, are unique numbers that can be used to identify and handle different application and network errors more easily. For the moment, these error codes are applicable only for errors that happen during HTTP requests, but they will be reused and extended to support other protocols in future k6 releases.
+
+When an error occurs, its code is determined and returned as both the `error_code` field of the [`http.Response`](/javascript-api/k6-http/response-k6-http) object, and also attached as the `error_code` [tag](/using-k6/tags-and-groups) to any [metrics](/using-k6/metrics) associated with that request. Additionally, for more details, the `error` metric tag and `http.Response` field will still contain the actual string error message.
+
+Error codes for different errors are as distinct as possible, but for easier handling and grouping, codes in different error categories are also grouped in broad ranges. The current error code ranges are:
+
+- 1000-1099 - General errors
+- 1100-1199 - DNS errors
+- 1200-1299 - TCP errors
+- 1300-1399 - TLS errors
+- 1400-1499 - HTTP 4xx errors
+- 1500-1599 - HTTP 5xx errors
+- 1600-1699 - HTTP/2 specific errors
+
+The following specific error codes are currently defined:
+
+- 1000: A generic error that isn't any of the ones listed below.
+- 1010: A non-TCP network error - this is a place holder there is no error currently known to trigger it.
+- 1100: A generic DNS error that isn't any of the ones listed below.
+- 1101: No IP for the provided host was found.
+- 1110: Blacklisted IP was resolved or a connection to such was tried to be established.
+- 1200: A generic TCP error that isn't any of the ones listed below.
+- 1201: A "broken pipe" on write - the other side has likely closed the connection.
+- 1202: An unknown TCP error - We got an error that we don't recognize but it is from the operating system and has `errno` set on it. The message in `error` includes the operation(write,read) and the errno, the OS, and the original message of the error.
+- 1210: General TCP dial error.
+- 1211: Dial timeout error - the timeout for the dial was reached.
+- 1212: Dial connection refused - the connection was refused by the other party on dial.
+- 1220: Reset by peer - the connection was reset by the other party, most likely a server.
+- 1300: General TLS error
+- 1310: Unknown authority - the certificate issuer is unknown.
+- 1311: The certificate doesn't match the hostname.
+- 1400 to 1499: error codes that correspond to the [HTTP 4xx status codes for client errors](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_errors)
+- 1500 to 1599: error codes that correspond to the [HTTP 5xx status codes for server errors](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_Server_errors)
+- 1600: A generic HTTP/2 error that isn't any of the ones listed below.
+- 1610: A general HTTP/2 GoAway error.
+- 1611 to 1629: HTTP/2 GoAway errors with the value of the specific [HTTP/2 error code](https://tools.ietf.org/html/rfc7540#section-7) added to 1611.
+- 1630: A general HTTP/2 stream error.
+- 1631 to 1649: HTTP/2 stream errors with the value of the specific [HTTP/2 error code](https://tools.ietf.org/html/rfc7540#section-7) added to 1631.
+- 1650: A general HTTP/2 connection error.
+- 1651 to 1669: HTTP/2 connection errors with the value of the specific [HTTP/2 error code](https://tools.ietf.org/html/rfc7540#section-7) added to 1651.
diff --git a/src/data/markdown/docs/03 cloud/01 Creating and running a test/01 Test Builder.md b/src/data/markdown/docs/03 cloud/01 Creating and running a test/01 Test Builder.md
new file mode 100644
index 0000000000..f25304f051
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/01 Creating and running a test/01 Test Builder.md
@@ -0,0 +1,83 @@
+---
+title: "Test Builder"
+excerpt: "Details on using the k6 Request Builder to generate test scripts and take the guess work out of scripting"
+---
+
+## Background
+
+The k6 Test Builder allows you to utilize a graphical interface to create a test script.
+Based on your inputs, we will automatically generate the proper required JavaScript to
+execute the test within the app or from the command line. Use the Test Builder to help
+speed up your scripting efforts.
+
+> ### HAR Import
+>
+> The test builder also accepts importing a HAR file. When creating your HAR file, you
+> should filter out third party requests and be mindful of your session length. Too many
+> requests as a result of a long journey or third party requests will be overwhelming.
+
+## Test Builder Configuration
+
+The top configuration section allows to:
+
+- `Create` (save) or `Create and Run` your test
+- Give your test a meaningful name
+- Import a HAR file
+- Configure ramping, VUs, and duration
+
+To input a HAR file, simply click on `IMPORT HAR` on the right, and select your file to be converted.
+We will automatically populate the Test Builder with your requests, including any `Headers` sent.
+You are able to modify/delete various parts of the requests to meet your requirements.
+
+
+
+
+## Test Builder Requests
+
+Whether you are importing a HAR file or starting from a blank slate, the `REQUESTS` section
+allows you to explicitly control requests, the data sent with them, and even save data from the response.
+
+Requests will be listed in order on the left. You can reorganize requests by clicking and dragging.
+You can also duplicate or delete requests when hovering over a specific request.
+To add a new request, click `ADD REQUEST`. Your test will execute in the order of these requests.
+
+To modify requests, move over to the right side of the `REQUESTS` section. You are able to:
+
+
+- Give your request a name to better describe it.
+- Change the `HTTP METHOD` by using the drop down prepopulated with `GET`.
+- Change the URL/Endpoint (This is predefine as `http://test.k6.io/` for example purposes)
+- Specify Headers (If you have imported a HAR file, we will include some Headers here)
+- Specify Query Parameters
+- Create Checks
+- Capture a variable (Helpful for dealing with dynamic data, such as authentication tokens)
+- For POST/PUT/PATCH, you can also specify a request body (JSON, Text, or File Content)
+
+> ### Examples
+>
+> We also include some examples of common actions. This is accessible by using the
+> `Test builder examples` drop down in the title bar of the section. Use these for
+> inspiration and guidance as you use the test builder.
+>
+>
+> **NOTE:** Choosing one of the examples will replace the current requests in the Test Builder.
+
+
+
+## Test Builder Script
+
+After you have completed building your requests. You can click on the `>` in the
+top right corner of the title bar to view the script we have generated for you.
+
+This script is syntactically correct and may be used run right from the web app.
+You may wish to copy it to your local IDE to place in version control, parameterize data,
+or add more business logic. It's highly recommended to add some Thresholds.
+
+If this is your first time using the Test Builder or k6. We recommend taking a moment to
+familiarize yourself with the generated script and how the different parts relate to inputs.
+For example, the option object reflects your configuration, names of requests are comments above the
+actual requests, checks are implemented with requests, and more.
+
+## See Also
+
+- [Projects](/cloud/analyzing-results/analysis-tab)
diff --git a/src/data/markdown/docs/03 cloud/01 Creating and running a test/02 In-app script editor.md b/src/data/markdown/docs/03 cloud/01 Creating and running a test/02 In-app script editor.md
new file mode 100644
index 0000000000..b71433fd8f
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/01 Creating and running a test/02 In-app script editor.md
@@ -0,0 +1,34 @@
+---
+title: "In-app script editor"
+excerpt: "How to use the k6 in-app script editor"
+---
+
+
+
+> ### Limitations of the in-app editor
+>
+> The in-app editor has access to all built-in k6 modules and remote modules available via
+> the web. If you need to import a custom library or file for parameterization, you
+> must trigger your test using the CLI.
+
+
+
+## Background
+
+The in-app script is designed to allow you to quickly write and mock up test scripts right within the web app. We believe most users will quickly graduate to using k6 as a CLI and their IDE of choice locally. However, organizational security rules vary from organization to organization and some may not be able to install a program without review from their security team. The script editor allows you to utilize just about all features of k6 right within the web app.
+
+Use the script editor as a stepping stone, if needed, before you move to running tests from the command line.
+
+## Usage
+
+Usage of the script editor is straight forward and you can use it like any other editor. Note some of the built in functionality to aide you in scripting
+
+
+- Built in examples
+ - Clicking on any example from the drop down will launch a side by side window for you to use as guidance to adapt your script.
+- Validation/error checking
+ - If you have a syntactical error, we will alert you of that/prevent you from running the script. If you have such an error you will see `There are validation errors, please fix them in order to run the test` above the example drop down.
+- Code Folding
+ - We allow you to fold/collapse blocks of code for easier reading. You can fold by using the down arrow next to the line numbers.
+- Variable highlighting
+ - Selecting any variable will result in other references to that variable being highlighted in your script
diff --git a/src/data/markdown/docs/03 cloud/01 Creating and running a test/03 Recording a test script.md b/src/data/markdown/docs/03 cloud/01 Creating and running a test/03 Recording a test script.md
new file mode 100644
index 0000000000..cd02627e7c
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/01 Creating and running a test/03 Recording a test script.md
@@ -0,0 +1,45 @@
+---
+title: 'Recording a test script'
+excerpt: 'A guide on how to use the k6 Cloud Chrome extension to record user behavior to quickly create load test scripts.'
+---
+
+## Background
+
+
+The k6 Cloud Test Script Recorder Chrome extension allows you to generate the bulk of your test scripts simply by browsing like a user would on your site or web app. The script created gives you a foundation which you can further edit, as required.
+
+The k6 Cloud Chrome extension will capture everything – every single HTTP(s) request being loaded into the browser as you click – including ads, images, documents, etc., so you get a far more accurate read of what’s going on. Just press “record”, start browsing and when complete, the script will automatically upload to your k6 Cloud account.
+
+>### Consider this:
+> The Chrome extension will not record other tabs or pop up windows. If you need to capture this information, you should check out [converting from a HAR file](/using-k6/session-recording-har-support).
+
+*Note:* Before you begin, please be sure to force refresh the k6 Cloud app to ensure you are on the most recent version of the k6 Cloud app.
+
+## Here's how to start:
+
+ 1. Download and install the
k6 Browser Recorder .
+
+ 2. **Start a recording**
+ Open the extension by clicking the k6 logo, and press "Start recording" to begin recording the current browser tab. Now browse like a user would or how you want our Virtual Users to execute. We suggest basing this on _real user behavior_ - don't try to visit every single page on your site or app. Focus on common journeys.
+
+
+ 3. **Stop recording**
+ When done, press "Stop recording", you'll be taken to the app to review the recorded test script
+
+
+ 4. **Save your test scripts**
+ Save the recorded script in any of your projects.
+ If any _third party requests_ or requests to download assets were made during the recording those requests will be filtered out by default.
+ Would you want to include some of the requests in the _third party list_ simply deselect the ones you want to include then hit save.
+
+
+ 5. **You can now edit your script as necessary.** k6 Cloud's in app IDE will update in real time to alert you of any syntax errors. You may need to edit your script to deal with CSRF tokens, adding advanced business logic, creating custom metrics, etc.
+
+ 6. **Once done, press run to start your test**
+
+
+> ## Important things to note
+>
+> - The default configuration will be a 12 minute test that ramps to 10 Virtual Users over 1 minute, stays at 10 for 10 minutes, then ramps back to 0 over 1 minute. You can change this in the stages section of the script. Refer to [this article](/test-types/introduction) for more information on ramping configurations.
+> - No load zone is specified and it will run out of Ashburn by default. You can specify different load zones by adding a `ext.loadimpact.distribution` option. See [this article](/using-k6/options) for more information
+> - We have set `discardResponseBodies: true`. This will discard all response bodies by default.
diff --git a/src/data/markdown/docs/03 cloud/01 Creating and running a test/04 Converters.md b/src/data/markdown/docs/03 cloud/01 Creating and running a test/04 Converters.md
new file mode 100644
index 0000000000..46e27f4204
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/01 Creating and running a test/04 Converters.md
@@ -0,0 +1,53 @@
+---
+title: 'Converters'
+redirect: 'https://k6.io/docs/integrations#converters'
+---
+
diff --git a/src/data/markdown/docs/03 cloud/01 Creating and running a test/05 Scheduling a test.md b/src/data/markdown/docs/03 cloud/01 Creating and running a test/05 Scheduling a test.md
new file mode 100644
index 0000000000..42f6e54f2a
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/01 Creating and running a test/05 Scheduling a test.md
@@ -0,0 +1,29 @@
+---
+title: 'Scheduling Tests'
+excerpt: 'A guide on how to schedule your load tests to run in the future or on a schedule within the k6 Cloud Web UI.'
+---
+
+## Background
+
+It's not always feasible to be able to trigger a test to run when you need it. The scheduling option in k6 Cloud allows you to configure a test to execute at a particular time, and on a regular interval, if needed.
+
+Some reasons include, but are not limited to:
+- You need to test a production system and want to do it during hours with minimal usage
+- You want to build a performance trend to monitor for regressions (but aren't ready to integrate it as a step in a CI Pipeline
+
+
+## Scheduling
+
+You have the ability to schedule any tests that currently exists in your k6 Cloud account that has been executed on our cloud service (you can not schedule a locally run test through the web UI). You may also schedule tests that you create within the web UI after saving your configuration.
+
+You can schedule any of your tests from the page with the performance trending graph as shown below. This is helpful if you have triggered a cloud test from the command line and want to automatically run it regularly (without using a CI tool)
+
+
+
+## Scheduling options
+
+In both cases, after clicking "Schedule" you are presented with the following options. You are able to run a test now or at a later date. You can also set the execution to repeat on an Hourly, Daily, Weekly, or Monthly interval. You can also control how long the test will run for, either after a set number of occurrences, or after a certain date. There is some very granular control here, so do explore the option.
+
+
+
+Finally - we do recommend setting up [notifications](/cloud/integrations/notifications) and [thresholds](/using-k6/thresholds) to complete an automated loop. Schedule your test to run and get notified if the test has passed or failed.
diff --git a/src/data/markdown/docs/03 cloud/01 Creating and running a test/06 Running a test from the CLI.md b/src/data/markdown/docs/03 cloud/01 Creating and running a test/06 Running a test from the CLI.md
new file mode 100644
index 0000000000..cc6a8b1bd9
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/01 Creating and running a test/06 Running a test from the CLI.md
@@ -0,0 +1,47 @@
+---
+title: 'Cloud tests from the CLI'
+excerpt: 'How to run cloud tests from the k6 CLI.'
+---
+
+## Background
+
+Running tests within the web app is helpful when getting a feel for the tool or
+building a proof of concept. However, many users will find great flexibility
+when using k6 to trigger tests from the command line.
+
+Reasons for triggering cloud tests from the command line include:
+
+- Integrating testing in CI/CD pipelines
+- Storing test scripts in local version control
+- Modularization of scripts for collaboration and easier maintenance
+- Preference to work in your local environment
+
+
+## Quick Start to using the CLI for cloud tests
+
+Assuming you have k6 installed and a script saved locally, the first step would
+be to authenticate against the k6 Cloud, like so:
+
+
+
+```shell
+k6 login cloud
+```
+
+
+
+Alternatively, you could also get your token from the [API token
+page](https://app.k6.io/account/api-token) and set the
+environment variable `K6_CLOUD_TOKEN` or use `k6 login cloud --token YOUR_TOKEN`.
+
+Once authenticated, you can trigger a test by using the `cloud` command:
+
+
+
+```shell
+k6 cloud nameOfYourScript.js
+```
+
+
+For more in depth instructions, please refer to our full article on
+[cloud execution](/using-k6/cloud-execution#getting-started)
diff --git a/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/01 Test Builder/test-builder-config.png b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/01 Test Builder/test-builder-config.png
new file mode 100644
index 0000000000..4faab2f884
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/01 Test Builder/test-builder-config.png differ
diff --git a/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/01 Test Builder/test-builder-requests.png b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/01 Test Builder/test-builder-requests.png
new file mode 100644
index 0000000000..ec6a9a5c83
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/01 Test Builder/test-builder-requests.png differ
diff --git a/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/03 Recording a test script/Step 2.png b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/03 Recording a test script/Step 2.png
new file mode 100644
index 0000000000..ba280d0eb9
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/03 Recording a test script/Step 2.png differ
diff --git a/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/03 Recording a test script/Step 3.png b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/03 Recording a test script/Step 3.png
new file mode 100644
index 0000000000..270bd41c59
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/03 Recording a test script/Step 3.png differ
diff --git a/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/03 Recording a test script/Step 4.png b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/03 Recording a test script/Step 4.png
new file mode 100644
index 0000000000..dec2dd5f95
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/03 Recording a test script/Step 4.png differ
diff --git a/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/05 Scheduling a test/schedule-options.png b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/05 Scheduling a test/schedule-options.png
new file mode 100644
index 0000000000..5a0695659d
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/05 Scheduling a test/schedule-options.png differ
diff --git a/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/05 Scheduling a test/scheduling.png b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/05 Scheduling a test/scheduling.png
new file mode 100644
index 0000000000..8ed37f7e7a
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/01 Creating and running a test/images/05 Scheduling a test/scheduling.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/01 Overview.md b/src/data/markdown/docs/03 cloud/02 Analyzing Results/01 Overview.md
new file mode 100644
index 0000000000..f68a64eab0
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/02 Analyzing Results/01 Overview.md
@@ -0,0 +1,93 @@
+---
+title: 'Overview'
+excerpt: 'An overview of using k6 Cloud Results as a premiere analysis tool'
+---
+
+## Background
+
+As part of the k6 Cloud offering, k6 Cloud Results enables you to visualize, store, analyze, and trend your test results. All tests that utilize k6 Clouds Results will be analyzed by our Performance Insights algorithms to detect patterns associated with performance issues.
+
+As you continue to use k6 Cloud Results, we will also automatically create trending graphs so that you can keep track of how performance of your system changes over time. This is helpful for identifying creeping performance issues before they become larger problems.
+
+## Test Navigation and Metadata
+
+The top of the page provides a breadcrumb menu and an overview of details about your test.
+
+The breadcrumb menu allows to quickly navigate between the latest runs of the test, the test page or all the tests in the current project.
+
+
+
+Additionally, you can see metadata about the current test. Status, Duration, VUs, Regions used, and who started a test. To the write, you have various buttons and menus to let you re-run, share, compare, configure, or delete your test.
+
+
+
+## Performance Overview
+
+The Performance Overview section displays high level data for your test. If the test is running live metrics are displayed.
+
+
+
+After the test has finished the section is updated to show HTTP failures and peak RPS in place of Active VUs and current RPS. If any Performance Alerts have been detected, we will also display that information to you. In this test, our automated algorithms did not find any problems.
+
+
+
+The first signal of a good or bad result will generally be in the Performance Overview panel. Here are the most common patterns to consider.
+
+Typical signs of a good result:
+
+- Response time has a flat trend for the duration of the test
+- Request rates follow the same ramping pattern as Virtual Users(if VUs increase, so does request rate)
+
+Typical signs of a performance issue/bottleneck
+
+- Response times rise during the test
+- Response times rise, then quickly bottom out and stay flat
+- Request rates do not rise with VUs (and response times start to increase)
+
+This is not an all inclusive list. You should use these patterns as a first indicator of good or bad performance of your test.
+
+## Result Tabs
+
+The result tabs allow you to dig into the specific result data sets from your test. We present the following tabs to organize your result data:
+
+| Tab Name | Definition | Add to analysis? | Sorting |
+| ---------- | ------------------------------------------------------------------------------- | ---------------- | ----------------------- |
+| Thresholds | List of your Thresholds in the order they are defined in your script (if used). | Yes | In order defined |
+| Checks | List of Checks, organized into Groups (if used). | Yes | By group, or list (all) |
+| HTTP | List of HTTP requests made, organized into Groups (if used). | Yes | By group, or list (all) |
+| Websocket | List of Websocket requests made, organized into groups (if used). | Yes | By group, or list (all) |
+| Analysis | Tab used to compare data for analysis | N/A | N/A |
+| Script | Script used to run your test (k6 cloud tests only) | N/A | N/A |
+
+These tabs let you dig into your test data in a visual and error-driven way. You are able to click on any metric to expand a graph to dig deeper. You can also add these graphs to the Analysis tab. This allows you to look for interesting correlations in your result data. Within each tab note the ✓ or ✕ next to the individual metrics if failures were encountered. In the example below, we have two checks with failures which are clearly apparent.
+
+
+
+Refer to these articles for more specific information on:
+
+- [Thresholds](/cloud/analyzing-results/threshold-tab)
+- [Checks](/cloud/analyzing-results/checks-tab)
+- [HTTP Table](/cloud/analyzing-results/http-tab)
+
+## Analysis
+
+The analysis tab enables you to analyze and compare metrics. This is helpful for viewing very specific pieces of your data and finding correlations. Added metrics will first appear as a small chart. You can change your aggregation or select Filters to visualize the data in different ways. Finally, you can use the + to add the data to the large chart for comparison.
+
+Here are some general tips to consider when adding metrics and using this tab.
+
+- Ensure that VUs and Request rate follow the same trend.
+- Add and compare interesting requests from the HTTP and Websocket tabs to compare with other metrics
+- Add the load generator CPU and Memory consumption metrics to ensure they are not saturated (metrics only available for tests run in the cloud)
+- Add thresholds that have been exceeded
+- Add checks that have failures
+- Add metrics for endpoints that you have specific SLAs/SLOs on
+
+The above list is not meant to be all inclusive, rather a starting point in helping you dig into performance related issues so you can identify them.
+
+## See Also
+
+- [Performance Insights](/cloud/analyzing-results/performance-trending)
+- [Thresholds](/cloud/analyzing-results/threshold-tab)
+- [Checks](/cloud/analyzing-results/checks-tab)
+- [HTTP Table](/cloud/analyzing-results/http-tab)
+- [Analysis and Comparison Tab](/cloud/analyzing-results/test-comparison)
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/02 Performance Insights.md b/src/data/markdown/docs/03 cloud/02 Analyzing Results/02 Performance Insights.md
new file mode 100644
index 0000000000..c7c083eb54
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/02 Analyzing Results/02 Performance Insights.md
@@ -0,0 +1,152 @@
+---
+title: 'Performance Insights'
+excerpt: 'Performance Insights are automated algorithms designed to help highlight performance issues automatically'
+---
+
+## Background
+
+k6 Cloud's Performance Insights are algorithms built into k6 Cloud Test Results. Our algorithms are automatically executed as part of test result processing. This happens when you run a cloud test `k6 cloud myScript.js` or when you stream a local execution to the cloud `k6 run myScript.js -o cloud`.
+
+> ### Using Performance Insights
+>
+> In order to utilize Performance Insights, all you need to do is run a test. The algorithms automatically analyze
+> the raw metrics and data. If we find something, we will let you know in the Performance Overview section at the top of your test
+
+## Throughput Limit
+
+This alert is raised when a throughput limit has been detected. The number of active in-flight requests continue to grow as the number of Virtual Users are increasing, while the request rate (finished requests) has flatlined. This is indicative that the system under test is overloaded, thus resulting in higher response times. We recommend that you correlate the performance bottleneck with data from an APM and/or server monitoring tool. After making changes, you should run your tests again at the same Virtual User level, to verify if your changes have improved performance on the system under test.
+
+## Increased HTTP failure rate
+
+This alert is raised when a period of elevated HTTP errors has been detected (10% higher than in the beginning of the test). There could be a number of reasons for this, e.g. web server configuration (timeouts, rate limiting etc.) or internal errors caused by saturation of a resource (CPU, memory, disk I/O or database connections). It typically means the target system is close to its performance limit.
+
+**Note:** Failed responses are often returned much faster than successful responses. Consequently, an increased HTTP error rate may produce misleading request rate and response time metrics.
+
+## High HTTP failure rate
+
+The total number of HTTP(s) errors is higher than 15% during the first 100 completed script iterations.
+
+Errors that occur early on are typically not considered to be performance related. Our algorithms also have not detected an increase in the error rate as load has increased.
+With that in mind, there are a number of non-performance related reasons for errors, which includes, but is not limited to:
+
+- You're making invalid requests:
+ - Invalid URLs, eg. with a typo in it or a hostname that is not in the public DNS system.
+ - Missing required headers, eg. authentication/authorization headers or user-agent.
+ - Sending the wrong body data.
+- You're trying to test a system that's behind a firewall.
+- You're hitting a rate limit.
+
+**Note:** Failed responses are often returned much faster than successful responses.
+
+## Not Enough Training Data
+
+This alert is raised because our Smart Results algorithms need at least 100 complete VU iterations of training data plus an additional 15 seconds to produce meaningful output. Your test did not complete the 100 VU iterations necessary for the training data. We recommend increasing the test duration to get the full benefits of Performance Insights.
+
+## Test Health and Informational Performance Insights
+
+Test Health Performance Insights are alerts that intend to highlight test or script related issues. These issues, if not addressed, can either skew your results or make result analysis harder to parse through. These alerts are often quickly solved through changes in the test script or test configuration.
+
+**Important**: The `Third Party Content` and `Too Many URLs` alerts are more informational alerts. Depending on what you are testing, it may be appropriate to disregard these alerts. High CPU or Memory usage **should never** be ignored.
+
+## Third Party Content
+
+**This is an best practice alert, we strongly recommend you remove third party requests from your test**
+
+This alert is raised when we detect many different domains in a test. This is typically caused by your test script containing requests to 3rd party resources such as CDNs, social media scripts, analytic tools, etc. It's recommended to remove third party requests as it may violate the terms of service of that third party, that third party may throttle your requests skewing the percentiles of your results, or you may have no ability to impact performance of that third party.
+
+_Special Notes:_
+
+- You may have a valid reason to test your CDN. Most CDNs charge based on usage so your tests could result in additional costs from your CDN.
+- Your system under test may utilize multiple domains, in which case you can ignore this alert.
+
+## Too Many URLs
+
+**This is an best practice alert, we strongly recommend you aggregate dynamic URLs as it will make analysis easier**
+
+This alert is raised when we detect more than 500 unique URLs in your test results. This is commonly caused by a URL that contains a query parameter or other ID that is unique per iteration. e.g. tokens, session IDs, etc.
+
+In the following example, our query parameter would produce large number of URL metrics:
+
+
+
+```javascript
+for (var id = 1; id <= 600; id++) {
+ http.get(`http://test.k6.io/?ts=${id}`);
+}
+// But you can group all these URL metrics together
+// in our result analysis using the name tag,
+// making it easier for you to interpret the data.
+// Note that you must use the name tag for grouping.
+
+for (var id = 1; id <= 600; id++) {
+ http.get(`http://test.k6.io/?ts=${id}`, {
+ tags: { name: 'test.k6.io?ts' },
+ });
+}
+```
+
+
+
+_Note:_ In some cases, the unique URL may be generated by a third party resource. As mentioned in the
Third Party Content alert , it is a best practice to not include third party resources in your test scripts.
+
+## Too Many Groups
+
+**This is an best practice alert, we recommend reviewing how you use the [Group name](/javascript-api/k6/group-name-fn) in your test script.**
+
+This alert is raised when we
detect a high number of groups in your test script. The most common reason for this alert is an incorrect usage of the [Group name](/javascript-api/k6/group-name-fn) using it for aggregating different HTTP requests, or within a loop statement. When aggregating URLs, please use the name tag.
+
+Groups are meant to organize and provide an overview of your result tests allowing you a BDD-style of testing. By allowing this organization, you can quickly find specific parts of your test. For example, the point where users are on a certain page or taking specific actions.
+
+
+
+```javascript
+import { group } from 'k6';
+
+export default function() {
+ group('user flow: returning user', function() {
+ group('visit homepage', function() {
+ // load homepage resources
+ });
+ group('login', function() {
+ // perform login
+ });
+ });
+}
+```
+
+
+
+If you want to group multiple HTTP requests, we suggest you use the [URL grouping](/using-k6/http-requests#url-grouping) feature of k6 to aggregate data into a single URL metric.
+
+## High Load Generator CPU Usage
+
+**This is a Test Health Alert. You should address this to ensure accurate results**
+
+This alert is raised when we detect high utilization of the load generator CPU during a test. Over utilization of the load generator can skew your test results producing data that varies from test to test and unpredictably. Virtual Users will naturally try to execute as quickly as possible. The exact cause of over utilization can vary, but is likely due to one of the following reasons:
+
+- Lack of sleep times in your scripts
+ - Sleep times help with pacing and emulating real user behaviour
+- High RPS per VU
+ - When testing API endpoints you may configure your test to aggressively request an endpoint.
+- Large number of requests in a single request batch
+ - Requests made in a request batch will be made in parallel up to the default or defined limits
+- Large amounts of data are returned in responses resulting in high memory utilization
+ - When the memory of the load generator reaches near total consumption, the garbage collection efforts of the Load Generator can cause increase CPU utilization.
+- A JavaScript exception is being thrown early in VU execution. This results in an endless restart loop until all CPU cycles are consumed.
+
+Possible fixes:
+
+- Increase sleep times where appropriate
+- Increase the number of VUs to produce less RPS per VU (thus the same total load)
+- Utilize multiple load zones to spread VUs out across multiple regions
+
+## High Load Generator Memory Usage
+
+**This is a Test Health Alert. You should address this to ensure accurate results**
+
+This alert is raised when we detect high utilization of load generator memory during a test. When memory is highly utilized in a test, it can result in some unexpected behaviour or failures. It may also cause high CPU utilization as garage collection efforts consume more and more of the CPU cycles.
+
+Possible fixes:
+
+- Utilize the test option `discardResponseBodies` to throw away the response body by Default
+ - Use `responseType:` to capture the responseBodies you may require
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/03 Threshold Tab.md b/src/data/markdown/docs/03 cloud/02 Analyzing Results/03 Threshold Tab.md
new file mode 100644
index 0000000000..39e3422dad
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/02 Analyzing Results/03 Threshold Tab.md
@@ -0,0 +1,23 @@
+---
+title: "Threshold Tab"
+excerpt: "The Threshold Tab allows you to visually inspect the performance of your Thresholds during a k6 test. "
+---
+
+## Cloud Results: Thresholds
+
+The Threshold Tab allow you to visually inspect and analyze `Thresholds` in your test. The number in the tab represents passing Thresholds / Total Thresholds so you can quickly see if something needs your attention.
+
+Further within this tab, you are able to:
+
+- Easily see failing checks. In the example below, our `check_failure_rate` custom metric is failing. Take notice of the ✓ or ✕ on the left side of each row.
+- Expand a threshold to view it's graph. In our example below, the expanded threshold is below the threshold of 100 ms
+- Add the chart to the `Analysis Tab` for further correlation with other data
+
+
+
+Next, [Checks Tab](/cloud/analyzing-results/checks-tab)
+
+
+## See Also
+
+For more information on defining `Thresholds` in your test, please refer to our documentation on [Thresholds](/using-k6/thresholds)
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/04 Checks Tab.md b/src/data/markdown/docs/03 cloud/02 Analyzing Results/04 Checks Tab.md
new file mode 100644
index 0000000000..5d7ff658ea
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/02 Analyzing Results/04 Checks Tab.md
@@ -0,0 +1,24 @@
+---
+title: "Checks Tab"
+excerpt: "The Checks tab allow you to visualize performance of your checks in your k6 test"
+---
+
+## Cloud Results: Checks
+
+The Checks Tab allow you to visually inspect and analyze `Checks` in your test. The number in the tab represents passing Checks / total Checks so you can quickly see if something needs your attention.
+
+As you further examine this tab, you are able to:
+
+- Easily see `checks` that have failures. Take notice of the ✓ or ✕ on the left side of each row.
+ - In the example below, the `Check` "is welcome header present" is succeeding only 32.7% of the time
+- Expand a `Check` to view it's graph. We can see how many failures occur at different points in the test.
+- Add the chart to the `Analysis Tab` for further correlation with other data
+
+
+
+Next, [HTTP Tab](/cloud/analyzing-results/http-tab)
+
+
+## See Also
+
+For more information on using `Checks` in your test, please refer to our documentation on [Checks](/using-k6/checks)
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/05 HTTP Tab.md b/src/data/markdown/docs/03 cloud/02 Analyzing Results/05 HTTP Tab.md
new file mode 100644
index 0000000000..622b4256cf
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/02 Analyzing Results/05 HTTP Tab.md
@@ -0,0 +1,23 @@
+---
+title: "HTTP Tab"
+excerpt: "The HTTP tab allows you to inspect individual requests in your k6 test"
+---
+
+## Cloud Results: HTTP Tab
+
+The HTTP Tab allow you to inspect individual HTTP requests made in your test. The number in the tab represents total passing HTTP Requests / Total HTTP Requests. This provides a quick hint to any failing requests. Displayed in the tab will be a row for each HTTP request by resource, method used, and status code returned. If an endpoint has multiple request methods or status codes, we separate them. Note the login endpoint which was requested via a POST but has had two different status codes returned, 200 and 302.
+
+
+On this tab, we can easily see `HTTP` requests with failures that have failures. Take notice of the ✓ on the left of each row. If we had failures they would be marked with a ✕
+
+
+
+When you click on a row to expand, you can also:
+
+- View additional metrics for the request, such as time spent handshaking
+- Change the aggregation (Mean, Median, Min, Max, Std Deviation, or 95th percentile)
+- Add the chart (plus additional metrics) to the analysis tab.
+
+
+
+Next, [Analysis Tab](/cloud/analyzing-results/analysis-tab)
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/06 Analysis Tab.md b/src/data/markdown/docs/03 cloud/02 Analyzing Results/06 Analysis Tab.md
new file mode 100644
index 0000000000..31608b7125
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/02 Analyzing Results/06 Analysis Tab.md
@@ -0,0 +1,25 @@
+---
+title: "Analysis Tab"
+excerpt: "The Analysis tab allows you to compare and correlate data from your k6 test."
+---
+
+## Cloud Results: Analysis Tab
+
+The Analysis Tab allow you to gather all interesting data points from your test to compare and correlate. Any metrics you that you click on `ADD CHART TO ANALYSIS` will show up here. In our example below, we've added some metrics from the previous three articles in this section. The number in the tab, will show how many metrics have been added, for quick reference.
+
+By default the comparison chart will be blank, we've added VUs and response time from the `ADD NEW METRIC` button in the top right corner. Clicking on this button will open a modal for you to add more metrics:
+
+
+
+Other things you can do on this tab:
+
+
+- When viewing the chart, hovering over any single point will show you data from that point in the test.
+- View metrics you added from previous tabs. They will show up below the main chart.
+ - You may also change aggregation of these metrics in this tab or filter on tags
+- Add small charts to the larger chart by clicking on the "+" in the top right corner of the small charts
+- Add additional metrics to the small chart area by clickin `ADD NEW METRIC` in the small chart area
+
+
+
+Next, [Test Comparison](/cloud/analyzing-results/test-comparison)
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/07 Test Comparison.md b/src/data/markdown/docs/03 cloud/02 Analyzing Results/07 Test Comparison.md
new file mode 100644
index 0000000000..972d2f3090
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/02 Analyzing Results/07 Test Comparison.md
@@ -0,0 +1,71 @@
+---
+title: "Test comparison"
+excerpt: "Use the k6 Cloud Results test comparison feature to compare data across different test runs."
+---
+
+
+
+> ### Test Comparison is for comparing runs of the same Test Script
+>
+> When using test comparison, you may only compare tests from the same series of test runs. You are not able to compare two different test scripts.
+
+
+
+The test comparison feature built-in to k6 Cloud Results that allows you to compare the results of two different test runs of the same test script. You can compare high-level metrics, individual checks and URL endpoints. You may wish to compare against a previous test run to look for a measurable difference in some change you made. Or you may be comparing against a known baseline.
+
+## Setting a baseline test
+
+Comparing results against a known [baseline](#phase-2---baseline-testing-scaling-your-tests-and-complex-cases) is a core part of the General Methodology for [performance testing](/testing-guides/automated-performance-testing). Baseline tests are important as they allow you to compare against a control to look for differences. Baseline tests should produce enough load to contain meaningful data and ideal results. In other words, a heavy stress test isn't a good baseline. Think much smaller.
+
+In order to set your baseline, open up the results for the test run you wish to be your baseline. Click on the three dots in the top right corner -> set as Baseline test run. Baseline tests are exempt from data retention policies.
+
+
+
+## Selecting test runs to compare
+
+To compare two test runs, open up one of the test runs. Then select the test run you want to compare it to using the select drop down on the right side of the test result page, just above the Performance Overview section.
+
+
+
+## Test Comparison Mode
+
+After you select a test you will be brought into comparison mode. While in this mode you will see two charts in the Performance Overview section and are able to change the base and target test you are comparing. This lets you quickly change between different test runs and visually see if there are any obvious performance differences.
+
+
+
+## Thresholds Tab Test Comparison
+
+In the thresholds tab, additional data is added to the table for the base and target test run These columns will show the current vs compared test run's Threshold `value` for each Threshold and the `pass/fail` status. Clicking on any threshold will display a separate threshold chart for each test run.
+
+
+
+## Checks Tab Test Comparison
+
+In the checks tab, additional data is added to the table for the base and target test run. These column will show the difference in `Success Rate`, `Success Count` and `Fail Count` between the current and compared test runs. clicking on any Check will display a separate checks charts for each test run.
+
+
+
+## HTTP Tab Test comparison
+
+In the HTTP Tab, additional data is added to the table for the base and target test run. Here you can compare `Count`, `p95`, and `p99` metrics for individual HTTP requests.
+
+Clicking on a row will also show two separate charts, one for each test run. You can also change aggregation of the data to add additional metrics, such as timing breakdown for each HTTP request.
+
+
+
+Next, [Sharing Results](/cloud/analyzing-results/test-results-menu#share-test-results)
+
+
+
+
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/08 Test Results Menu.md b/src/data/markdown/docs/03 cloud/02 Analyzing Results/08 Test Results Menu.md
new file mode 100644
index 0000000000..95c4c1a1d0
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/02 Analyzing Results/08 Test Results Menu.md
@@ -0,0 +1,54 @@
+---
+title: "Test Results Menu"
+excerpt: "Additional options available in the test results menu"
+---
+
+The Test Results menu allows you to take some more actions on your specific test.
+
+
+
+## Export data
+
+Starts a data export. For more information on the structure of the export, please refer to this [documentation](/cloud/analyzing-results/result-export)
+
+
+## Share test results
+
+Generates a URL that you can use to share test results.
+
+
+
+
+> ### Test Result Sharing URL
+>
+> The generated URL does not require a user to be authenticated to view. If you want to share sensitive results, considering add users as [Team Members](/cloud/project-and-team-management/team-members) instead.
+>
+> **Note**: The URL is not generated until after you choose the option to share.
+
+
+
+
+
+## Delete test results
+
+Deletes the current test result.
+
+
+
+> ### Test Deletion is not reversible
+>
+>
+
+
+
+## Set as baseline
+
+Sets the current test run as your baseline test. You are able to specify one test run per test as baseline. This allows you to compare performance over a longer period of time.
+
+
+
+## Create Note
+
+The `Create Note` option launches a modal window with a text box. You can use this to enter notes regarding the test, changes made, or anything that may be worth noting about your test.
+
+
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/09 Performance Trending.md b/src/data/markdown/docs/03 cloud/02 Analyzing Results/09 Performance Trending.md
new file mode 100644
index 0000000000..695e85d3bf
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/02 Analyzing Results/09 Performance Trending.md
@@ -0,0 +1,20 @@
+---
+title: "Performance Trending"
+excerpt: "The performance trending graph allows you to quickly see how performance changes over between test runs"
+---
+
+## Background
+
+The performance trending graph is automatically created as you continue to run your tests. It's intended to provide a high level overview of your test performance over time. The bars on the graph also signal the status of a specific test. You are able to view the performance trending graph both from the dashboard. In both cases we plot the `p95` response time metric for all HTTP requests from the test run.
+
+## Dashboard Performance Trending
+
+Directly from the dashboard you can see all of your tests and the status of the test runs. This view is helpful to get a high level view of how performance is trending over time. In our example below, we can see `Insights Demo with Cloud Execution` is currently running, but has quite a few failures before this test run. Our script, `api.js` is passing and trending down, which is a good sign.
+
+
+
+## Test Performance Trending
+
+By clicking on the name of any of your tests will bring you to a performance trending graph for that specific test run. We show more data points over time in this graph. You can see more information by hovering over any bar in the graph. This test has stable response times between test runs, but is failing by [Thresholds](/using-k6/thresholds)
+
+
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/10 Result Export.md b/src/data/markdown/docs/03 cloud/02 Analyzing Results/10 Result Export.md
new file mode 100644
index 0000000000..615e95a625
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/02 Analyzing Results/10 Result Export.md
@@ -0,0 +1,37 @@
+---
+title: 'Result Export'
+excerpt: 'Structure for the CSV export for cloud results'
+---
+
+Test result data can be exported after a test has finished and data processing complete. To do so, use the [test results menu](/cloud/analyzing-results/test-results-menu) in the top right of a test result and select `Export Data`. The data will be exported as a `.tar.gz` file with a `.csv` contained inside.
+
+## Structure of the CSV data
+
+The CSV data has the following columns of data:
+
+
+
+```
+"time","metric","group_id","response_time","url","method","status","count","load_zone","tags"
+```
+
+
+
+Here's example data, units (where necessary) and description of each field present in the CSV data.
+
+| Column | Example | Data/Unit | Description |
+| --------------- | -------------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
+| `time` | 2019-03-25 11:12:48.927949+00:00 | datetime (UTC) | The ISO-8601 timestamp when this data point was captured (when the HTTP request was made). |
+| `metric` | http_req_duration | string | The metric name that this data point represents. |
+| `group_id` | e1158ec16fa10dcfd16f4bd7309e2c55 | string | The ID of the k6 [`group()`](/using-k6/tags-and-groups) from where this request was made. |
+| `response_time` | 2.008016 | number (ms) | The HTTP response time of the request that this data point represents (if `count` > 1 then this will be an aggregate value, the average). |
+| `url` | http://test.k6.io/style.css | string | The URL requested. |
+| `method` | GET | string | The HTTP method of the request that this data point represents. |
+| `status` | 200 | number | The HTTP response status code of the request that this data point represents. |
+| `count` | 1.0 | number | Number of samples that this data point represents (if > 1 `response_time` is an aggregate value). |
+| `load_zone` | amazon:us:ashburn | string | The load zone where the request(s) was made from. |
+| `tags` | staticAsset=true | string | Pipe (`|`) separated list of `name=value` tags as specified for the request in the script. |
+
+### Future
+
+The exported CSV file currently only contains data from the primary HTTP response time metric (`http_req_duration`). In the future we'll expand the export feature with more HTTP data, WebSocket data, Checks data, Thresholds data as well as Custom metrics data.
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/checks-tab-with-failures.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/checks-tab-with-failures.png
new file mode 100644
index 0000000000..afcce7461c
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/checks-tab-with-failures.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/finished-performance-overview.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/finished-performance-overview.png
new file mode 100644
index 0000000000..11d164a2c5
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/finished-performance-overview.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/running-performance-overview.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/running-performance-overview.png
new file mode 100644
index 0000000000..c56fc43b08
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/running-performance-overview.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/test-metadata.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/test-metadata.png
new file mode 100644
index 0000000000..1ea30d742a
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/test-metadata.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/test-run-navigation.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/test-run-navigation.png
new file mode 100644
index 0000000000..3930479fe9
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/01 Overview/test-run-navigation.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/03 Threshold Tab/thresholds-tab.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/03 Threshold Tab/thresholds-tab.png
new file mode 100644
index 0000000000..3a634403c8
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/03 Threshold Tab/thresholds-tab.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/04 Checks Tab/checks-tab.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/04 Checks Tab/checks-tab.png
new file mode 100644
index 0000000000..93b19381fb
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/04 Checks Tab/checks-tab.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/05 HTTP Tab/http-tab-graph.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/05 HTTP Tab/http-tab-graph.png
new file mode 100644
index 0000000000..a15bed1e6a
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/05 HTTP Tab/http-tab-graph.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/05 HTTP Tab/http-tab.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/05 HTTP Tab/http-tab.png
new file mode 100644
index 0000000000..a1204624fb
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/05 HTTP Tab/http-tab.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/06 Analysis Tab/add-metric-modal.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/06 Analysis Tab/add-metric-modal.png
new file mode 100644
index 0000000000..01c66a566a
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/06 Analysis Tab/add-metric-modal.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/06 Analysis Tab/analysis-tab.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/06 Analysis Tab/analysis-tab.png
new file mode 100644
index 0000000000..cffcd544a6
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/06 Analysis Tab/analysis-tab.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/checks-comparison.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/checks-comparison.png
new file mode 100644
index 0000000000..0feea46f66
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/checks-comparison.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/comparison-mode.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/comparison-mode.png
new file mode 100644
index 0000000000..a64ea257ef
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/comparison-mode.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/http-comparison.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/http-comparison.png
new file mode 100644
index 0000000000..5908670a40
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/http-comparison.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/select-test-comparison.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/select-test-comparison.png
new file mode 100644
index 0000000000..a7723e08b4
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/select-test-comparison.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/set-baseline-test.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/set-baseline-test.png
new file mode 100644
index 0000000000..83f7ef927a
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/set-baseline-test.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/thresholds-comparison.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/thresholds-comparison.png
new file mode 100644
index 0000000000..ae3a7061c6
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/07 Test Comparison/thresholds-comparison.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/08 Test Results Menu/set-baseline.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/08 Test Results Menu/set-baseline.png
new file mode 100644
index 0000000000..d4027eaafa
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/08 Test Results Menu/set-baseline.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/08 Test Results Menu/test-note.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/08 Test Results Menu/test-note.png
new file mode 100644
index 0000000000..db3239f499
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/08 Test Results Menu/test-note.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/08 Test Results Menu/test-results-menu.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/08 Test Results Menu/test-results-menu.png
new file mode 100644
index 0000000000..3dbaea0487
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/08 Test Results Menu/test-results-menu.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/08 Test Results Menu/test-share.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/08 Test Results Menu/test-share.png
new file mode 100644
index 0000000000..6062897357
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/08 Test Results Menu/test-share.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/09 Performance Trending/dashboard-perf-trending.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/09 Performance Trending/dashboard-perf-trending.png
new file mode 100644
index 0000000000..6072681132
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/09 Performance Trending/dashboard-perf-trending.png differ
diff --git a/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/09 Performance Trending/performance-trending.png b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/09 Performance Trending/performance-trending.png
new file mode 100644
index 0000000000..2cdb51ca73
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/02 Analyzing Results/images/09 Performance Trending/performance-trending.png differ
diff --git a/src/data/markdown/docs/03 cloud/03 Integrations/02 CI.md b/src/data/markdown/docs/03 cloud/03 Integrations/02 CI.md
new file mode 100644
index 0000000000..4fcbfcf12e
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/03 Integrations/02 CI.md
@@ -0,0 +1,20 @@
+---
+title: 'CI'
+excerpt: 'Overview of Integrations page in k6 Cloud web UI'
+---
+
+## K6 as a CLI
+
+One of the main features in k6 is its ability to act as a command line interface with k6 cloud. This makes it effortless to integrate into your CI/CD pipelines and making sure that your build is free of performance regressions.
+
+Our [Integrations](/integrations) section contains guides for all major CI tools with more on the horizon.
+
+## Converters
+
+With k6 being an open source project, community contributed to multiple converters that make it easier to port your JMeter or Postman scripts to k6. Our Har to k6 converter can help you quickly convert HAR files exported from any browser or third party application.
+
+There is more information available in our [Integrations](/integrations) section.
+
+***
+
+Check out our [Automated performance testing guide too!](/testing-guides/automated-performance-testing)
diff --git a/src/data/markdown/docs/03 cloud/03 Integrations/03 Notifications.md b/src/data/markdown/docs/03 cloud/03 Integrations/03 Notifications.md
new file mode 100644
index 0000000000..f8bc5f0907
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/03 Integrations/03 Notifications.md
@@ -0,0 +1,140 @@
+---
+title: 'Notifications'
+excerpt: ''
+---
+
+Notifications allow you to subscribe to events happening in your organizations/projects. You can receive notifications to be aware when:
+
+- A test has started.
+ - With option to notify on "manual" or "scheduled" tests.
+- A test has completed
+ - With option to notify based on specific result statuses, eg. only on test failure (like a [threshold](/using-k6/thresholds) failing).
+
+You will likely want to use notifications when you schedule your performance tests and/or configure them into your Continuous Integration pipeline.
+
+k6 supports two different notification options:
+
+- Slack
+- WebHook
+
+**Note:** Notifications are configured per organization by the organization owner or an admin member.
+
+
+
+## Slack
+
+Slack is a messaging app for teams. Follow these instructions to configure Slack notifications:
+
+1. From Slack, add a Custom Integration and select Incoming WebHook app.
+2. Select or create a channel and copy the generated WebHook URL.
+3. From the k6 web app, select "Integrations" from the left menubar, and from there select "Setup" for Slack.
+4. Add Slack WebHook URL into the URL input field and click Save Changes or Test Hook.
+
+
+
+---
+
+## WebHooks
+
+When an event is triggered, we'll send a HTTP POST request to the configured URL with a JSON payload containing event specific data. The format is explained in the following section.
+
+---
+
+## Notification events
+
+Headers sent with all requests
+
+| Header | Description |
+| ------------------ | --------------------------------------------------------------------- |
+| X-LoadImpact-ID | Unique ID for this request |
+| X-LoadImpact-Event | Name of the event |
+| User-Agent | User agent for webhook requests always start with `LoadImpactWebHook` |
+
+
+
+```
+Example headers:
+X-LoadImpact-ID: 19c5d426-3b4d-43c3-8277-37ad7d457430
+X-LoadImpact-Event: test.started
+User-Agent: LoadImpactWebHook
+```
+
+
+
+## Load test run started event
+
+Sent when a load test is starting.
+
+Example JSON body:
+
+
+
+```json
+{
+ "status": 2,
+ "status_text": "Running",
+ "user_id": 1,
+ "name": "Load test",
+ "organization_id": 1,
+ "load_test_id": 1,
+ "load_test_run_id": 1,
+ "project_id": 1,
+ "event": "test.started"
+}
+```
+
+
+
+## Load test run finished event
+
+Sent when a load test finishes, aborts or fails
+
+Example JSON body:
+
+
+
+```json
+{
+ "status": 3,
+ "status_text": "Finished",
+ "user_id": 1,
+ "name": "Load test",
+ "organization_id": 1,
+ "load_test_id": 1,
+ "load_test_run_id": 1,
+ "project_id": 1,
+ "event": "test.finished"
+}
+```
+
+
+
+## Status Codes
+
+| Status | Description
+|------- | ------------
+| -2 | Created
+| -1 | Validated
+| 0 | Queued
+| 1 | Initializing
+| 2 | Running
+| 3 | Finished
+| 4 | Timed out
+| 5 | Aborted by user
+| 6 | Aborted by system
+| 7 | Aborted by script error
+| 8 | Aborted by threshold
+| 9 | Aborted by limit
+
+---
+
+## Test notification channel event
+
+Test event that can be triggered from the UI to test webhook
+
+Example JSON body:
+```json
+{
+"event": "test.notification_channel"
+}
+```
diff --git a/src/data/markdown/docs/03 cloud/03 Integrations/04 Token.md b/src/data/markdown/docs/03 cloud/03 Integrations/04 Token.md
new file mode 100644
index 0000000000..0685376827
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/03 Integrations/04 Token.md
@@ -0,0 +1,106 @@
+---
+title: 'Token'
+excerpt: 'How to authenticate with k6 Cloud token'
+---
+
+Before you can interact with the k6 Cloud service, whether it's for streaming results or running tests in the cloud, you'll need to authenticate. Your Auth Token is what enables this and allows you to interact with the k6 Cloud using the k6 CLI or through the REST API. To get your Auth Token, please visit this [page](https://app.k6.io/account/token).
+
+Below are some examples on how to utilize the token to authenticate.
+
+
+
+
+> ### Google/Github Single Sign-On Users
+For Single Sign-On (SSO) users, `k6 login cloud` requires a k6 cloud account email and password. You will need to create a password by using [Forgot Password](), or you'll instead need to
get your API authentication token from the app and supply that explicitly: `k6 login cloud --token YOUR_API_AUTH_TOKEN`.
+
See below for more information.
+
+
+
+
+
+> ### Docker Users
+If you're running k6 in a Docker container you'll need to make sure that the k6 config file where the k6 cloud API authentication information (an API authentication token) will be stored to is persisted via a Docker volume to the host machine using the `-c/--config PATH/TO/CONFIG_FILE` CLI flag, e.g. `docker run -i -v /path/on-host:/path/in-container/ loadimpact/k6 login cloud -c /path/in-container/config.json`.
+
+
+
+
+
+> ### Integrating with CI
+If you are integrating k6 into your CI pipeline, we recommend using one of the token methods to authenticate and not exposing your username/password within your CI configuration files or as variables.
+
+
+
+
+
+## Authenticate with email/password
+
+You can forego using a token and use your k6 cloud email/password credentials by entering the following command into your terminal:
+
+
+
+```shell
+k6 login cloud
+```
+
+
+
+
+
+This will login to your account, fetch (and create of necessary) your k6 cloud API authentication token, and save it to a [k6 configuration file](#using-config-file).
+
+## Authenticating with API token
+
+If you're a Google/Github Single Sign-On (SSO) user or if you have a use case where using your k6 cloud account credentials is not appropriate you can choose to enter your k6 cloud API authentication token directly by entering the following command into your terminal:
+
+
+
+```C
+k6 login cloud --token YOUR_API_AUTH_TOKEN
+```
+
+
+
+
+## API Token as an environment variables
+
+You can also authenticate with your k6 cloud API authentication token via environment variables. If you make sure the `K6_CLOUD_TOKEN` has been set to your k6 cloud API authentication token k6 will pick it up when executing.
+
+## Authentication with a config file
+
+You can also directly add your k6 cloud API authentication token to a configuration file. Either in the default path that k6 will look for it by default:
+
+
+
+```
+${HOME}/.config/loadimpact/k6/config.json
+```
+
+```
+${HOME}/Library/Application Support/LoadImpact/k6/config.json
+```
+
+```
+C:\Users\<User>\AppData\Roaming\loadimpact\k6\config.json
+```
+
+
+
+
+
+or by specifying the `-c/--config PATH/TO/CONFIG_FILE` CLI flag.
+
+When your k6 cloud API authentication token has been added to the config file, it should look something like this (removing any other config options from the file):
+
+
+
+```json
+{
+ "collectors" {
+ "cloud": {
+ "token": "YOUR_API_AUTH_TOKEN"
+ }
+ }
+}
+```
+
+
diff --git a/src/data/markdown/docs/03 cloud/03 Integrations/images/03 Notifications/k6-notifications-slack-webhook.png b/src/data/markdown/docs/03 cloud/03 Integrations/images/03 Notifications/k6-notifications-slack-webhook.png
new file mode 100644
index 0000000000..27d0ed3abd
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/03 Integrations/images/03 Notifications/k6-notifications-slack-webhook.png differ
diff --git a/src/data/markdown/docs/03 cloud/03 Integrations/images/03 Notifications/slack-webhook-setup.png b/src/data/markdown/docs/03 cloud/03 Integrations/images/03 Notifications/slack-webhook-setup.png
new file mode 100644
index 0000000000..a9b3f3661f
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/03 Integrations/images/03 Notifications/slack-webhook-setup.png differ
diff --git a/src/data/markdown/docs/03 cloud/04 Project and Team Management/01 Organizations.md b/src/data/markdown/docs/03 cloud/04 Project and Team Management/01 Organizations.md
new file mode 100644
index 0000000000..8166f32d31
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/04 Project and Team Management/01 Organizations.md
@@ -0,0 +1,37 @@
+---
+title: 'Organizations'
+excerpt: 'Explanation of organizations and the hierarchy in the k6 web app'
+---
+
+
+
+> Running tests in another Organization
+> If you have been invited to another organization as a Team Member, you must specify a projectId in `ext.loadimpact.projectId` in order to use that organization's subscription to run your test.
+
+
+
+## Background
+
+Organizations are a hierarchical tier within k6. Organizations are owned by a single user (the account owner).
+
+## Using Organizations
+
+By default, all users have one Organization within their k6 account, this is automatically created and assigned during account registration. If you purchase a subscription, it is associated with the organization. As an organization owner, you may invite [Team Members](/cloud/project-and-team-management/team-members) as either Admins or Project Members. These users will be able to utilize the subscription and access projects in the organization.
+
+If you have been invited to another user's Organization, you will have access based on the role the owner assigned to you (Admin or Project Member). You may utilize the subscription associated with that Organization.
+
+> ### Note on Organizations
+>
+> Most users will only require 1 organization for their subscription. Some larger companies may wish to use multiple organizations to manage multiple unique subscriptions.
+
+## Hierarchy Diagram
+
+Please refer to the following diagram to visualize the relationship of different aspects of the web application
+
+
+
+
+## See also
+
+- [Projects](/cloud/project-and-team-management/projects)
+- [Team Members](/cloud/project-and-team-management/team-members)
diff --git a/src/data/markdown/docs/03 cloud/04 Project and Team Management/02 Projects.md b/src/data/markdown/docs/03 cloud/04 Project and Team Management/02 Projects.md
new file mode 100644
index 0000000000..c5859faaf1
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/04 Project and Team Management/02 Projects.md
@@ -0,0 +1,26 @@
+---
+title: 'Projects'
+excerpt: 'Keep your tests and team members organized with projects, a foldering system built into the k6 web app'
+---
+
+## Background
+
+Projects are a way to stay organized within your account in k6. In the simpliest terms, projects can be considered a foldering system which you can use to organize your tests. Projects are assigned on a per organization level. Organization owners and admins can invite users to be members of a project. Only Read/Write members can be explicitly restricted from accessing a project.
+
+## Using Projects
+
+Projects are a simple foldering system. They are flexible enough to allow you to use them in a way that makes sense to you, but simple enough to not lose information in a deep nested structure.
+
+Here are some ways we have seen users utilize Projects to stay organized:
+
+- Per team: Each team is given their own project for them to do their work and collaborate
+- Per component: A project is created for each component or service you are testing.
+- Per brand: E-commerce customers may want to use projects per brand to stay organized
+- Per Customer: When dealing with custom software, you may wish to organize by customer to ensure each unique system is tested
+- Per Major Release: After your systems go through a major change, you may wish to create a new project to organize the most recent data
+
+## Running CLI Tests in a Specific Project
+
+If you are using the CLI to trigger your tests, you will need to specify a projectId in order to use the correct subscription and organize your results in the correct project. You will need to get the project ID from the top left corner of the dashboard:
+
+
diff --git a/src/data/markdown/docs/03 cloud/04 Project and Team Management/03 Team Members.md b/src/data/markdown/docs/03 cloud/04 Project and Team Management/03 Team Members.md
new file mode 100644
index 0000000000..f0b6a54e62
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/04 Project and Team Management/03 Team Members.md
@@ -0,0 +1,59 @@
+---
+title: 'Team Members'
+excerpt: 'Guide on inviting Team Members to your k6 account for collaboration'
+---
+
+
+
+> Team Members must specify a projectId
+> Invited Team members must specify a `projectId` in `options.ext.loadimpact.projectId` of the options object of their test in order to use the subscription of the organization they have been invited to.
+
+
+
+## Background
+
+The Team Member functionality allows you to invite others users to gain access to your Organization and Subscription within the k6 web app. This make it easy to collaborate and share results to key stakeholders across your company.
+
+---
+
+## Adding Team Members
+
+To add Team Members, click "Members" under "Organization Settings" in the drop down menu in the top left corner.
+
+
+
+---
+
+Next, click "Invite New Members"
+
+
+
+---
+
+Then, within the modal window, enter the email address and select a role for the user. For the Project Member permission role, users can be specifically assigned to projects. Admins are able to see all projects
+
+
+
+---
+
+## Specify a projectId
+
+
+
+```javascript
+export let options = {
+ // Truncated for brevity
+ ext: {
+ loadimpact: {
+ name: 'My Test Name',
+ projectId: 1234567, // Replace with your projectId
+ distribution: {
+ scenarioLabel1: { loadZone: 'amazon:us:ashburn', percent: 50 },
+ scenarioLabel2: { loadZone: 'amazon:ie:dublin', percent: 50 },
+ },
+ },
+ },
+};
+```
+
+
diff --git a/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/01 Organizations/organization-hierarchy-diagram.png b/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/01 Organizations/organization-hierarchy-diagram.png
new file mode 100644
index 0000000000..1cb7f6e7a3
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/01 Organizations/organization-hierarchy-diagram.png differ
diff --git a/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/02 Projects/projectID.png b/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/02 Projects/projectID.png
new file mode 100644
index 0000000000..934892ad42
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/02 Projects/projectID.png differ
diff --git a/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/03 Team Members/drop-down-menu.png b/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/03 Team Members/drop-down-menu.png
new file mode 100644
index 0000000000..5e28532816
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/03 Team Members/drop-down-menu.png differ
diff --git a/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/03 Team Members/invite-modal.png b/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/03 Team Members/invite-modal.png
new file mode 100644
index 0000000000..497fd7e8cd
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/03 Team Members/invite-modal.png differ
diff --git a/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/03 Team Members/invite-new-members.png b/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/03 Team Members/invite-new-members.png
new file mode 100644
index 0000000000..0b8e74aa02
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/04 Project and Team Management/images/03 Team Members/invite-new-members.png differ
diff --git a/src/data/markdown/docs/03 cloud/05 Billing & User Menu/01 Settings.mdx b/src/data/markdown/docs/03 cloud/05 Billing & User Menu/01 Settings.mdx
new file mode 100644
index 0000000000..1e6acca9d6
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/05 Billing & User Menu/01 Settings.mdx
@@ -0,0 +1,6 @@
+---
+title: "Settings"
+excerpt: ""
+---
+
+Not sure what to put in this doc? You can only change your org name....
diff --git a/src/data/markdown/docs/03 cloud/05 Billing & User Menu/02 Subscription.md b/src/data/markdown/docs/03 cloud/05 Billing & User Menu/02 Subscription.md
new file mode 100644
index 0000000000..3248ec5d8f
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/05 Billing & User Menu/02 Subscription.md
@@ -0,0 +1,26 @@
+---
+title: 'Subscription'
+excerpt: 'Details of what is available within the k6 subscription menu'
+---
+
+## Background
+
+The subscription menu gives you a simple view of the limits of your current plan as well as how many tests remain for your current period.
+
+## Your Subscription
+
+Details of your current active subscription and any add-ons you may have. Note the counter in the right corner displaying usage for the current period of your subscription.
+
+
+
+## Upgrades
+
+If you have paid online with a credit card, you may upgrade at any time. We will prorate your existing subscripition towards the cost of a new one.
+
+## Downgrades
+
+To downgrade your subscription, you may select a new plan at any time. We will bill the new cost immediately and start the new plan at the conclusion of your current period.
+
+## Cancelation / Data Retention
+
+You may cancel your online subscription at any time through the web app. When canceling you are able to opt into Data Retention to save your result data longer term / between projects. If you do not choose data retention all test result data is deleted 7 days after your subscription expires. When canceling you may use your plan through the end of the current period.
diff --git a/src/data/markdown/docs/03 cloud/05 Billing & User Menu/03 Billing.md b/src/data/markdown/docs/03 cloud/05 Billing & User Menu/03 Billing.md
new file mode 100644
index 0000000000..20dbfc4c19
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/05 Billing & User Menu/03 Billing.md
@@ -0,0 +1,41 @@
+---
+title: 'Billing'
+excerpt: 'Update and view information such as Billing address, VAT, Credit Cards, and receipts'
+---
+
+
+
+> VAT for EU Organizations
+> If you are making a purchase from within the EU, Please be sure to enter your VAT number before purchase.
+> If you do not enter a VAT number, we must collect VAT and are unable to refund VAT if you present a valid number later on.
+> Due to regulations, all purchases made within Sweden will include VAT.
+
+
+
+## Background
+
+The billing allows you to manages things such as billing address, VAT numbers, payment methods and receipts. This menu is only accessible to Account Owners and Admins. The billing menu can be accessed by clicking on the Account Menu -> `Billing`
+
+## Billing Information
+
+Use this section to update your billing email, billing address and VAT (if applicable). To update this information click "Update Billing Information"
+
+
+
+---
+
+## Credit Cards
+
+Use this section to add/remove/change your Credit Card on file. When updating cards, please remove old/expired cards to ensure no lapse in service. To add a card, click `Add New Card`
+
+---
+
+## Payments and Receipts
+
+The Payments section will include all of your receipts for purchases made with a Credit Card in our web app. If you have paid via wire / invoice please reach out to support for help on retrieving historical payment details.
+
+---
+
+## Invoicing and Wire Transfer
+
+For annual agreements we do offer wire transfer / invoicing as a method of payment. In order to get started, please reach out to sales@k6.io.
diff --git a/src/data/markdown/docs/03 cloud/05 Billing & User Menu/images/02 Subscription/subscription.png b/src/data/markdown/docs/03 cloud/05 Billing & User Menu/images/02 Subscription/subscription.png
new file mode 100644
index 0000000000..c541d0eba9
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/05 Billing & User Menu/images/02 Subscription/subscription.png differ
diff --git a/src/data/markdown/docs/03 cloud/05 Billing & User Menu/images/03 Billing/update-billing.png b/src/data/markdown/docs/03 cloud/05 Billing & User Menu/images/03 Billing/update-billing.png
new file mode 100644
index 0000000000..5126b6c733
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/05 Billing & User Menu/images/03 Billing/update-billing.png differ
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/01 Pricing.md b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/01 Pricing.md
new file mode 100644
index 0000000000..2e13b0b36e
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/01 Pricing.md
@@ -0,0 +1,65 @@
+---
+title: "Pricing FAQ"
+excerpt: "Frequently asked questions about k6 cloud pricing and subscriptions"
+---
+
+
+This document contains some of our most frequently asked questions about pricing. If your question isn't answered here, please contact [support](mailto:support@k6.io).
+
+
+## Does k6 have a concept of VU Hours?
+
+No - There is no concept of VU hours for our plans. You can run all your tests up to the maximum limits as defined by your subscription.
+
+## What tests count against my monthly limit?
+
+Only tests executed from or streamed to the k6 cloud will count against your total number of tests. Specifically tests run with the `cloud` command or `-o cloud` flag. e.g. `k6 cloud myScript.js` or `k6 run myScript.js -o cloud`
+
+## Can I pay with a PO or Wire transfer?
+
+Yes - please contact sales or support to initiate the process
+
+## Do you offer discounts for: Non-profits? Gov't organizations? Education institutions? Start-ups?
+
+Yes we offer discounts on our base subscriptions. Please contact [sales](mailto:sales@k6.io.com?subject=Not-for-profit%2FStart-up%2FEducational%20Discount%20Inquiry) to confirm your eligibility and a custom quote
+
+## Do plans auto renew?
+
+Yes - our online plans will auto renew, however they can be canceled at any time in app, for any reason.
+
+## Can I upgrade or downgrade my plan?
+
+Yes - You can upgrade or downgrade.
+
+Upgrades result in a prorated amount being applied towards your new plan. For example, if you are 15 days into a month and upgrade, about 50% of your current subscription would be applied towards the new plan's cost.
+
+Downgrades are charged immediately but do not start until the next billing cycle. You retain the ability to test with your current plan until the next billing cycle starts.
+
+## Can I specify a billing contact to receive monthly receipts?
+
+Yes - within the billing menu, you can specify a billing email address. We will send a copy of the receipt to this email upon each renewal.
+
+## How do I get an invoice/receipt for my purchase?
+
+These are located in the billing menu
+
+To get a invoice/receipt for your purchase:
+Click drop down next to your name in app -> billing
+Scroll down to the “Payments” category.
+Click the “View receipt” button.
+
+You can choose to either save or print the receipt. Your receipt number is considered your invoice number.
+
+## My receipt details aren't correct, help!
+
+Please update the billing details in the billing menu. This will regenerate the receipts with proper address, company name and other details.
+
+## I've been charged VAT but have a valid VAT number
+
+For Swedish organizations - we must charge this as we are based in Sweden
+
+For other EU organizations - please be sure to enter your VAT number in the billing menu. If you have forgotten to add your VAT number and it is the same calendar month as your charge, please contact support for potential options on adjusting the charge.
+
+## Do you offer professional services or help with scripting?
+
+Yes - we have a professional services team that can be engaged to assist in your testing. This team can be utilized to just give a hand with scripting or execute entire projects based on your need. Please contact [sales](mailto:sales@k6.io) for more information. Note: Projects need to be scoped and typically require a 1-2 week lead time, depending on availability.
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/02 Data Retention.md b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/02 Data Retention.md
new file mode 100644
index 0000000000..0867928ee2
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/02 Data Retention.md
@@ -0,0 +1,45 @@
+---
+title: "What is Data Retention?"
+excerpt: "What is k6 data retention and how does it work?"
+---
+
+
+
+> ### Data Retention
+>
+> Data is retained on a rolling basis. Test result data older than what is specified in
+> your plan is automatically deleted. If you need to save data, please be sure to export,
+> set the test as baseline, or purchase a period of data retention that meets your
+> requirements.
+
+
+
+
+## What is Data Retention?
+
+Generally speaking data retention is continued storage of an organization's data for various reasons. k6 differentiates between test result data and your user data (projects, profiles, etc.). In terms of data retention, we are referring only to test result data. Your specific user data and test configurations are saved indefinitely on your user profile.
+
+## How do I save my baseline tests?
+
+You can select a single test run per test as a baseline test. The purpose of this is to serve as a point of comparison for future tests. Baseline tests are exempt from data retention rules and are saved indefinitely. To mark a test as baseline, use the three dot menu in the top right corner of your test run and click `Set as Baseline`
+
+
+
+
+## How long is my data retained?
+
+Data is retained on a rolling basis, depending on your subscription. For plans with 1 month of data retention, we will retain data for 30 days from the test run. After that period the data is deleted. This rolling period only applies while you have an active subscription. If you cancel your subscription, data is retained for 7 days past subscription expiration.
+
+> ### Buying Data Retention
+>You can purchase a data retention plan during cancelation of your subscription. This allows you to retain your result data at a lower rate between testing periods.
+
+## Can I export my data?
+
+Yes, data can be exported. To export data from a specific test run, please click on the three dot menu in the top right corner of your test run -> export.
+
+## When can I purchase Data Retention
+
+A period data retention is automatically included in every subscription plan. The time your data is retained depends on specific plan you have purchased. For more info on specific plans and corresponding data retention periods please visit our [pricing](https://k6.io/pricing/) page.
+
+> ### Longer Periods of Retention
+> For periods of data retention longer than specified in our plans, please reach out to support for pricing information.
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/03 IP addresses Used k6 cloud.md b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/03 IP addresses Used k6 cloud.md
new file mode 100644
index 0000000000..0e8b320b82
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/03 IP addresses Used k6 cloud.md
@@ -0,0 +1,34 @@
+---
+title: "What IP addresses are used by the k6 cloud?"
+excerpt: "List of the IP addresses used by the k6 cloud"
+---
+
+This article contains the various IP addresses k6 uses to generate load for cloud based tests and other services. The most common reason for needing this information is to open your firewall to allow tests to reach the staging/test environment from a cloud based test. If you are streaming results to k6 Cloud utilizing `k6 run myscript.js -o cloud` you may only need to allow traffic against ingest.k6.io on port 443.
+
+Other methods, such as header or query parameter whitelisting, may also fit your requirements. Refer to [this article](/cloud/cloud-faq/how-to-open-your-firewall-to-k6-cloud-service-for-cloud-executed-tests) for more information on those methods.
+
+## Load Zones
+
+k6 uses AWS for cloud load generators. For the IP addresses used in the different load zones and filtering methods please refer directly to [Amazon](http://docs.aws.amazon.com/general/latest/gr/aws-ip-ranges.html).
+
+If you prefer to view the ranges directly, within the above link, the [ip-ranges.json](https://ip-ranges.amazonaws.com/ip-ranges.json) file provides the updated list of IP addresses used by our load generators. In order to know which IP ranges can be used, you need to filter the `service` of type EC2 and the `region` of the selected load zone/s in your test configuration.
+
+The zone codes are mapped as follows:
+
+
+Code | Name
+---------------|--------------------------
+us-east-1 | US East (Ashburn)
+us-east-2 | US East (Columbus)
+us-west-1 | US West (Palo Alto)
+us-west-2 | US West (Portland)
+ca-central-1 | Canada (Montreal)
+eu-west-1 | EU (Dublin)
+eu-central-1 | EU (Frankfurt)
+eu-west-2 | EU (London)
+ap-northeast-1 | Asia Pacific (Tokyo)
+ap-northeast-2 | Asia Pacific (Seoul)
+ap-southeast-1 | Asia Pacific (Singapore)
+ap-southeast-2 | Asia Pacific (Sydney)
+ap-south-1 | Asia Pacific (Mumbai)
+sa-east-1 | South America (São Paulo)
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/04 Debugging test scripts.md b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/04 Debugging test scripts.md
new file mode 100644
index 0000000000..22c3e4d73a
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/04 Debugging test scripts.md
@@ -0,0 +1,77 @@
+---
+title: "What is the best way to debug my load test scripts"
+excerpt: "Tips and tricks to help debug your load test scripts efficiently"
+---
+
+## Background
+
+A common task to any development is debugging your code to make sure it's producing the expect output and results. k6 utilizes JavaScript as the scripting language for writing your load tests. Because tests are written in real code, you can and should debug your tests scripts before running them. Utilize the tips in this document to aid you in speeding up your script development.
+
+
+## Tip 1: Use k6 locally to debug your scripts
+
+While there is an code editor built into the k6 web application, it has limited debugging abilities. Using k6 locally, you can actually execute your test scripts on a small scale to quickly see how the script executes.
+
+
+## Tip 2: Debug locally only.
+
+Building on tip 1 above, you want to avoid streaming your results to our cloud using `-o cloud` and avoid running the test using the cloud service. There are two reasons for this:
+
+1. Tests that run in or stream to our cloud will count against any limits you may have
+2. Execution is slower when streaming or executing in the cloud. We want debugging to be a fast iterative process.
+
+
+
+```C
+k6 run myScript.js
+```
+
+
+
+When debugging, you'll likely be making many changes as you work through your scripts to ensure they work as expected. The data sent or run from the cloud won't be of much value, so just keep it local until you need to run a larger test.
+
+
+## Tip 3: Use flags to limit execution when debugging
+
+It's likely that you've configured Virtual Users and/or duration in your script. adding the flags `-i 1 -u 1` will instruct k6 to execute 1 iteration with 1 Virtual User.
+
+
+
+```C
+k6 run myScript.js -i 1 -u 1
+```
+
+
+
+**Note**: 1 Virtual User and 1 iteration is also the default execution for k6. If you have not defined any VUs or iterations in your test, k6 will execute with 1.
+
+
+## Tip 4: Use builtin debugging options
+
+Sometimes you need to understand more details about the requests being sent and response received. Using `--http-debug` as a flag allows you to do just that. You can also print full response bodies by using `--http-debug="full"`
+
+
+
+```C
+k6 run myScript.js --http-debug="full"
+```
+
+
+
+**Note**: If your test script has a large number of HTTP requests, this will produce a large output.
+
+
+## Tip 5: Print info to the terminal window with console.log(); when debugging
+
+Sometimes it's just easier to print some information to the terminal window. Feel free to use `console.log();` to print useful information to the terminal window. Perhaps you want to examine some JSON returned in a response, a specific response body, or even just know if you've correctly entered/exited loops or IF statements.
+
+To take the above a step further, consider the following snippet of code. We are making a GET request, saving the response to `res` and then logging the complete response object. Now we can examine it to find exactly what we may be looking for to adapt our test script.
+
+
+
+```JavaScript
+let res = http.get("http://httpbin.test.k6.io/json");
+ console.log(JSON.stringify(res));
+```
+
+
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/05 I was invited to an organization and I cannot run tests.md b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/05 I was invited to an organization and I cannot run tests.md
new file mode 100644
index 0000000000..2e067b68ad
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/05 I was invited to an organization and I cannot run tests.md
@@ -0,0 +1,41 @@
+---
+title: "I was invited to an organization and I cannot run tests"
+excerpt: "How to correctly specify/choose a correct project to run your tests in k6"
+---
+
+## Purpose
+
+The Organizations, Projects, and Team Member functionality enables account administrators to organize and control access to their k6 account and subscription for collaboration purposes. However, newly invited members, not being familiar with this structure, sometimes ask us questions or get stuck when trying to run tests.
+
+For example:
+
+>I was invited to an organization with a subscription. However, When I try to run tests, I get an error that my subscription doesn't have enough Virtual Users/exceeds the duration/uses too many load zones. Our subscription allows for the test I want to run. What is wrong and how do I fix this?
+
+It's important to note that every user in k6 is an owner of organization. Organizations can contain multiple Projects. Each Project can contain multiple tests. k6 subscriptions are associated with an Organization. In almost all cases, this error is a result of trying to run a test in an Organization without a subscription (likely your own default organization).
+
+## How do I change my Organization to fix this?
+
+
+## In the web interface
+
+If you are running tests from the web interface, you will need to use the menu in the left side bar, to select a project within the organization with a subscription. The ogranization can be changed from the dropdown User menu in the top left corner. In the image below, our user is a member of two organizations, "LoadImpact" and "Second Organization". In this example "LoadImpact" is our primary organization associated with the users account and "Second Organization" is one which the user has been invited to. In order to run tests using the subscription that was purchased and associated with "Second Organization" we would need to select a Project associated with it. Once you have selected the Project, the project ID will be displayed below the project name in your main overview (top left). You can simply click the copy button next to it.
+
+
+
+## From the command line
+
+If you are using k6 to trigger tests from the command line, you will need to specify, in your test configuration, the project that should be used to run a test. By default, k6 will use the default Organization and default Project associated with a users account. In order to do this, the `projectId` must be set as a [test configuration option](/using-k6/options) within the `ext` object of your script.
+
+
+
+```JavaScript
+export let options = {
+ ext: {
+ loadimpact: {
+ projectID: 123456
+ }
+ }
+}
+```
+
+
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/06 What's the Difference Between k6 Cloud's Version 3.0 (Lua) and 4.0(JavaScript).md b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/06 What's the Difference Between k6 Cloud's Version 3.0 (Lua) and 4.0(JavaScript).md
new file mode 100644
index 0000000000..eedb936000
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/06 What's the Difference Between k6 Cloud's Version 3.0 (Lua) and 4.0(JavaScript).md
@@ -0,0 +1,346 @@
+---
+title: "What's the Difference Between LoadImpact's Version 3.0 (Lua) and k6 Cloud 4.0(JavaScript)"
+excerpt: "A brief overview of the differences between LoadImpact 3.0 (Lua) and k6 Cloud 4.0 (JS/k6) products"
+---
+
+## Purpose
+
+An overview of some key differences between k6 Cloud (JS) and LoadImpact (Lua)
+
+## Overview
+
+From a general performance testing perspective the 3.0 and 4.0 products are more or less the same:
+
+1. You create one or more user scenarios that step through a particular process of your target system that you want to test.
+2. You then combine your user scenario(s) with a traffic simulation profile that specifies how many Virtual Users (VUs) should be concurrently executing the user scenario(s) at different points in time of the test.
+3. You execute your test, metrics data is collected and you are presented with results.
+
+## Differences between 3.0 and 4.0 products
+When looking more closely though there are some differences in how you accomplish step 1, 2 and 3 above.
+
+A big difference is in the workflow that you can accomplish with each respective product.
+
+The 3.0 product is completely cloud based, user scenarios and test configuration are created/edited and stored in the LoadImpact cloud service, and running tests is also done exclusively from the cloud. This also means the target system that is being tested needs to be accessible from the public Internet.
+
+With the 4.0 product we've opened up the platform in two important ways. First, the core load testing software, [k6](https://github.com/loadimpact/k6), is now an open source tool, and secondly it can be used both [on-premise](/getting-started/running-k6) as well as from the k6 Cloud service via our [cloud execution](/using-k6/cloud-execution) functionality. The user scenarios and test configuration has been merged into one, it's now all just JavaScript code, so very version control friendly.
+
+This brings us to an important difference, in the 4.0 product you're responsible for storing and version controlling your tests (the JavaScript combining user scenario and test configuration), and the k6 Cloud service can provide you with result storage, visualization and trending, as well as geographically distributed cloud execution of tests.
+
+## User scenario
+
+In the 3.0 product user scenarios described using Lua code. You can end up with the Lua code in various ways, by using one of the recorder options, the Postman converter or hand coding it, but at the end of the day the output of all these various ways is a piece of Lua code.
+
+In the 4.0 product user scenarios are described using JavaScript, the ES6 version of JS to be precise. This means it's not only a more familiar language to most developers/testers but it also introduces a nice addition compared to the 3.0 product: support for modules, allowing code to be modularized and reused across tests and teams.
+
+See the Lua to JS migration guide down below for more information on how to migrate your Lua user scenarios to JS.
+
+## Test configuration
+
+In the 3.0 product you compose one or more user scenarios into a separate entity known as a "Test" (aka "Test configuration"), and then add additional configuration like traffic simulation profile and thresholds. This is done through the LoadImpact WebApp UI.
+
+In the 4.0 product the equivalent configuration options are specified in the script itself:
+
+
+
+```JavaScript
+export let options = {
+ // Stages represents the traffic ramping profile that will be used in the test,
+ // controlling the VU concurrency throughout the duration of the test
+ 'stages': [
+ // Linear ramp-up from 0 to 50 VUs for 60s
+ { 'target': 50, 'duration': '60s' },
+
+ // Stay constant at 50 VUs for 60s
+ { 'target': 50, 'duration': '60s' },
+
+ // Linear ramp-down from 50 to 0 VUs for 60s
+ { 'target': 0, 'duration': '60s' }
+ ],
+
+ // Use thresholds to set your metric targets, thresholds are used to pass/fail tests
+ // and for controlling automatic test termination
+ thresholds: {
+ // Add a threshold mark test as failed is 95th percentile of overall response time goes above 500ms
+ 'http_req_duration': 'p(95)<500',
+
+ // Add another threshold to fail and abort the test if the threshold hits 1s
+ 'http_req_duration': {'trigger': 'p(95)<1000', 'abortOnFail': true}
+ }
+};
+```
+
+
+
+***
+
+## Lua to JS migration guide
+
+Lua and JavaScript (JS) share most of the fundamental logical constructs and control flow mechanisms that are commonly found in general purpose programming languages. Same goes for the load testing oriented APIs that we've added in each respective product. This section will look at how to convert Lua APIs into the JS equivalent.
+
+## High-level differences
+
+On highest level there are some differences to be aware of before we continue on into more details.
+
+## Loading of builtin modules and APIs
+
+In Lua all the available functionality is loaded by default, APIs can be called right away without explicit loading/importing, while In JS you need to explicitly import the builtin modules and APIs that you want to use:
+
+
+
+```lua linenos
+http.get("https://test.k6.io/")
+client.sleep(3)
+```
+
+```JavaScript
+import {sleep} from "k6";
+import http from "k6/http";
+export default function() {
+ http.get("https://test.k6.io/");
+ sleep(3);
+}
+```
+
+
+
+## Scope of VU code
+
+In Lua VUs execute the script from top to bottom over and over, while in JS VUs execute the global scope (aka "init code") once to initialize, and then executes the "main function" (`export default function`) over and over:
+
+
+
+
+```lua linenos
+// The VU code is the same as global scope, and gets run over and over by a VU
+client.sleep(3)
+```
+
+```JavaScript
+// Imports and other global scope code
+export default function() {
+ // The VU code, that gets run over and over by a VU
+}
+```
+
+
+
+
+## Converting Lua APIs to JS APIs
+
+## Client sleep/think time
+
+Below you have examples on how to have a VU sleep or think for a specific amount of time (in the example below for 3 seconds), pausing the VU execution:
+
+
+
+
+```lua linenos
+client.sleep(3.0)
+```
+
+```Java
+import {sleep} from "k6";
+export default function() {
+ sleep(3);
+}
+```
+
+
+
+
+## Making requests
+
+To make HTTP requests there are a number of different Lua APIs available. In the end they're all wrappers around the `http.request_batch()` API. Below you can see a comparison for Lua and JS:
+
+
+
+```lua linenos
+-- Send a single GET request
+http.get("https://httpbin.org/")
+-- Send a single POST request
+http.post("https://httpbin.org", "key=val&key2=val")
+-- Send several requests in parallel
+http.request_batch({
+ { "GET", "https://httpbin.org/" },
+ { "POST", "https://httpbin.org/", "key=val&key2=val" }
+})
+```
+
+```JavaScript
+import http from "k6/http";
+export default function() {
+ // Send a single GET request
+ http.get("https://httpbin.org/");
+ // Send a single POST request
+ http.post("https://httpbin.org", "key=val&key2=val");
+ // Send several requests in parallel
+ http.batch([
+ "https://httpbin.org/",
+ { method: "POST", url: "https://httpbin.org/", body: "key=val&key2=val" }
+ ]);
+}
+```
+
+
+
+
+
+See the [HTTP API](/using-k6/http-requests) docs for k6 for more information and examples.
+
+## Group requests and logic into transactions/pages
+In the 3.0 product there's a concept of pages. Lua code in between calls to `http.page_start()` and `http.page_end()` will be be measured to provide a page load times in the results. The equivalent in JS would be to use [`Groups`](/using-k6/tags-and-groups#groups):
+
+
+
+```lua linenos
+http.page_start("My page")
+http.get("https://httpbin.org/")
+http.request_batch({
+ { "GET", "https://httpbin.org/" },
+ { "GET", "https://httpbin.org/get" },
+})
+http.page_end("My page")
+```
+
+```JavaScript
+import http from "k6/http";
+export default function() {
+ group("My page", function() {
+ http.get("https://httpbin.org/");
+ http.batch([
+ "https://httpbin.org/",
+ "https://httpbin.org/get",
+ ]);
+ });
+}
+```
+
+
+
+
+## Data store
+
+In the 3.0 product there's a concept of a Datastore. A CSV file that you can upload to the service and then attach to your user scenario for accessing and using the data in your user scenario logic.
+
+In the 4.0 product there's no specific concept of a Datastore, but in k6 you have two different ways to separate test parameterization data from script logic.
+
+Both of the examples below can be run with:
+
+
+
+```shell
+k6 run --vus 3 --iterations 3 script.js
+```
+
+
+
+
+## Use the open() scripting API to open a CSV/JSON/TXT file:
+
+more info here: [open](/javascript-api/init-context/open-filepath-mode)
+
+
+
+```json
+[
+ {
+ "username": "user1",
+ "password": "password1"
+ },
+ {
+ "username": "user2",
+ "password": "password2"
+ },
+ {
+ "username": "user3",
+ "password": "password3"
+ }
+]
+```
+
+
+
+
+
+
+```JavaScript
+import { sleep } from "k6";
+const users = JSON.parse(open("./users.json"));
+export default function() {
+ let user = users[__VU - 1];
+ console.log(`${user.username}, ${user.password}`);
+ sleep(3);
+}
+```
+
+
+
+
+## Put the data in a JS file and import it as a module:
+
+
+
+```JavaScript
+export let users = [
+ {
+ "username": "user1",
+ "password": "password1"
+ },
+ {
+ "username": "user2",
+ "password": "password2"
+ },
+ {
+ "username": "user3",
+ "password": "password3"
+ }
+];
+```
+
+
+
+## Main Script:
+
+
+
+
+```JavaScript
+import { sleep } from "k6";
+import { users } from "./userData.js"
+export default function() {
+ let user = users[__VU - 1];
+ console.log(`${user.username}, ${user.password}`);
+ sleep(3);
+}
+```
+
+
+## Custom metrics
+
+Beyond the standard metrics collected by the 3.0 product you can also collect custom metrics using the `results.custom_metric()` API in the example below. The equivalent in JS would be to use the [`Trend`](/javascript-api/k6-metrics/trend-k6-metrics) custom metric:
+
+
+
+```lua linenos
+-- Track the time-to-first-byte (TTFB)
+local res = http.get("https://httpbin.org/")
+result.custom_metric("time_to_first_byte", res.time_to_first_byte)
+```
+
+```JavaScript
+import {group} from "k6";
+import http from "k6/http";
+import {Trend} from "k6/metrics";
+let ttfbMetric = new Trend("time_to_first_byte");
+export default function() {
+ group("My page", function() {
+ let res = http.get("https://httpbin.org/");
+ ttfbMetric.add(res.timings.waiting);
+ });
+}
+```
+
+
+
+
+For more information, see our docs on [custom metrics](/using-k6/metrics#custom-metrics) (Additional metrics for `Counter`, `Gauge` and `Rate` are available beyond the `Trend` one used above).
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/07 Filtering Domains.mdx b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/07 Filtering Domains.mdx
new file mode 100644
index 0000000000..7ed9868057
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/07 Filtering Domains.mdx
@@ -0,0 +1,32 @@
+---
+title: "Why should I filter domains in my load test?"
+excerpt: "Reasons why you should filter domains in your load test and how to achieve that both in-app and using k6 locally"
+---
+
+TODO fix links, We should consider how much we need this article. References things only related to v3.0 - e.g. enter website urls
+
+## Background
+
+Nearly all apps, sites, and services on the web make requests to external resources. It's likely that an app or site you are testing includes any or all of the following: CDNs, marketing trackers (Facebook, Twitter, etc.), analytics tools (Google Analytics), marketing automation trackers (Hubspot, Marketo, Eloqua), developer tools (Fullstory, HotJar, etc.) and more.
+
+
+## Why should I filter domains?
+
+You should filter your domains for all or some of the following reasons:
+
+- External resources may generate unique IDs, which could produce a lot of extra URLs. This would make reviewing results harder to parse through
+- External resources may throttle your requests generated from a load test, this could produce skewed results or even trigger a threshold when it should not have been.
+- External resources typically don't want to be a part of a load test and including them may violate your TOS with that third party
+- In most cases, even if you did notice a performance problem with a 3rd party you normally will have no way to fix that issue
+- Most CDNs charge based on usage, so making requests against the CDN could have financial costs associated with the test. *Note:* You may have a valid reason to test your CDN. We have seen users specifically test CDNs in the past for various reasons.
+
+## Filtering test scripts created in-app
+There are a few ways to filter your domains with k6. If you are using the URL analyzer or HAR file upload in-app, you can utilize the `Domain` option. This allows you to enter domains you specifically want to include within your test. e.g. If I was testing `app.example.com` I would use `example.com` in the `Domain` option.
+
+For both the in-app URL analyzer and HAR file upload, we would ignore requests to other domains when creating the script.
+
+If you are scripting in-app, it's unlikely you would manually write those third party requests, therefore no filter option is needed
+
+## Filtering when using k6's built in HAR file converter
+
+If you are not using one of the in-app options to create your script, you are likely using the built in HAR file converter available in k6. You can convert a HAR file from the command line using `k6 convert myHarFile.har -O myScript.js`. This command would convert the entire contents of the target HAR file in a script with the output denoted.
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/08 Open Firewall k6 Cloud.md b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/08 Open Firewall k6 Cloud.md
new file mode 100644
index 0000000000..383e5ef46b
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/08 Open Firewall k6 Cloud.md
@@ -0,0 +1,92 @@
+---
+title: "How to Open Your Firewall to k6 Cloud service For Cloud Executed Tests"
+excerpt: "A guide with different methods to open your firewall to k6 cloud service execution traffic for load testing"
+---
+
+## Purpose
+
+If you are running a `k6 cloud` test, you will be utilizing k6's cloud infrastructure. These are dynamically allocated from our cloud providers and we do not know the source IP until the test is running.
+
+**If you are streaming results to k6 Cloud utilizing `k6 run -o cloud myscript.js` you SHOULD NOT need to whitelist anything.**
+
+To open your firewall to k6 cloud traffic, you have multiple options. Depending on your business needs, one may be a better fit than another.
+
+
+1. Open up your firewall to the whole range of AWS IP addresses used by the load zones where you want to run your load test from. To use AWS to generate traffic, you will have to open up your firewall to a large IP range.
+
+2. Use HTTP headers, URL query parameters, or unique data that identifies the traffic as belonging to your load test, This requires that your firewall has support for scanning application payload data and apply rules based on what it finds.
+
+
+Here is a more samples on how to complete the different options above:
+
+## Opening up your firewall to all IPs potentially used in the test
+
+We list the full range of IP addresses used in [this article](/cloud/cloud-faq/what-ip-addresses-are-used-by-the-k6-cloud). You will need to whitelist the IP ranges for the load zones you are utilizing. Please note the JSON file at the bottom of the article.
+
+## Using HTTP headers or URL query parameters to send an identifying token
+
+You can add custom HTTP headers to any request in your script. You'll need to add the header to every single request.
+
+
+
+```JavaScript
+import http from "k6/http";
+
+export default function() {
+ var url = "http://test.k6.io/login";
+ var payload = JSON.stringify({ email: "aaa", password: "bbb" });
+ var params = { headers: { "Content-Type": "application/json" , "Myheader": "TOKEN_STRING"} }
+ http.post(url, payload, params);
+};
+```
+
+
+
+
+If you're not dependent on having the simulated users in your load test to be a certain user agent, you can also use the `userAgent` option to set the "User-Agent" header for all subsequent requests. That header could then contain your token value and you would not have to modify every single HTTP request in your script. In the below example the user agent is set to `MyK6UserAgentString/1.0`
+
+
+
+```JavaScript
+// Set a custom User Agent globally in your test options.
+export let options = {
+ userAgent: "MyK6UserAgentString/1.0"
+};
+```
+
+
+
+
+You might also use query parameters, if it doesn't interfere with the functionality of your application:
+
+
+
+```JavaScript
+// Add query parameters to your requests with a unique piece of data
+export default function() {
+ http.get("http://test.k6.io/?firewall_token=TOKEN_STRING");
+}
+```
+
+
+
+Another option would be to request content from a certain hostname that is not in the DNS, but your site would of course need to be configured to respond to requests for that hostname. This is how you do it on the k6 cloud's side:
+
+
+
+```JavaScript
+// In your options, map your a unique/unused/secret hostname to the IP of the server.
+export let options = {
+ hosts: {
+ "https://very_difficult.to.guess.hostname.com": "1.2.3.4"
+ }
+};
+// Make your requests to that hostname
+export default function() {
+ http.get("https://very_difficult.to.guess.hostname.com/");
+}
+```
+
+
+
+Of course, this last solution requires that your firewall terminates SSL traffic, otherwise it will not see the Host header in unencrypted form. You could also use unencrypted HTTP, but get a bit less security.
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/09 Calculating Virtual Users.md b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/09 Calculating Virtual Users.md
new file mode 100644
index 0000000000..87438c5228
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/09 Calculating Virtual Users.md
@@ -0,0 +1,73 @@
+---
+title: "Calculating Virtual Uses with Google Analytics"
+excerpt: "A guide on how to use data from Google Analytics to calculate the number of Virtual Users required for load and performance testing with k6 Cloud."
+---
+
+To calculate the number of Virtual Users you need to test with, you should consider this formula:
+
+>Virtual Users = (Hourly Sessions x Average Session Duration in seconds) / 3,600
+
+Google Analytics and other tools are great for seeing where your users are coming from, and it also offers plenty of data that can help you create realistic load tests.
+
+Google Analytics tracks new visitors (“Users”) and how long they stay on your site. During their stay — the "Session" — they will perform actions (page loads, AJAX requests) that cause traffic to be generated that will load your servers.
+
+The formula above takes it's inputs and determines how many users are in the middle of a "Session" at any one time, meaning they are currently active on the site and generating traffic the servers have to handle.
+
+
+**Where do I get this data in Google Analytics?**
+
+
+1. Login to your Google Analytics account
+2. Click the “Reporting” tab across the top
+3. Select “Audience” in the sidebar menu
+4. Click “Overview”
+5. Set the time period you want to base your data on in the top-right corner
+6. And the data you need is right in front of you!
+
+Check out the screenshot below to get an idea of the view and where you’ll find sessions and average session duration.
+
+**Note:** This screenshot is from a small site’s Google Analytics dashboard. We redacted the name in the top-right corner in the name of privacy!
+
+
+
+And that’s it! As you can see, Google made this data pretty easy to find.
+
+### How to design your load test
+
+One good way of determining what traffic to subject your site to during a load test is to check your peak hours of traffic in Google Analytics, figure out how many sessions you faced then, and perform a test that generates a similar kind of load in a similar [pattern](/test-types/introduction). You probably want to add some margin to it also — to ensure that you can handle higher traffic levels than your latest peak.
+
+The reason we want to find the traffic peak and not just use the average level for the whole month is that, in most cases, average traffic will be quite low. It’s quite common for sites to have regular, recurring peak periods where they experience maybe 2-3x the average traffic levels, so it is important to test for that level of traffic, at the very least.
+
+Some sites or services may also have occasional (or regular) extreme traffic peaks. It can be due to the nature of the site — perhaps it’s a new release your customers are eager to try, your app that is gaining popularity as the next bit thing, a site declaring the result of an election, or a site selling concert tickets that are released at a certain date and time.
+
+It can also be because of user behaviour. Maybe your app provides dinner recipes, which means everyone logs on just before dinner. Regardless, such sites can have peaks that are much higher than 10x the longtime average for the site. In this case it is even more important to load test at traffic levels way beyond the average to ensure the system doesn’t break down when it counts.
+
+So how do we do this? Here’s the Google Analytics dashboard for a small example site that has had a (relatively) big traffic spike. For this little site, nearly 40 percent of November 2015’s traffic came on a single day — Nov. 25. Keep in mind this is example data for a super small site.
+
+
+
+Here’s the basic math we used to analyze the data:
+
+The site averaged .08 concurrent sessions for the entire month.
+
+- 2,591 **monthly** sessions x 82 seconds per session / 3600 = 59.0172
+- 59.0172 / 720 (30 days in November x 24h per day = 720) = .08 average concurrent users in November
+
+
+However, if you calculate the average concurrent sessions for just Nov. 25, you get 1.05 — that is more than 10x the monthly number. And if you calculate the average concurrent sessions between 3 p.m. and 4 p.m. on that day, when most of the traffic spike happened, the average number of concurrent sessions is 7.2. While not a huge number by itself, it is almost 100x the monthly average.
+
+
+
+This illustrates how important it is to look at the right numbers, or the right time frames, when designing your load test. Even if you do not have a huge spike like in this case, chances are that you will still see temporary peaks that can reach perhaps 10x your average traffic level.
+
+No matter the size of your company or the amount of traffic you typically handle, a sudden increase in traffic by nearly 100x definitely has the potential to degrade performance for the user, so spike tests are always a good idea before marketing initiatives, funding announcements, new feature rollouts and just for the sake of always being prepared.
+
+
+***
+
+See also:
+- [Virtual Users](/cloud/cloud-faq/what-are-vus-virtual-users)
+- [Creating tests in k6 Cloud](/cloud/creating-and-running-a-test)
+- [Code Samples and Scripting Examples](/examples)
+- [Test configuration options](/using-k6/options)
+- [Test ramping configurations](/test-types/introduction)
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/11 Test status codes.md b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/11 Test status codes.md
new file mode 100644
index 0000000000..580be63b4e
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/11 Test status codes.md
@@ -0,0 +1,74 @@
+---
+title: "Test Status codes"
+excerpt: "List of the different test statuses that can be returned by k6 cloud service, with reasons and fixes for dealing with such a status."
+---
+
+## Purpose
+
+Explanation of the different test statuses in k6 along with the code returned. The code returned here is different than what is returned by k6.
+
+Status | Description
+-------|------------------------
+-2 | Created
+-1 | Validated
+0 | Queued
+1 | Initializing
+2 | Running
+3 | Finished
+4 | Timed out
+5 | Aborted by user
+6 | Aborted by system
+7 | Aborted by script error
+8 | Aborted by threshold
+9 | Aborted by limit
+
+
+
+Every successful test, will go through the following statuses. The time from Created -> Running, is typically very short and hardly noticeable as you use the platform.
+
+## Created
+A test that is newly created, but has not yet been validated.
+
+## Validated
+A test which has finished initial validation, but has not been queued to run yet.
+
+## Queued
+A test which has entered our queue. Once it is picked up by a test worker, it will begin initializing.
+
+## Initializing
+A test which has been assigned to Load Generators, but has not yet started to make HTTP requests.
+
+## Running
+A test which is actively making HTTP(s) or websocket requests
+
+## Finished
+A test which has finished running. If thresholds were used, no thresholds have failed.
+
+***
+
+When a does not finish as expected, you the test will have one of the following statues.
+
+
+## Timed Out
+A test which has not received or sent any information for a long time
+
+## Aborted (by user)
+A test which was aborted by the user. Tests aborted by user count against your total usage.
+
+## Aborted (by system)
+A test that was aborted by the system. These tests typically abort due to a fatal error occuring. If the test fails before launch, there may be an underlying issue with the Load Zone, unrelated to k6. If the test aborts during execution, it may be due to overutilization of the Load Generators. In this case, we suggest you look at the CPU and Memory utilization and add or increase sleep times. You may also want to set the option `discardRepsonseBodies` to `true`, to lower memory pressure.
+
+## Aborted (script error)
+A test that was aborted due to an error in your script. For example, if you were to capture data from the response body of a request that you reuse in a future request. If the first request were to fail, your future request would contain a null value. Sudden script errors can suggest a performance issue. Fix the performance issue or add error handling to account for these cases.
+
+## Aborted (by threshold)
+A test that exceeded your defined threshold value and that threshold was given the option to automatically abort the test.
+
+## Aborted (by limit)
+A test that has exceeded one or more of the following limits:
+- There are "too many" (>40) groups in a test
+- There are "too many" (>10,000) metrics reported
+- The duration is longer than 60 mins (for tests longer than 60 min, please contact us)
+- The max VUs is higher than 20,000 VUs (for tests higher than 20k, please contact us)
+
+If your test has too many groups, please reduce their number. If your test has too many metrics, please use URL grouping to combine similar URLs. You should also remove external requests from your test script. Each URL captured will account for 7 individual metrics that we keep track of. External requests can quickly produce a large number of metrics that aren't helpful to the understanding performance of the System Under Test.
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/12 Virtual Users.md b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/12 Virtual Users.md
new file mode 100644
index 0000000000..b13dce5ddc
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/12 Virtual Users.md
@@ -0,0 +1,44 @@
+---
+title: "What are VUs (Virtual Users)?"
+excerpt: "Definition of what Virtual Users are in context of k6 cloud service."
+---
+
+## Background
+
+k6 cloud's definition of Virtual Users(VUs) along with supplemental information to cover common questions about VUs.
+
+Virtual Users(VUs) are the entities in k6 cloud service that execute your test script and make HTTP(s) or websocket requests. VUs are concurrent and will continuously iterate through the default function until they ramp down or the test ends. Any number of VUs will create a number of sessions a factor larger than their total count, depending on test and script length.
+
+For example, if you ran a test with 10 VUs for 10 minutes and the default function took each VU 30 seconds to complete, you would see roughly 200 completions/total sessions generated from this test. This is approximate and will vary based on your ramping configuration.
+
+## Virtual Users in context of Web Apps/Websites
+
+Virtual Users are designed to act and behave like real users/browsers would. That is, they are capable of making multiple network connnections in parallel, just like a real user in a browser would. When using a `http.batch()` request, HTTP requests are sent in parallel. Further, you can even control the specifics of this behavior through the [batch](/using-k6/options#batch) and [batchPerHost](/using-k6/options#batchPerHost) options. The default is 10 connections in parallel and per host.
+
+
+
+
+## Virtual Users in context of APIs
+
+When testing individual API endpoints, you can take advantage of each VU making multiple requests each to produce requests per second(rps) a factor higher than your VU count. e.g. Your test may be stable with each VU making 10 rps each. If you wanted to reach 1000 RPS, you may only need 100 VUs in that case. This will vary based on what you are testing and the amount of data returned. For more information on testing APIs, please refer to our article [API Load Testing](/testing-guides/api-load-testing)
+
+
+Virtual Users are using multiple parallel network connections, they will be opening multiple concurrent network connections and transferring resources in parallel. This results in faster page loads, more stress on the target server, and more realistic result data set. **Not all load testing tools operate in this more complex and realistic fashion**
+
+## Calculating the number of Virtual Users needed
+
+Calculating the number of virtual users can be done by using this formula:
+
+
+
+```
+VUs = (hourly sessions * average session duration in seconds)/3600
+```
+
+
+
+You may want to use this formula multiple times, with different data such as sessions in your busiest/peak hour, a normal hour, etc. When setting up your test, if your user journey (default function) mimics an average user session, the number of VUs determined by the formula would produce the amount of sessions you entered if you ran your test for an hour.
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/13 (find new home) load-testing-methodology.mdx b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/13 (find new home) load-testing-methodology.mdx
new file mode 100644
index 0000000000..8abb1caba6
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/13 (find new home) load-testing-methodology.mdx
@@ -0,0 +1,75 @@
+---
+title: Basic Load Testing Methodology
+excerpt: A simple methodology to help you start testing
+---
+
+
+## Purpose
+
+This methodology is intended to walk through the high level steps that will help you get started and running more meaningful tests, faster. The best tests are the ones that simulate the most realistic conditions and user behavior. However, simple testing is better than no testing. It's easy to become overwhelmed if you attempt to do everything all at once. Treat testing like you would development, _start small and iterate, iterate, iterate._
+
+## What am I testing?
+
+If you have not already, you start thinking about any or all of the following:
+
+* What are my users doing?
+ - What specific actions do they take the most?
+ - What actions are most critical on my app/website?
+* What API endpoints are utilized the most?
+ - What API endpoints are most critical?
+* What response times are acceptable to my users?
+ - What response times are acceptable to me?
+* What SLAs, contracts, or regulations must I adhere to?
+
+### When do I run tests?
+
+As the world shifts left with DevOps, testing earlier in the development cycle is becoming the best practice. After an initial testing project, many users decide to continue to run tests, at a smaller scale, so they can identify performance issues immediately after the line of code that caused it, is built.
+
+### Virtual Users or Requests Per Second?
+
+This depends on what you are testing, and there is a good chance you want to think in terms of both. Virtual Users are complex workers that can open multiple connections in parallel, thus making multiple requests per second.
+
+## Virtual Users
+When testing anything that is "User journey" related, webapps, websites, or API endpoints in a specific order you should think in terms of Virtual Users. It's important to also note that Virtual Users are concurrent, unless specified otherwise, they will continue to iterate through their script until the test is complete. A small number of Virtual Users can create a number of sessions magnitudes higher than their count. _This is a very common point of overestimation we see._
+
+## Requests per second
+When testing individual API endpoints it makes more sense to think in terms of request per second. As mentioned above, LoadImpact's virtual users are complex and can make multiple requests per second each. For example, if you wanted to test an endpoint with 100 rps you wouldn't need 100 virtual users in nearly all cases.
+
+## Calculating Virtual Users
+
+We recommend looking into any analytics tools you may have. If they list any metric for concurrent users, you can likely use that. If not, you can instead look for:
+
+- Hourly Sessions
+- Average session duration for those Users
+
+If you are looking to stress your system to see what happens as you exceed your busiest traffic, you should look for the Peak hourly sessions and it's coordinating average session duration.
+
+For any tests you may run on a regular basis, as part of a CI pipeline or otherwise, you should instead look for your average hourly sessions, or lower, depending on your needs.
+
+With these two metrics, you can determine the number of Virtual Users needed, using the following formula:
+
+`VUs = (Peak Hourly Sessions * Average Session Duration in Seconds) / 3600`
+
+Depending on your goals, you may want to increase this number to provide a cushion beyond your expectations (15-30% is typical).
+
+## Plan your first tests
+
+You should plan to run the following tests:
+
+1. Baseline Test "Performace under ideal conditions"
+ - Small test relative to your total Virtual User needs
+ - Run for at least 5-10 minutes
+ - Used to set a baseline metric for you to compare future tests against
+ - You may want to set Thresholds based on this test
+2. Stress test "Where do things break?"
+ - The test you likely will run the most
+ - Used to gradually increase load and identify performance problems
+ - Iterate often- analyze results, make changes, verify with another test
+3. Load Test "Are my changes working?"
+ - The test you run to see if all your hard work in step 2 paid off
+ - Used to verify your system handles the load through the entire test
+4. Continuous test "Is my performance regressing as new code/infrastructure updates are deployed?"
+ - Used to monitor for performance regressions over time
+ - Larger than a baseline test but smaller than a load test
+ - Most commonly integrated as part of automation, such as a CI pipeline
+
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/14 (find new home) General Methodology.mdx b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/14 (find new home) General Methodology.mdx
new file mode 100644
index 0000000000..e666088ca8
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/14 (find new home) General Methodology.mdx
@@ -0,0 +1,95 @@
+---
+layout: classic-docs
+title: General Methodology
+description: A general load testing methodology to help you strategize and get started.
+categories: [guides]
+order: 0
+redirect_from:
+ - /4.0/testing-methodologies/application-testing-methodology/
+ - /4.0/testing-methodologies/
+---
+
+
+## Background
+
+This guide will speak from a high level on topics related to Load and Performance testing.
+
+As developers and testers we love to jump straight into the tools, but successful testing starts with successful planning. The intent of this document is to provide additional guidance to best prepare you for testing and to meet your testing goals. This methodology assumes that some initial performance testing is being done before automating testing in a CI/CD pipeline. Testing can be broken down into the following phases.
+
+
+## Phase 0 - It's a new tool, go explore!
+
+If you are like us here at k6. You will probably want to spend some time clicking around, trying things without any guidance, and seeing what happens when you click that button (yes, that one). Go ahead and do that, think of questions you may have about the tool, they may get answered here. If not, you can always ask us.
+
+
+## Phase 1 - Planning, Test Configuration, and Validation
+
+Phase 1 is further broken down into multiple parts. Before you get started with real testing, we recommend that you:
+
+### Planning
+
+- What are the most important parts of the system I am testing?
+ - _Perhaps a handful of API endpoints are most critical to the proper function of your system. e.g. Login_
+- What are my expected outcomes?
+ - _Is there historical performance testing or events I can use to predict initial outcomes? e.g. During a new release we experienced heavy traffic of users trying our new features out, the response times of our login process was degraded_
+- What other business rules should I consider?
+ - _Has the business implemented any SLAs or other rules that relate to performance. e.g. certain response times, availability, etc._
+- What environment am I testing?
+ - _Production? A testing environment that is identical to production? A testing environment that is scaled down?_
+ - _A test environment that matches production is the most ideal, but not always available. What other considerations should I make to ensure that users aren't disrupted_
+
+
+Once you have thought about these high level items, you should next move into creating and configuring your tests.
+
+### Test Configuration
+
+Here are some tips to consider when you start to write/record your first test script:
+
+- What are the most common actions my user take as they relate to the important components I want to test?
+ - _Login generally tends to be one important part of an application. What are others? If testing user journeys, what logical order do users of your application follow?_
+ - _Don't get caught up on trying to test everything, focus on the most important parts. So skip(for now) that feature that a handful of users use on a regular basis_
+- How much load should I put on my target system?
+ - _If testing individual components, such as API endpoints, think in terms of requests per Second_
+ - _If testing user journeys, such as users visiting page to page or taking multiple actions, think in terms of concurrent users._
+- What test patterns will I execute?
+ - _Different tests will tell you different things. Refer to this article for an explanation of the different patterns:_ [Ramping configurations](/test-types/introduction)
+ - _Generally, this order will work for most when establishing a testing process: Baseline test, stress test, load test, spike test (if applicable)_
+- What data do I need?
+ - _Using the login example, testing the same user logging in will usually only test how well your system can cache responses. What data do you need to be parameterized to support your test executions?_
+
+Keeping the above tips in mind, you should now start writing your scripts by hand or by using our [Chrome extension](/chrome-extension/) or the built in [HAR file converter](/using-k6/session-recording-har-support)to create a test script. **It's best to start small and then expand your test coverage. Treat writing your test scripts as you would code you are writing for your application by breaking it down into smaller, more manageable pieces.** A lot of value can be found in simple tests against the most important components.
+
+**Best Practice Alert:** k6 supports modularization. The script you write to perform a login function with a random user, can later become it's own module in your load testing library. This can be shared with your team through version control or other means.
+
+### Validation
+
+As you are writing your scripts and before you run them at a larger scale it's important to run validations (smoke tests) to ensure that the test works as intended. These tests are typically run with a very low number of Virtual Users for a very short amount of time. For speed, it's recommended you run these locally with output to `stdout`. In other words, don't run a validation using cloud infrastructure or streamed into the cloud for analysis.
+
+## Phase 2 - Baseline Testing, Scaling your tests, and Complex Cases
+
+If you take the time to consider all your initial planning, the next steps become much easier to complete and require less specific instruction.
+
+### Baseline testing
+
+It's not recommended to run your first test at full throttle and the maximum number of Virtual Users. When testing, it is critical to always be thinking comparatively. Your first test should then be a **baseline test.** A baseline test is a test at an ideal number of concurrent users/requests per second and run for a long enough duration to produce a clear stable result. This baseline test will give you initial performance metrics for you to compare to future test runs. The ability to compare will help highlight performance issues more quickly.
+
+**Common question** "What is ideal as it relates to baseline testing above?"
+**Answer:** A number of Virtual Users you know your system can handle well. If you aren't sure, try 1 - 10 Virtual Users.
+
+**For example:** Does your application generally receive 500 concurrent users at any given time? You should be running your baseline tests below that.
+
+### Scaling your Tests
+
+After your baseline tests are complete, it's time to start to run larger tests to detect performance problems. You should utilize the Stress Test pattern to scale / step your tests up through different levels of concurrency to detect degradations. As you find these degradations and make adjustments you will iterate on the test multiple times until a satisfactory level of performance is met.
+
+Regardless of the complexity of your test cases, the testing you do in this step will provide the most information to take action on to improve performance. Once you have finished iterating through tests at this stage, verified by acceptable test runs that finish, you should move to a load test pattern to verify the system under test can handle an extended duration at the goal level of load.
+
+### Complex Cases
+
+If, like most users, you started by writing small simple test cases, now is the time to expand them. You may want to repeat this second phase to look for deeper performance problems, ones that could only show up when multiple components are under load.
+
+**Best practice Alert:** If you have been modularizing your test scripts up to this point, this step may be as easy as including them into a "master" test case.
+
+## Phase 3 - Ongoing performance Testing
+
+The final step in our methodology is one that is long term. Up until now you have spent a good deal of time and effort creating your test cases, executing your tests, fixing various issues, and improving performance. You can continue to receive returns on the time investment made by including performance testing into your Continuous integration pipelines or just by running tests on a regular basis manually.
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/02 data retention/set-as-baseline.png b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/02 data retention/set-as-baseline.png
new file mode 100644
index 0000000000..d359273865
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/02 data retention/set-as-baseline.png differ
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/05 team member org id/projects-in-app.png b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/05 team member org id/projects-in-app.png
new file mode 100644
index 0000000000..3242bfcc91
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/05 team member org id/projects-in-app.png differ
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/09 Calculating virtual users/calculating-the-number-of-virtual-users-to-test-1.png b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/09 Calculating virtual users/calculating-the-number-of-virtual-users-to-test-1.png
new file mode 100644
index 0000000000..16f3756e9d
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/09 Calculating virtual users/calculating-the-number-of-virtual-users-to-test-1.png differ
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/09 Calculating virtual users/calculating-the-number-of-virtual-users-to-test-2.png b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/09 Calculating virtual users/calculating-the-number-of-virtual-users-to-test-2.png
new file mode 100644
index 0000000000..a88abc8edc
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/09 Calculating virtual users/calculating-the-number-of-virtual-users-to-test-2.png differ
diff --git a/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/09 Calculating virtual users/calculating-the-number-of-virtual-users-to-test-3.png b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/09 Calculating virtual users/calculating-the-number-of-virtual-users-to-test-3.png
new file mode 100644
index 0000000000..d03a2b6673
Binary files /dev/null and b/src/data/markdown/docs/03 cloud/06 Cloud FAQ/images/09 Calculating virtual users/calculating-the-number-of-virtual-users-to-test-3.png differ
diff --git a/src/data/markdown/docs/03 cloud/07 Cloud REST API/07 Cloud REST API.md b/src/data/markdown/docs/03 cloud/07 Cloud REST API/07 Cloud REST API.md
new file mode 100644
index 0000000000..442b9485ef
--- /dev/null
+++ b/src/data/markdown/docs/03 cloud/07 Cloud REST API/07 Cloud REST API.md
@@ -0,0 +1,4 @@
+---
+title: 'Cloud REST API'
+redirect: 'http://developers.loadimpact.com/'
+---
diff --git a/src/data/markdown/docs/04 integrations/01 Converters/01 har-to-k6.md b/src/data/markdown/docs/04 integrations/01 Converters/01 har-to-k6.md
new file mode 100644
index 0000000000..65c8f47750
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/01 Converters/01 har-to-k6.md
@@ -0,0 +1,4 @@
+---
+title: 'HAR-to-k6'
+redirect: 'https://github.com/loadimpact/har-to-k6'
+---
diff --git a/src/data/markdown/docs/04 integrations/01 Converters/02 jmeter-to-k6.md b/src/data/markdown/docs/04 integrations/01 Converters/02 jmeter-to-k6.md
new file mode 100644
index 0000000000..0d3412bdda
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/01 Converters/02 jmeter-to-k6.md
@@ -0,0 +1,4 @@
+---
+title: 'JMeter-to-k6'
+redirect: 'https://github.com/loadimpact/jmeter-to-k6'
+---
diff --git a/src/data/markdown/docs/04 integrations/01 Converters/03 postman-to-k6.md b/src/data/markdown/docs/04 integrations/01 Converters/03 postman-to-k6.md
new file mode 100644
index 0000000000..2057c3b33e
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/01 Converters/03 postman-to-k6.md
@@ -0,0 +1,4 @@
+---
+title: 'Postman-to-k6'
+redirect: 'https://github.com/loadimpact/postman-to-k6'
+---
diff --git a/src/data/markdown/docs/04 integrations/02 CI tools/01 jenkins.md b/src/data/markdown/docs/04 integrations/02 CI tools/01 jenkins.md
new file mode 100644
index 0000000000..47e262ea67
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/02 CI tools/01 jenkins.md
@@ -0,0 +1,4 @@
+---
+title: 'Jenkins'
+redirect: 'https://k6.io/blog/integrating-load-testing-with-jenkins'
+---
diff --git a/src/data/markdown/docs/04 integrations/02 CI tools/02 circleci.md b/src/data/markdown/docs/04 integrations/02 CI tools/02 circleci.md
new file mode 100644
index 0000000000..7860963f99
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/02 CI tools/02 circleci.md
@@ -0,0 +1,4 @@
+---
+title: 'Circleci'
+redirect: 'https://k6.io/blog/integrating-load-testing-with-circleci'
+---
diff --git a/src/data/markdown/docs/04 integrations/02 CI tools/03 gitlab.md b/src/data/markdown/docs/04 integrations/02 CI tools/03 gitlab.md
new file mode 100644
index 0000000000..93a7152738
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/02 CI tools/03 gitlab.md
@@ -0,0 +1,4 @@
+---
+title: 'Gitlab'
+redirect: 'https://k6.io/blog/integrating-load-testing-with-gitlab'
+---
diff --git a/src/data/markdown/docs/04 integrations/02 CI tools/04 azure.md b/src/data/markdown/docs/04 integrations/02 CI tools/04 azure.md
new file mode 100644
index 0000000000..b3aeb000ee
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/02 CI tools/04 azure.md
@@ -0,0 +1,4 @@
+---
+title: 'Azure Pipelines'
+redirect: 'https://k6.io/blog/integrating-load-testing-with-azure-pipelines'
+---
diff --git a/src/data/markdown/docs/04 integrations/02 CI tools/05 teamcity.md b/src/data/markdown/docs/04 integrations/02 CI tools/05 teamcity.md
new file mode 100644
index 0000000000..3ab4270c60
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/02 CI tools/05 teamcity.md
@@ -0,0 +1,4 @@
+---
+title: 'TeamCity'
+redirect: 'https://k6.io/blog/load-testing-using-teamcity-and-k6'
+---
diff --git a/src/data/markdown/docs/04 integrations/02 CI tools/06 github.md b/src/data/markdown/docs/04 integrations/02 CI tools/06 github.md
new file mode 100644
index 0000000000..a98c947e7a
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/02 CI tools/06 github.md
@@ -0,0 +1,4 @@
+---
+title: 'GitHub Actions'
+redirect: 'https://k6.io/blog/load-testing-using-github-actions'
+---
diff --git a/src/data/markdown/docs/04 integrations/03 Community Integrations/01 easy-graphql.md b/src/data/markdown/docs/04 integrations/03 Community Integrations/01 easy-graphql.md
new file mode 100644
index 0000000000..6b4030d73a
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/03 Community Integrations/01 easy-graphql.md
@@ -0,0 +1,4 @@
+---
+title: 'easygraphql-load-tester'
+redirect: 'https://github.com/EasyGraphQL/easygraphql-load-tester'
+---
diff --git a/src/data/markdown/docs/04 integrations/03 Community Integrations/02 azure-integration.md b/src/data/markdown/docs/04 integrations/03 Community Integrations/02 azure-integration.md
new file mode 100644
index 0000000000..1985e12887
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/03 Community Integrations/02 azure-integration.md
@@ -0,0 +1,4 @@
+---
+title: 'k6ToAzure'
+redirect: 'https://github.com/benc-uk/smilr/blob/master/azure/load-test-reports/k6ToAzure.js'
+---
diff --git a/src/data/markdown/docs/04 integrations/04 Result Store/01 json.md b/src/data/markdown/docs/04 integrations/04 Result Store/01 json.md
new file mode 100644
index 0000000000..11d4184446
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/04 Result Store/01 json.md
@@ -0,0 +1,4 @@
+---
+title: 'JSON'
+redirect: 'https://k6.io/docs/getting-started/results-output/json'
+---
diff --git a/src/data/markdown/docs/04 integrations/04 Result Store/02 influxdb-grafana.md b/src/data/markdown/docs/04 integrations/04 Result Store/02 influxdb-grafana.md
new file mode 100644
index 0000000000..68657cad97
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/04 Result Store/02 influxdb-grafana.md
@@ -0,0 +1,4 @@
+---
+title: 'InfluxDB/Grafana'
+redirect: 'https://k6.io/docs/getting-started/results-output/influxdb'
+---
diff --git a/src/data/markdown/docs/04 integrations/04 Result Store/03 kafka.md b/src/data/markdown/docs/04 integrations/04 Result Store/03 kafka.md
new file mode 100644
index 0000000000..7411be05f4
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/04 Result Store/03 kafka.md
@@ -0,0 +1,4 @@
+---
+title: 'Apache Kafka'
+redirect: 'https://k6.io/docs/getting-started/results-output/apache-kafka'
+---
diff --git a/src/data/markdown/docs/04 integrations/04 Result Store/04 datadog.md b/src/data/markdown/docs/04 integrations/04 Result Store/04 datadog.md
new file mode 100644
index 0000000000..1f285f360d
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/04 Result Store/04 datadog.md
@@ -0,0 +1,4 @@
+---
+title: 'DataDog'
+redirect: 'https://k6.io/docs/getting-started/results-output/datadog'
+---
diff --git a/src/data/markdown/docs/04 integrations/04 Result Store/05 cloud-service.md b/src/data/markdown/docs/04 integrations/04 Result Store/05 cloud-service.md
new file mode 100644
index 0000000000..bc462e7c1c
--- /dev/null
+++ b/src/data/markdown/docs/04 integrations/04 Result Store/05 cloud-service.md
@@ -0,0 +1,4 @@
+---
+title: 'k6 Cloud'
+redirect: 'https://k6.io/docs/getting-started/results-output/cloud'
+---
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/01 single-request.md b/src/data/markdown/docs/05 Examples/01 Examples/01 single-request.md
new file mode 100644
index 0000000000..2711cf5b54
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/01 single-request.md
@@ -0,0 +1,46 @@
+---
+title: 'Single request'
+excerpt: 'Example of one HTTP GET request'
+draft: 'false'
+---
+
+
+
+```js
+import http from 'k6/http';
+import { sleep, check } from 'k6';
+import { Counter } from 'k6/metrics';
+
+// A simple counter for http requests
+
+export const requests = new Counter('http_reqs');
+
+// you can specify stages of your test (ramp up/down patterns) through the options object
+// target is the number of VUs you are aiming for
+
+export const options = {
+ stages: [
+ { target: 20, duration: '1m' },
+ { target: 15, duration: '1m' },
+ { target: 0, duration: '1m' },
+ ],
+ thresholds: {
+ requests: ['count < 100'],
+ },
+};
+
+export default function() {
+ // our HTTP request, note that we are saving the response to res, which can be accessed later
+
+ const res = http.get('http://test.k6.io');
+
+ sleep(1);
+
+ const checkRes = check(res, {
+ 'status is 200': r => r.status === 200,
+ 'response body': r => r.body.indexOf('Feel free to browse'),
+ });
+}
+```
+
+
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/02 http-authentication.md b/src/data/markdown/docs/05 Examples/01 Examples/02 http-authentication.md
new file mode 100644
index 0000000000..f38cced1f3
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/02 http-authentication.md
@@ -0,0 +1,205 @@
+---
+title: 'HTTP Authentication'
+excerpt: 'Scripting examples on how to use different authenitcation or authorization methods in your load test.'
+---
+
+Scripting examples on how to use different authentication or authorization methods in your load test.
+
+## Basic authentication
+
+
+
+```js
+import encoding from 'k6/encoding';
+import http from 'k6/http';
+import { check } from 'k6';
+
+const username = 'user';
+const password = 'passwd';
+
+export default function() {
+ const credentials = `${username}:${password}`;
+
+ // Passing username and password as part of the URL will
+ // allow us to authenticate using HTTP Basic Auth.
+ const url = `http://${credentials}@httpbin.org/basic-auth/${username}/${password}`;
+
+ let res = http.get(url);
+
+ // Verify response
+ check(res, {
+ 'status is 200': r => r.status === 200,
+ 'is authenticated': r => r.json().authenticated === true,
+ 'is correct user': r => r.json().user === username,
+ });
+
+ // Alternatively you can create the header yourself to authenticate
+ // using HTTP Basic Auth
+ const encodedCredentials = encoding.b64encode(credentials);
+ const options = {
+ headers: {
+ Authorization: `Basic ${encodedCredentials}`,
+ },
+ };
+
+ res = http.get(
+ `http://httpbin.org/basic-auth/${username}/${password}`,
+ options,
+ );
+
+ // Verify response (checking the echoed data from the httpbin.org
+ // basic auth test API endpoint)
+ check(res, {
+ 'status is 200': r => r.status === 200,
+ 'is authenticated': r => r.json().authenticated === true,
+ 'is correct user': r => r.json().user === username,
+ });
+}
+```
+
+
+
+## Digest authentication
+
+
+
+```js
+import http from 'k6/http';
+import { check } from 'k6';
+
+const username = 'user';
+const password = 'passwd';
+
+export default function() {
+ // Passing username and password as part of URL plus the auth option will
+ // authenticate using HTTP Digest authentication.
+ const credentials = `${username}:${password}`;
+ const res = http.get(
+ `http://${credentials}@httpbin.org/digest-auth/auth/${username}/${password}`,
+ { auth: 'digest' },
+ );
+
+ // Verify response (checking the echoed data from the httpbin.org digest auth
+ // test API endpoint)
+ check(res, {
+ 'status is 200': r => r.status === 200,
+ 'is authenticated': r => r.json().authenticated === true,
+ 'is correct user': r => r.json().user === username,
+ });
+}
+```
+
+
+
+## NTLM authentication
+
+
+
+```js
+import http from 'k6/http';
+
+const username = 'user';
+const password = 'passwd';
+
+export default function() {
+ // Passing username and password as part of URL and then specifying
+ // "ntlm" as auth type will do the trick!
+ const credentials = `${username}:${password}`;
+ let res = http.get(`http://${credentials}@example.com/`, { auth: 'ntlm' });
+}
+```
+
+
+
+## AWS Signature v4 authentication
+
+Requests to the AWS APIs requires a special type of auth, called AWS Signature Version 4. k6
+does not support this authentication mechanism out of the box, so we'll have to resort to using
+a Node.js library called [awsv4.js](https://github.com/mhart/aws4) and
+[Browserify](http://browserify.org/) (to make it work in k6).
+
+There are a few steps required to make this work:
+
+1. Make sure you have the necessary prerequisites installed: [Node.js](https://nodejs.org/en/download/)
+ and [Browserify](http://browserify.org/)
+2. Install the `awsv4.js` library:
+
+
+
+ ```shell
+ $ npm install aws4
+ ```
+
+
+
+3. Run it through browserify:
+
+
+
+ ```shell
+ $ browserify node_modules/aws4/aws4.js -s aws4 > aws4.js
+ ```
+
+
+
+4. Move the `aws4.js` file to the same folder as your script file and you'll be able to import
+ it into your test script:
+
+
+
+ ```js
+ import aws4 from "./aws4.js"`
+ ```
+
+
+
+Here's an example script to list all the regions available in EC2. Note that the AWS access key
+and secret key needs to be provided through [environment variables](/using-k6/environment-variables).
+
+> ### ⚠️ CPU- and Memory-heavy
+>
+> As the browserified version of this Node.js library includes several Node.js APIs
+> implemented in pure JS (including crypto APIs) it will be quite heavy on CPU and memory hungry
+> when run with more than just a few VUs.
+
+
+
+```js
+
+import http from 'k6/http';
+import { sleep } from 'k6';
+
+// Import browserified AWSv4 signature library
+import aws4 from './aws4.js';
+
+// Get AWS credentials from environment variables
+const AWS_CREDS = {
+ accessKeyId: __ENV.AWS_ACCESSKEY,
+ secretAccessKey: __ENV.AWS_SECRETKEY,
+};
+
+export default function() {
+ // Sign the AWS API request
+ const signed = aws4.sign(
+ {
+ service: 'ec2',
+ path: '/?Action=DescribeRegions&Version=2014-06-15',
+ },
+ AWS_CREDS,
+ );
+
+ // Make the actual request to the AWS API including the
+ // "Authorization" header with the signature
+ let res = http.get(
+ `https://${signed.hostname}${signed.path}`,
+ { headers: signed.headers },
+ );
+
+ // Print the response
+ console.log(res.body);
+
+ sleep(1);
+}
+```
+
+
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/03 correlation-and-dynamic-data.md b/src/data/markdown/docs/05 Examples/01 Examples/03 correlation-and-dynamic-data.md
new file mode 100644
index 0000000000..db5b0a0616
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/03 correlation-and-dynamic-data.md
@@ -0,0 +1,110 @@
+---
+title: 'Correlation and Dynamic Data'
+excerpt: |
+ Scripting examples on how to correlate dynamic data in your test script. Correlation is
+ often required when using the Chrome Extension or HAR converter to generate your test script.
+ This is due to the fact that those tools will capture session IDs, CSRF tokens, VIEWSTATE,
+ wpnonce, and other dynamic values from your specific session.
+---
+
+Scripting examples on how to correlate dynamic data in your test script. Correlation is often
+required when using the Chrome Extension or HAR converter to generate your test script. This
+is because those tools will capture session IDs, CSRF tokens, VIEWSTATE, wpnonce, and other
+dynamic values from your specific session. These tokens typically expire very quickly. This
+is one of the most common things that users will script for when testing user journeys across
+websites or web apps.
+
+### Correlation
+
+In a load testing scenario, correlation means extracting one or more values from the response
+of one request and then reusing them in subsequent requests. Often, this could be getting
+a token or some sort of ID necessary to fulfill a sequence of steps in a user journey.
+
+The [browser recording](/using-k6/session-recording-har-support) will for example capture things like CSRF tokens,
+VIEWSTATES, nonce, etc. from your session. This type of data is likely to no longer be valid when
+you run your test, meaning you'll need to handle the extraction of this data from the HTML/form
+to include it in subsequent requests. This issue is fairly common with any site that has forms
+and can be handled with a little bit of scripting.
+
+### Extracting values/tokens from JSON response
+
+
+
+```js
+import http from 'k6/http';
+import { check } from 'k6';
+
+export default function() {
+ // Make a request that returns some JSON data
+ let res = http.get('https://httpbin.org/json');
+
+ // Extract data from that JSON data by first parsing it
+ // using a call to "json()" and then accessing properties by
+ // navigating the JSON data as a JS object with dot notation.
+ let slide1 = res.json().slideshow.slides[0];
+ check(slide1, {
+ 'slide 1 has correct title': s => s.title === 'Wake up to WonderWidgets!',
+ 'slide 1 has correct type': s => s.type === 'all',
+ });
+
+ // Now we could use the "slide1" variable in subsequent requests...
+}
+```
+
+
+
+**Relevant k6 APIs**:
+
+- [Response.json()](/javascript-api/k6-http/response-k6-http)
+- [JSON.parse()](https://developer.mozilla.org/en-US/Web/JavaScript/Reference/Global_Objects/JSON/parse)
+ (An alternative API that can be used for parsing JSON data)
+
+### Extracting values/tokens from form fields
+
+There are primarily two different ways you can choose from when deciding how to handle form
+submissions. Either you use the higher-level [Response.submitForm([params])](/javascript-api/k6-http/response/response-submitform-params) API
+or you extract necessary hidden fields etc. and build a request yourself and then send it using the
+appropriate `http.*` family of APIs, like [http.post(url, [body], [params])](/javascript-api/k6-http/post-url-body-params).
+
+#### Extracting .NET ViewStates, CSRF tokens and other hidden input fields
+
+
+
+```js
+import http from "k6/http";
+import {sleep} from "k6";
+
+export default function() {
+
+ // Request the page containing a form and save the response. This gives you access
+ //to the response object, `res`.
+ const res = http.get("https://test.k6.io/my_messages.php", {"responseType": "text"});
+
+ // Query the HTML for an input field named "redir". We want the value or "redir"
+ const elem = res.html().find('input[name=redir]');
+
+ // Get the value of the attribute "value" and save it to a variable
+ const val = elem.attr('value');
+
+ // Now you can concatenate this extracted value in subsequent requests that require it.
+ ...
+ // console.log() works when executing k6 scripts locally and is handy for debugging purposes
+ console.log("The value of the hidden field redir is: " + val);
+
+ sleep(1);
+}
+```
+
+
+
+> ### ⚠️ Did you know?
+>
+> Take note if `discardResponseBodies` is set to true in the options
+> section of your script. If it is, you can either make it `false` or save the response per
+> request with `{"responseType": "text"}` as shown in the example.
+
+**Relevant k6 APIs**:
+
+- [Selection.find(selector)](/javascript-api/k6-html/selection/selection-find-selector) (the [jQuery Selector API](http://api.jquery.com/category/selectors/)
+ docs are also a good resource on what possible selector queryies can be made)
+- [Selection.attr(name)](/javascript-api/k6-html/selection/selection-attr-name)
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/04 data-parameterization.md b/src/data/markdown/docs/05 Examples/01 Examples/04 data-parameterization.md
new file mode 100644
index 0000000000..7fba5d1c0b
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/04 data-parameterization.md
@@ -0,0 +1,149 @@
+---
+title: 'Data Parameterization'
+excerpt: |
+ Scripting examples on how to parameterize data in a test script. Parameterization is typically
+ necessary when Virtual Users(VUs) will make a POST, PUT, or PATCH request in a test.
+
+ Parameterization helps to prevent server-side caching from impacting your load test.
+ This will, in turn, make your test more realistic.
+---
+
+Scripting examples on how to parameterize data in a test script. Parameterization is typically
+necessary when Virtual Users(VUs) will make a POST, PUT, or PATCH request in a test.
+
+Parameterization helps to prevent server-side caching from impacting your load test.
+This will, in turn, make your test more realistic.
+
+
+## From a JSON file
+
+
+
+```json
+{
+ "users": [
+ { username: "test", password: "qwerty" },
+ { username: "test", password: "qwerty" }
+ ]
+}
+```
+
+
+
+
+
+```js
+
+const data = JSON.parse(open('./data.json'));
+
+export default function() {
+ let user = data.users[0];
+ console.log(data.users[0].username);
+}
+```
+
+
+
+## From a CSV file
+
+As k6 doesn't support parsing CSV files natively, we'll have to resort to using a
+library called [Papa Parse](https://www.papaparse.com/).
+
+You can download the library and and import it locally like this:
+
+
+
+```JavaScript
+import papaparse from './papaparse.js';
+
+const csvData = papaparse.parse(open('./data.csv'), { header: true });
+
+export default function() {
+ // ...
+}
+```
+
+
+
+Or you can grab it directly from [jslib.k6.io](https://jslib.k6.io/) like this.
+
+
+
+```JavaScript
+import papaparse from "https://jslib.k6.io/papaparse/5.1.1/index.js"
+
+// Load CSV file and parse it using Papa Parse
+const csvData = papaparse.parse(open('./data.csv'), { header: true });
+
+export default function() {
+ // ...
+}
+```
+
+
+
+Here's an example using Papa Parse to parse a CSV file of username/password pairs and using that
+data to login to the test.k6.io test site:
+
+
+
+```js
+/* Where contents of data.csv is:
+
+ username,password
+ admin,123
+ test_user,1234
+*/
+import http from 'k6/http';
+import { check, sleep } from 'k6';
+import papaparse from "https://jslib.k6.io/papaparse/5.1.1/index.js"
+
+// Load CSV file and parse it using Papa Parse
+const csvData = papaparse.parse(open('./data.csv'), { header: true }).data;
+
+export default function() {
+ // Now you can use the CSV data in your test logic below.
+ // Below are some examples of how you can access the CSV data.
+
+ // Loop through all username/password pairs
+ csvData.forEach(userPwdPair => {
+ console.log(JSON.stringify(userPwdPair));
+ });
+
+ // Pick a random username/password pair
+ let randomUser =
+ csvData[Math.floor(Math.random() * csvData.length)];
+ console.log('Random user: ', JSON.stringify(randomUser));
+
+ const params = {
+ login: randomUser.username,
+ password: randomUser.password,
+ };
+
+ let res = http.post('https://test.k6.io/login.php', params);
+ check(res, {
+ 'login succeeded': r =>
+ r.status === 200 && r.body.indexOf('successfully authorized') !== -1,
+ });
+
+ sleep(1);
+}
+```
+
+
+
+
+
+
+
+> ### ⚠️ Strive to keep the data files small
+> Each VU in k6 will have its separate copy of the data file.
+> If your script uses 300 VUs, there will be 300 copies of the data file in memory.
+> Cloud service allots 8GB of memory for every 300VUs.
+> When executing cloud tests, make sure your data files aren't exceeding this limit or your test run may get aborted.
+
+
+
+## Generating data
+
+See [this example project on GitHub](https://github.com/k6io/example-data-generation) showing how to use faker.js to generate realistic data at runtime.
\ No newline at end of file
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/05 html-forms.md b/src/data/markdown/docs/05 Examples/01 Examples/05 html-forms.md
new file mode 100644
index 0000000000..41d0a20352
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/05 html-forms.md
@@ -0,0 +1,41 @@
+---
+title: "HTML Forms"
+excerpt: "Scripting example on how to handle HTML forms in a load test."
+---
+
+Scripting example on how to handle HTML forms in a load test.
+
+### Filling in and submitting forms
+One of the most tedious tasks when testing websites and apps are to get all the form filling to
+work. You have to get all the so-called "correlations" ([see above](/examples/correlation-and-dynamic-data)) correct
+which can take time, even with the help of a scenario recorder as the starting point for getting
+the basic user journey down into a re-playable test.
+
+
+
+```js
+import http from "k6/http";
+import {sleep} from "k6";
+
+export default function() {
+ // Request page containing a form
+ let res = http.get("https://httpbin.org/forms/post");
+
+ // Now, submit form setting/overriding some fields of the form
+ res = res.submitForm({
+ formSelector: 'form[action="/post"]',
+ fields: { custname: "test", extradata: "test2" },
+ submitSelector: "mySubmit",
+ });
+
+ sleep(3);
+}
+```
+
+
+
+**Relevant k6 APIs**:
+- [Response.submitForm([params])](/javascript-api/k6-http/response/response-submitform-params)
+- [Selection.find(selector)](/javascript-api/k6-html/selection/selection-find-selector)
+ (the [jQuery Selector API](http://api.jquery.com/category/selectors/) docs are also a good
+ resource on what possible selector queries can be made)
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/06 cookies-example.md b/src/data/markdown/docs/05 Examples/01 Examples/06 cookies-example.md
new file mode 100644
index 0000000000..648adf30d6
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/06 cookies-example.md
@@ -0,0 +1,113 @@
+---
+title: "Cookies Example"
+excerpt: "Scripting examples on how you can interact with cookies during your load test if required."
+---
+
+Scripting examples on how you can interact with cookies during your load test if required.
+
+## Cookies
+As HTTP is a stateless protocol, cookies are used by server-side applications to persist data
+on client machines. This is used more or less everywhere on the web, commonly for user session
+tracking purposes. In k6 cookies are managed automatically by default, however, there are use
+cases where access to read and manipulate cookies are required.
+
+## From the response headers
+
+
+
+```js
+import http from "k6/http";
+import { check, group } from "k6";
+
+export default function() {
+ // Since this request redirects the `res.cookies` property won't contain the cookies
+ let res = http.get("http://httpbin.org/cookies/set?name1=value1&name2=value2");
+ check(res, {
+ "status is 200": (r) => r.status === 200
+ });
+
+ // Make sure cookies have been added to VU cookie jar
+ let vuJar = http.cookieJar();
+ let cookiesForURL = vuJar.cookiesForURL(res.url);
+ check(null, {
+ "vu jar has cookie 'name1'": () => cookiesForURL.name1.length > 0,
+ "vu jar has cookie 'name2'": () => cookiesForURL.name2.length > 0
+ });
+}
+```
+
+
+
+## Log all the cookies in a response
+> ### ⚠️ Note that this only works when using k6 locally
+>
+> The `console.log()` family of APIs are currently only usable when running k6 locally.
+> When running k6 tests with LoadImpact Cloud Execution the logs will be discarded.
+
+
+
+```js
+
+// Example showing two methods how to log all cookies (with attributes) from a HTTP response.
+
+import http from "k6/http";
+
+function logCookie(cookie) {
+ // Here we log the name and value of the cookie along with additional attributes.
+ // For full list of attributes see: https://k6.io/docs/using-k6/cookies#properties-of-a-response-cookie-object
+ console.log(`${cookie.name}: ${cookie.value}\n\tdomain: ${cookie.domain}\n\tpath: ${cookie.path}\n\texpires: ${cookie.expires}\n\thttpOnly: ${cookie.http_only}`);
+}
+
+export default function() {
+ let res = http.get("https://www.google.com/");
+
+ // Method 1: Use for-loop and check for non-inherited properties
+ for (var name in res.cookies) {
+ if (res.cookies.hasOwnProperty(name) !== undefined) {
+ logCookie(res.cookies[name][0]);
+ }
+ }
+
+ // Method 2: Use ES6 Map to loop over Object entries
+ new Map(Object.entries(res.cookies)).forEach((v, k) => logCookie(v[0]) );
+}
+```
+
+
+
+## Setting a cookie in the VU cookie jar
+To set a cookie that should be sent with every request matching a particular domain, path, etc.
+you'd do something like this:
+
+
+
+```js
+import http from "k6/http";
+import { check } from "k6";
+
+export default function() {
+ // Get VU cookie jar and add a cookie to it providing the parameters
+ // that a request must match (domain, path, HTTPS or not etc.)
+ // to have the cookie attached to it when sent to the server.
+ let jar = http.cookieJar();
+ jar.set("https://httpbin.org/cookies", "my_cookie", "hello world",
+ { domain: "httpbin.org", path: "/cookies", secure: true, max_age: 600 });
+
+ // As the following request is matching the above cookie in terms of domain,
+ // path, HTTPS (secure) and will happen within the specified "age" limit, the
+ // cookie will be attached to this request.
+ let res = http.get("https://httpbin.org/cookies");
+ check(res, {
+ "has status 200": (r) => r.status === 200,
+ "has cookie 'my_cookie'": (r) => r.json().cookies.my_cookie !== null,
+ "cookie has correct value": (r) => r.json().cookies.my_cookie == "hello world"
+ });
+}
+```
+
+
+
+**Relevant k6 APIs**:
+- [http.CookieJar](/javascript-api/k6-http/cookiejar-k6-http)
+ - [set(name, value, [options])](/javascript-api/k6-http/cookiejar/cookiejar-set-name-value-options)
+- [http.cookieJar()](/javascript-api/k6-http/cookiejar)
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/07 data-uploads.md b/src/data/markdown/docs/05 Examples/01 Examples/07 data-uploads.md
new file mode 100644
index 0000000000..9d22f4cb32
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/07 data-uploads.md
@@ -0,0 +1,61 @@
+---
+title: "Data Uploads"
+excerpt: "Scripting examples on how to execute a load test that will upload a file to the System Under Test(SUT)."
+---
+
+Scripting examples on how to execute a load test that will upload a file to the System Under Test(SUT).
+
+## Binary file upload
+
+
+
+```js
+import http from "k6/http";
+import { sleep } from "k6";
+
+const binFile = open("/path/to/file.bin", "b");
+
+export default function() {
+ var data = {
+ file: http.file(binFile, "test.bin")
+ };
+ var res = http.post("https://example.com/upload", data);
+ sleep(3);
+}
+```
+
+
+
+### Relevant k6 APIs
+- [open(filePath, [mode])](/javascript-api/init-context/open-filepath-mode)
+- [http.file(data, [filename], [contentType])](/javascript-api/k6-http/file-data-filename-contenttype)
+
+## Creating a multipart request
+With multipart requests, you combine pieces of data with possibly different content types into one
+request body. A common scenario is, for example, a form with regular text input fields and a file
+field used for uploading a file:
+
+
+
+```js
+import http from "k6/http";
+import { sleep } from "k6";
+
+let file = open("/path/to/file.txt");
+
+export default function() {
+ var data = {
+ field: "this is a standard form field",
+ file: http.file(file, "test.txt")
+ };
+ var res = http.post("https://example.com/upload", data);
+ sleep(3);
+}
+```
+
+
+
+### Relevant k6 APIs
+- [open(filePath, [mode])](/javascript-api/init-context/open-filepath-mode)
+- [http.file(data, [filename], [contentType])](/javascript-api/k6-http/file-data-filename-contenttype)
+
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/08 advanced-api-flow.md b/src/data/markdown/docs/05 Examples/01 Examples/08 advanced-api-flow.md
new file mode 100644
index 0000000000..847268b821
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/08 advanced-api-flow.md
@@ -0,0 +1,136 @@
+---
+title: "Advanced API flow"
+excerpt: "This example covers the usage of different k6 APIs for API load testing."
+---
+
+
+
+```js
+ import http from 'k6/http';
+ import {check, group, sleep, fail} from 'k6';
+
+ export let options = {
+ stages: [
+ { target: 70, duration: '30s' },
+ ],
+ thresholds: {
+ 'http_req_duration': ['p(95)<500', 'p(99)<1500'],
+ 'http_req_duration{name:PublicCrocs}': ['avg<400'],
+ 'http_req_duration{name:Create}': ['avg<600', 'max<1000'],
+ },
+ };
+
+ function randomString(length) {
+ const charset = 'abcdefghijklmnopqrstuvwxyz';
+ let res = '';
+ while (length--) res += charset[Math.random() * charset.length | 0];
+ return res;
+ }
+
+ const USERNAME = `${randomString(10)}@example.com`; // Set your own email or `${randomString(10)}@example.com`;
+ const PASSWORD = 'superCroc2019';
+ const BASE_URL = 'https://test-api.k6.io';
+
+ export function setup() {
+ // register a new user and authenticate via a Bearer token.
+ let res = http.post(`${BASE_URL}/user/register/`, {
+ first_name: 'Crocodile',
+ last_name: 'Owner',
+ username: USERNAME,
+ password: PASSWORD,
+ });
+
+ check(res, { 'created user': (r) => r.status === 201 });
+
+ let loginRes = http.post(`${BASE_URL}/auth/token/login/`, {
+ username: USERNAME,
+ password: PASSWORD
+ });
+
+ let authToken = loginRes.json('access');
+ check(authToken, { 'logged in successfully': () => authToken !== '', });
+
+ return authToken;
+ }
+
+
+ export default (authToken) => {
+ const requestConfigWithTag = tag => ({
+ headers: {
+ Authorization: `Bearer ${authToken}`
+ },
+ tags: Object.assign({}, {
+ name: 'PrivateCrocs'
+ }, tag)
+ });
+
+ group('Public endpoints', () => {
+ // call some public endpoints in a batch
+ let responses = http.batch([
+ ['GET', `${BASE_URL}/public/crocodiles/1/`, null, {tags: {name: 'PublicCrocs'}}],
+ ['GET', `${BASE_URL}/public/crocodiles/2/`, null, {tags: {name: 'PublicCrocs'}}],
+ ['GET', `${BASE_URL}/public/crocodiles/3/`, null, {tags: {name: 'PublicCrocs'}}],
+ ['GET', `${BASE_URL}/public/crocodiles/4/`, null, {tags: {name: 'PublicCrocs'}}],
+ ]);
+
+ const ages = Object.values(responses).map(res => res.json('age'));
+
+ // Functional test: check that all the public crocodiles are older than 5
+ check(ages, {
+ 'Crocs are older than 5 years of age': Math.min(...ages) > 5
+ });
+ });
+
+ group('Create and modify crocs', () => {
+ let URL = `${BASE_URL}/my/crocodiles/`;
+
+ group('Create crocs', () => {
+ const payload = {
+ name: `Name ${randomString(10)}`,
+ sex: 'M',
+ date_of_birth: '2001-01-01',
+ };
+
+ const res = http.post(URL, payload, requestConfigWithTag({ name: 'Create' }));
+
+ if (check(res, { 'Croc created correctly': (r) => r.status === 201 })) {
+ URL = `${URL}${res.json('id')}/`;
+ } else {
+ console.log(`Unable to create a Croc ${res.status} ${res.body}`);
+ return
+ }
+ });
+
+ group('Update croc', () => {
+ const payload = { name: 'New name' };
+ const res = http.patch(URL, payload, requestConfigWithTag({ name: 'Update' }));
+ const isSuccessfulUpdate = check(res, {
+ 'Update worked': () => res.status === 200,
+ 'Updated name is correct': () => res.json('name') === 'New name',
+ });
+
+ if (!isSuccessfulUpdate) {
+ console.log(`Unable to update the croc ${res.status} ${res.body}`);
+ return
+ }
+ });
+
+ const delRes = http.del(URL, null, requestConfigWithTag({ name: 'Delete' }));
+
+ const isSuccessfulDelete = check(null, {
+ 'Croc was deleted correctly': () => delRes.status === 204,
+ });
+
+ if (!isSuccessfulDelete) {
+ console.log(`Croc was not deleted properly`);
+ return
+ }
+ });
+
+ sleep(1);
+ }
+
+
+```
+
+
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/09 generating-uuids.md b/src/data/markdown/docs/05 Examples/01 Examples/09 generating-uuids.md
new file mode 100644
index 0000000000..71efc03275
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/09 generating-uuids.md
@@ -0,0 +1,65 @@
+---
+title: "Generating UUIDs"
+excerpt: "Scripting example on how to generate UUIDs in your load test."
+---
+
+Scripting example on how to generate UUIDs in your load test.
+
+## Generate v1 and v4 UUIDs
+Universally unique identifiers are handy in many scenarios, as k6 doesn't have built-in support
+for UUIDs, we'll have to resort to using a Node.js library called [uuid](https://www.npmjs.com/package/uuid)
+and [Browserify](http://browserify.org/) (to make it work in k6).
+
+There are a few steps required to make this work:
+
+1. Make sure you have the necessary prerequisites installed:
+ [Node.js](https://nodejs.org/en/download/) and [Browserify](http://browserify.org/)
+
+2. Install the `uuid` library:
+
+
+ ```shell
+ $ npm install uuid
+ ```
+
+
+3. Run it through browserify:
+
+
+ ```shell
+ $ browserify node_modules/uuid/index.js -s uuid > uuid.js
+ ```
+
+
+
+4. Move the `uuid.js` file to the same folder as your script file and you'll be able to import
+ it into your test script:
+
+
+
+ ```js
+ import uuid from "./uuid.js"`
+ ```
+
+
+
+
+Here's an example generating a v1 and v4 UUID:
+
+
+
+```js
+import uuid from "./uuid.js";
+
+export default function() {
+ // Generate a UUID v1
+ let uuid1 = uuid.v1();
+ console.log(uuid1);
+
+ // Generate a UUID v4
+ let uuid4 = uuid.v4();
+ console.log(uuid4);
+}
+```
+
+
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/10 http2.md b/src/data/markdown/docs/05 Examples/01 Examples/10 http2.md
new file mode 100644
index 0000000000..76d3ec1078
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/10 http2.md
@@ -0,0 +1,25 @@
+---
+title: "HTTP2"
+excerpt: "Information on how to load test HTTP/2."
+---
+
+If the target system indicates that a connection can be upgraded from HTTP/1.1 to HTTP/2, k6 will do so automatically.
+
+## Making HTTP/2 requests
+
+
+
+```js
+import http from "k6/http";
+import { check } from "k6";
+
+export default function() {
+ let res = http.get("https://test-api.k6.io/");
+ check(res, {
+ "status is 200": (r) => r.status === 200,
+ "protocol is HTTP/2": (r) => r.proto === "HTTP/2.0",
+ });
+}
+```
+
+
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/11 websockets.md b/src/data/markdown/docs/05 Examples/01 Examples/11 websockets.md
new file mode 100644
index 0000000000..7b634616f5
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/11 websockets.md
@@ -0,0 +1,51 @@
+---
+title: "Websockets"
+excerpt: "Scripting example on how to test websocket APIs."
+---
+
+
+## Testing a WebSocket API
+
+
+
+```js
+import ws from "k6/ws";
+import { check } from "k6";
+
+export default function() {
+ var url = "ws://echo.websocket.org";
+ var params = { "tags": { "my_tag": "hello" } };
+
+ var res = ws.connect(url, params, function(socket) {
+ socket.on('open', function open() {
+ console.log('connected');
+
+ socket.setInterval(function timeout() {
+ socket.ping();
+ console.log("Pinging every 1sec (setInterval test)");
+ }, 1000);
+ });
+
+ socket.on('ping', function () {
+ console.log("PING!");
+ });
+
+ socket.on('pong', function () {
+ console.log("PONG!");
+ });
+
+ socket.on('close', function() {
+ console.log('disconnected');
+ });
+
+ socket.setTimeout(function () {
+ console.log('2 seconds passed, closing the socket');
+ socket.close();
+ }, 2000);
+ });
+
+ check(res, { "status is 101": (r) => r && r.status === 101 });
+}
+```
+
+
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/12 soap.md b/src/data/markdown/docs/05 Examples/01 Examples/12 soap.md
new file mode 100644
index 0000000000..f3de087f73
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/12 soap.md
@@ -0,0 +1,43 @@
+---
+title: "SOAP"
+excerpt: "Load Testing SOAP API."
+---
+
+Although k6 doesn't have any built-in APIs for working with SOAP or XML data in general, you
+can still easily load test a SOAP-based API by crafting SOAP messages and using the HTTP request APIs.
+
+## Making SOAP requests
+
+
+
+```js
+import http from "k6/http";
+import { check, sleep } from "k6";
+
+const soapReqBody = `
+
+
+
+ UnitedStates
+
+
+ `;
+
+export default function() {
+ // When making a SOAP POST request we must not forget to set the content type to text/xml
+ let res = http.post(
+ "http://www.holidaywebservice.com/HolidayService_v2/HolidayService2.asmx?wsdl",
+ soapReqBody,
+ {headers: { 'Content-Type': 'text/xml' }});
+
+ // Make sure the response is correct
+ check(res, {
+ 'status is 200': (r) => r.status === 200,
+ 'black friday is present': (r) => r.body.indexOf('BLACK-FRIDAY') !== -1
+ });
+
+ sleep(1);
+}
+```
+
+
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/13 tls.md b/src/data/markdown/docs/05 Examples/01 Examples/13 tls.md
new file mode 100644
index 0000000000..42b3e7aba7
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/13 tls.md
@@ -0,0 +1,34 @@
+---
+title: "Transport Layer Security (TLS)"
+excerpt: |
+ TLS is the mechanism through which encrypted connections can be established between clients and
+ servers on the web and through which data can flow with integrity intact.
+---
+
+
+
+```js
+import http from "k6/http";
+import { check } from "k6";
+
+export let options = {
+ tlsCipherSuites: [
+ "TLS_RSA_WITH_RC4_128_SHA",
+ "TLS_RSA_WITH_AES_128_GCM_SHA256",
+ ],
+ tlsVersion: {
+ min: "ssl3.0",
+ max: "tls1.2"
+ }
+};
+
+export default function() {
+ let res = http.get("https://sha256.badssl.com");
+ check(res, {
+ "is TLSv1.2": (r) => r.tls_version === http.TLS_1_2,
+ "is sha256 cipher suite": (r) => r.tls_cipher_suite === "TLS_RSA_WITH_AES_128_GCM_SHA256"
+ });
+};
+```
+
+
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/14 data generation.md b/src/data/markdown/docs/05 Examples/01 Examples/14 data generation.md
new file mode 100644
index 0000000000..eb1cfa3029
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/14 data generation.md
@@ -0,0 +1,6 @@
+---
+title: 'Generating realistic data'
+redirect: 'https://github.com/k6io/example-data-generation/'
+excerpt: |
+ Reference project demonstrating how to generate data with realistic traits at runtime using faker.js
+---
diff --git a/src/data/markdown/docs/05 Examples/01 Examples/15 Bundling and transpilation.md b/src/data/markdown/docs/05 Examples/01 Examples/15 Bundling and transpilation.md
new file mode 100644
index 0000000000..bca4b9b1d3
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/01 Examples/15 Bundling and transpilation.md
@@ -0,0 +1,7 @@
+---
+title: 'Bundling and transpilation'
+redirect: 'https://github.com/k6io/k6-es6/'
+excerpt: |
+ Reference project demonstrating how to use webpack and babel to bundle
+ node modules or transpile code to ES5.1+ for usage in k6 tests.
+---
\ No newline at end of file
diff --git a/src/data/markdown/docs/05 Examples/02 Tutorials/01 datadog-alerts.md b/src/data/markdown/docs/05 Examples/02 Tutorials/01 datadog-alerts.md
new file mode 100644
index 0000000000..92aeccabe4
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/02 Tutorials/01 datadog-alerts.md
@@ -0,0 +1,4 @@
+---
+title: 'Datadog alerts and Thresholds'
+redirect: 'https://k6.io/blog/datadog-alerts-fail-load-test'
+---
diff --git a/src/data/markdown/docs/05 Examples/02 Tutorials/02 postman.md b/src/data/markdown/docs/05 Examples/02 Tutorials/02 postman.md
new file mode 100644
index 0000000000..097eb26745
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/02 Tutorials/02 postman.md
@@ -0,0 +1,4 @@
+---
+title: 'Load testing with Postman'
+redirect: 'https://k6.io/blog/load-testing-with-postman-collections'
+---
diff --git a/src/data/markdown/docs/05 Examples/02 Tutorials/03 openapi.md b/src/data/markdown/docs/05 Examples/02 Tutorials/03 openapi.md
new file mode 100644
index 0000000000..3158c20315
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/02 Tutorials/03 openapi.md
@@ -0,0 +1,4 @@
+---
+title: 'Load testing with Swagger/OpenAPI'
+redirect: 'https://k6.io/blog/load-testing-your-api-with-swagger-openapi-and-k6'
+---
diff --git a/src/data/markdown/docs/05 Examples/02 Tutorials/04 cron.md b/src/data/markdown/docs/05 Examples/02 Tutorials/04 cron.md
new file mode 100644
index 0000000000..a3b4f132da
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/02 Tutorials/04 cron.md
@@ -0,0 +1,4 @@
+---
+title: 'Performance monitoring with cron'
+redirect: 'https://k6.io/blog/performance-monitoring-with-cron-and-k6'
+---
diff --git a/src/data/markdown/docs/05 Examples/02 Tutorials/05 kafka.md b/src/data/markdown/docs/05 Examples/02 Tutorials/05 kafka.md
new file mode 100644
index 0000000000..c4c1c8212d
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/02 Tutorials/05 kafka.md
@@ -0,0 +1,4 @@
+---
+title: 'k6 output to Apache Kafka'
+redirect: 'https://k6.io/blog/integrating-k6-with-apache-kafka'
+---
diff --git a/src/data/markdown/docs/05 Examples/02 Tutorials/06 graphql.md b/src/data/markdown/docs/05 Examples/02 Tutorials/06 graphql.md
new file mode 100644
index 0000000000..4a4ef54afc
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/02 Tutorials/06 graphql.md
@@ -0,0 +1,4 @@
+---
+title: 'Load testing GraphQL'
+redirect: 'https://k6.io/blog/load-testing-graphql-with-k6'
+---
diff --git a/src/data/markdown/docs/05 Examples/02 Tutorials/07 restful-apis.md b/src/data/markdown/docs/05 Examples/02 Tutorials/07 restful-apis.md
new file mode 100644
index 0000000000..d74a8c2cff
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/02 Tutorials/07 restful-apis.md
@@ -0,0 +1,4 @@
+---
+title: 'Load testing RESTful APIs'
+redirect: 'https://k6.io/blog/load-testing-restful-apis-with-k6'
+---
diff --git a/src/data/markdown/docs/05 Examples/02 Tutorials/08 control-live-test.md b/src/data/markdown/docs/05 Examples/02 Tutorials/08 control-live-test.md
new file mode 100644
index 0000000000..30d97ea500
--- /dev/null
+++ b/src/data/markdown/docs/05 Examples/02 Tutorials/08 control-live-test.md
@@ -0,0 +1,4 @@
+---
+title: 'Control a live k6 test'
+redirect: 'https://k6.io/blog/how-to-control-a-live-k6-test'
+---
diff --git a/src/data/markdown/posts/2020-01-15--load-impact-v3-end-of-life/cover-v3-end-of-life.png b/src/data/markdown/posts/2020-01-15--load-impact-v3-end-of-life/cover-v3-end-of-life.png
new file mode 100755
index 0000000000..c376fb4961
Binary files /dev/null and b/src/data/markdown/posts/2020-01-15--load-impact-v3-end-of-life/cover-v3-end-of-life.png differ
diff --git a/src/data/markdown/posts/2020-01-15--load-impact-v3-end-of-life/index.md b/src/data/markdown/posts/2020-01-15--load-impact-v3-end-of-life/index.md
new file mode 100644
index 0000000000..57e72525a0
--- /dev/null
+++ b/src/data/markdown/posts/2020-01-15--load-impact-v3-end-of-life/index.md
@@ -0,0 +1,48 @@
+---
+title: Deprecation Timeline for LoadImpact v3.0
+summary: LoadImpact v3.0 is currently being deprecated. We plan to formally shut down v3.0 on Dec 31 2020.
+category: 'Releases'
+covertext: 'V3 End Of Life'
+author: Mark Meier, Head of Customer Success
+cover: cover-v3-end-of-life.png
+socialImage: cover-v3-end-of-life.png
+featured: true
+---
+
+In 1940, the American Football Championship Game was played between the Chicago Bears and Washington Redskins. A few weeks earlier the teams faced off and Washington won with a score of 7-3. Moving into the championship game, Chicago knew they had to make a strategic move and adapt the game to regain the upper hand. For the first time in the history of American football, a new strategy was introduced, the “T-formation”. Using this strategy, Chicago handed Washington the most lopsided margins of victory in sports history (73-0) and a
Scorigami . Their adaptation resulted in the team, players and fans enjoying success from their strategic planning.
+
+So what does this have to do with our deprecation timeline for v3.0? I’ll be honest - it’s more of a fun story than anything. But I think there are similarities. LoadImpact v3.0 (Lua), has been a tried and tested load and performance testing solution. It’s executed millions of tests and have helped thousands of users understand how their systems react to various levels of load. But as time ticks on, you learn that things can be improved. In our case, it made sense to come up with an entirely new way to think about testing rather than making small adaptations.
+
+The new version of LoadImpact is the result of years of feedback we’ve gathered and experienced we gained. We think it will enable strategies for you to become more efficient and come to valuable conclusions, faster. Maybe even handing your opponent a 73-0 loss.
+
+I’ve had many conversations with users regarding this transition, I’ve put together some of the most common questions and answers. If you have a question that is not listed here, please reach out using the contact info at the bottom of this post.
+
+## Why are you releasing a new version and new load generator?
+
+Based on feedback we have received from users over the years and from our own observations, we wanted to create a more modern and open source tool for testing. We set out to address the following:
+
+- CLI tool with developer-friendly APIs.
+- Load-Test-As-Code with scripting in JavaScript.
+- Easy automation and powerful performance
Thresholds (Pass/Fail criteria).
+- Integrates with the LoadImpact cloud to accelerate and scale your performance testing.
+
+## What is happening to the version with Lua Scripting?
+
+As we call it, v3.0, is currently being deprecated. We plan to formally shut down v3.0 on Dec 31 2020. After this date it will not be possible to access the service, so we strongly urge you to back up and export any information you need to save.
+
+## Do I need to buy a new plan? Do I have access to v4.0?
+
+For most users, we already added a plan that gives v4.0 access to your account. Due to pricing differences, it is at some different limits than your v3.0 plan. If you would like, you can switch to one of our current v4.0 plans on our
pricing page . If you feel your needs don’t fit into what we’ve enabled or what we have available, please contact us and we can discuss other potential options.
+
+You can view full details of your available plans on your
Account page .
+
+## Where do I go if I need help getting used to v4.0?
+
+Myself and the rest of the customer success team are here to support you. Additionally, we have our knowledge base, extensive documentation, community forum and slack channel where you can get more information.
+
+-
https://support.loadimpact.com/4.0/
+-
https://docs.k6.io/docs
+-
https://community.k6.io/
+-
https://k6.io/slack/
+
+Finally, you are always welcome to contact us directly at
support@loadimpact.com . We would be happy to set up a Q&A session or walkthrough to answer your questions after you have some time to explore v4.0 on your own.
diff --git a/src/hooks/index.js b/src/hooks/index.js
new file mode 100644
index 0000000000..8c6e44e368
--- /dev/null
+++ b/src/hooks/index.js
@@ -0,0 +1,3 @@
+import useLandmark from './useLandmark';
+
+export { useLandmark };
diff --git a/src/hooks/useLandmark.js b/src/hooks/useLandmark.js
new file mode 100644
index 0000000000..b26c37800c
--- /dev/null
+++ b/src/hooks/useLandmark.js
@@ -0,0 +1,48 @@
+import React, { useState, useEffect } from 'react';
+import ReactDOM from 'react-dom';
+import { Heading } from 'components/shared/heading';
+import { slugify } from 'utils';
+import docPageContent from 'components/templates/doc-page/doc-page-content/doc-page-content.module.scss';
+import AnchorIcon from 'components/templates/doc-page/doc-page-content/svg/anchor.inline.svg';
+
+// auxillary component that will replace default ones
+const markHeadingComponent = ({ content }) => (
+
+
+
+
+ {content}
+
+);
+
+const useLandmark = ({ selector, component = markHeadingComponent }) => {
+ const [links, setLinks] = useState([]);
+ useEffect(() => {
+ const Component = component;
+ // get all h2 headings of a parent
+ const allHeadingMarks = document
+ .querySelector(`.${selector}`)
+ .querySelectorAll('h2');
+ allHeadingMarks.forEach(element => {
+ const compContent = element.innerHTML;
+ // Render with container replacement.
+ const temp = document.createElement('div');
+ ReactDOM.render(
, temp, () =>
+ element.parentElement.replaceChild(temp.children[0], element),
+ );
+ });
+ setLinks(
+ Array.from(allHeadingMarks).map(({ innerHTML }) => ({
+ title: innerHTML,
+ anchor: `#${slugify(innerHTML).replace(/\//g, '-')}`,
+ })),
+ );
+ }, []);
+ return { links };
+};
+export default useLandmark;
diff --git a/src/images/NetInfo-API-Blog-post.png b/src/images/NetInfo-API-Blog-post.png
new file mode 100644
index 0000000000..78df4165fa
Binary files /dev/null and b/src/images/NetInfo-API-Blog-post.png differ
diff --git a/src/images/about/about-story-image.png b/src/images/about/about-story-image.png
new file mode 100644
index 0000000000..edb8b9a6ff
Binary files /dev/null and b/src/images/about/about-story-image.png differ
diff --git a/src/images/about/about-team-image.png b/src/images/about/about-team-image.png
new file mode 100644
index 0000000000..f8a58ad841
Binary files /dev/null and b/src/images/about/about-team-image.png differ
diff --git a/src/images/about/about-team/bernard-alexander.png b/src/images/about/about-team/bernard-alexander.png
new file mode 100644
index 0000000000..0759617597
Binary files /dev/null and b/src/images/about/about-team/bernard-alexander.png differ
diff --git a/src/images/about/about-team/courtney-alexander.png b/src/images/about/about-team/courtney-alexander.png
new file mode 100644
index 0000000000..0759617597
Binary files /dev/null and b/src/images/about/about-team/courtney-alexander.png differ
diff --git a/src/images/about/about-team/ronald-edwards.png b/src/images/about/about-team/ronald-edwards.png
new file mode 100644
index 0000000000..0759617597
Binary files /dev/null and b/src/images/about/about-team/ronald-edwards.png differ
diff --git a/src/images/about/about-team/ted-pena.png b/src/images/about/about-team/ted-pena.png
new file mode 100644
index 0000000000..0759617597
Binary files /dev/null and b/src/images/about/about-team/ted-pena.png differ
diff --git a/src/images/cloud-illustrated-note/data-flow@2x.png b/src/images/cloud-illustrated-note/data-flow@2x.png
new file mode 100644
index 0000000000..699e772ed0
Binary files /dev/null and b/src/images/cloud-illustrated-note/data-flow@2x.png differ
diff --git a/src/images/cloud-illustrated-note/hacking-croco@2x.png b/src/images/cloud-illustrated-note/hacking-croco@2x.png
new file mode 100644
index 0000000000..63263b6f81
Binary files /dev/null and b/src/images/cloud-illustrated-note/hacking-croco@2x.png differ
diff --git a/src/images/cloud-promo@2x.png b/src/images/cloud-promo@2x.png
new file mode 100755
index 0000000000..9891fd77e4
Binary files /dev/null and b/src/images/cloud-promo@2x.png differ
diff --git a/src/images/cloud/cloud-features/first-block/01image-1@2x.jpg b/src/images/cloud/cloud-features/first-block/01image-1@2x.jpg
new file mode 100644
index 0000000000..4e132e1552
Binary files /dev/null and b/src/images/cloud/cloud-features/first-block/01image-1@2x.jpg differ
diff --git a/src/images/cloud/cloud-features/first-block/01image-2@2x.jpg b/src/images/cloud/cloud-features/first-block/01image-2@2x.jpg
new file mode 100644
index 0000000000..ab0a70f739
Binary files /dev/null and b/src/images/cloud/cloud-features/first-block/01image-2@2x.jpg differ
diff --git a/src/images/cloud/cloud-features/second-block/02image-1@2x.png b/src/images/cloud/cloud-features/second-block/02image-1@2x.png
new file mode 100644
index 0000000000..5fa2da0b1a
Binary files /dev/null and b/src/images/cloud/cloud-features/second-block/02image-1@2x.png differ
diff --git a/src/images/cloud/cloud-features/second-block/02image-2@2x.png b/src/images/cloud/cloud-features/second-block/02image-2@2x.png
new file mode 100644
index 0000000000..5a1bfd9a06
Binary files /dev/null and b/src/images/cloud/cloud-features/second-block/02image-2@2x.png differ
diff --git a/src/images/cloud/cloud-features/second-block/02image-3@2x.jpg b/src/images/cloud/cloud-features/second-block/02image-3@2x.jpg
new file mode 100644
index 0000000000..8dbe062718
Binary files /dev/null and b/src/images/cloud/cloud-features/second-block/02image-3@2x.jpg differ
diff --git a/src/images/cloud/cloud-features/third-block/03image-1@2x.jpg b/src/images/cloud/cloud-features/third-block/03image-1@2x.jpg
new file mode 100644
index 0000000000..75a8c649ab
Binary files /dev/null and b/src/images/cloud/cloud-features/third-block/03image-1@2x.jpg differ
diff --git a/src/images/cloud/cloud-features/third-block/03image-2@2x.jpg b/src/images/cloud/cloud-features/third-block/03image-2@2x.jpg
new file mode 100644
index 0000000000..909fc2911f
Binary files /dev/null and b/src/images/cloud/cloud-features/third-block/03image-2@2x.jpg differ
diff --git a/src/images/cloud/cloud-hero/cloud-promo.png b/src/images/cloud/cloud-hero/cloud-promo.png
new file mode 100755
index 0000000000..9a3045c9d9
Binary files /dev/null and b/src/images/cloud/cloud-hero/cloud-promo.png differ
diff --git a/src/images/cloud/cloud-hero/cloud-promo@3x.png b/src/images/cloud/cloud-hero/cloud-promo@3x.png
new file mode 100755
index 0000000000..7400e5511a
Binary files /dev/null and b/src/images/cloud/cloud-hero/cloud-promo@3x.png differ
diff --git a/src/images/cloud/cloud-hero/cloud-stub.png b/src/images/cloud/cloud-hero/cloud-stub.png
new file mode 100644
index 0000000000..49ae6de2c0
Binary files /dev/null and b/src/images/cloud/cloud-hero/cloud-stub.png differ
diff --git a/src/images/data-flow@2x.png b/src/images/data-flow@2x.png
new file mode 100644
index 0000000000..699e772ed0
Binary files /dev/null and b/src/images/data-flow@2x.png differ
diff --git a/src/images/doc-integrations/azure.png b/src/images/doc-integrations/azure.png
new file mode 100644
index 0000000000..b310f2650d
Binary files /dev/null and b/src/images/doc-integrations/azure.png differ
diff --git a/src/images/doc-integrations/graphql.png b/src/images/doc-integrations/graphql.png
new file mode 100644
index 0000000000..bad4a50d7a
Binary files /dev/null and b/src/images/doc-integrations/graphql.png differ
diff --git a/src/images/doc-integrations/har.jpg b/src/images/doc-integrations/har.jpg
new file mode 100644
index 0000000000..4c21cdacba
Binary files /dev/null and b/src/images/doc-integrations/har.jpg differ
diff --git a/src/images/doc-integrations/jmeter.png b/src/images/doc-integrations/jmeter.png
new file mode 100644
index 0000000000..261efeb643
Binary files /dev/null and b/src/images/doc-integrations/jmeter.png differ
diff --git a/src/images/doc-integrations/openapi.png b/src/images/doc-integrations/openapi.png
new file mode 100644
index 0000000000..a5fb84f67b
Binary files /dev/null and b/src/images/doc-integrations/openapi.png differ
diff --git a/src/images/doc-integrations/postman.png b/src/images/doc-integrations/postman.png
new file mode 100644
index 0000000000..cfd823454d
Binary files /dev/null and b/src/images/doc-integrations/postman.png differ
diff --git a/src/images/doc-welcome-quick-start/doc-welcome-quick-start-image-1.png b/src/images/doc-welcome-quick-start/doc-welcome-quick-start-image-1.png
new file mode 100644
index 0000000000..44e0e4a321
Binary files /dev/null and b/src/images/doc-welcome-quick-start/doc-welcome-quick-start-image-1.png differ
diff --git a/src/images/doc-welcome-quick-start/doc-welcome-quick-start-image-2.png b/src/images/doc-welcome-quick-start/doc-welcome-quick-start-image-2.png
new file mode 100644
index 0000000000..eeb50af6d8
Binary files /dev/null and b/src/images/doc-welcome-quick-start/doc-welcome-quick-start-image-2.png differ
diff --git a/src/images/doc-welcome-quick-start/doc-welcome-quick-start-image-3.png b/src/images/doc-welcome-quick-start/doc-welcome-quick-start-image-3.png
new file mode 100644
index 0000000000..371061e673
Binary files /dev/null and b/src/images/doc-welcome-quick-start/doc-welcome-quick-start-image-3.png differ
diff --git a/src/images/favicon.png b/src/images/favicon.png
new file mode 100644
index 0000000000..4b01474386
Binary files /dev/null and b/src/images/favicon.png differ
diff --git a/src/images/home-built-for/developers-closed.png b/src/images/home-built-for/developers-closed.png
new file mode 100644
index 0000000000..d25094d914
Binary files /dev/null and b/src/images/home-built-for/developers-closed.png differ
diff --git a/src/images/home-built-for/developers.png b/src/images/home-built-for/developers.png
new file mode 100644
index 0000000000..12b23afd72
Binary files /dev/null and b/src/images/home-built-for/developers.png differ
diff --git a/src/images/home-built-for/devops-closed.png b/src/images/home-built-for/devops-closed.png
new file mode 100644
index 0000000000..7df5f07b00
Binary files /dev/null and b/src/images/home-built-for/devops-closed.png differ
diff --git a/src/images/home-built-for/devops.png b/src/images/home-built-for/devops.png
new file mode 100644
index 0000000000..6acdfb9571
Binary files /dev/null and b/src/images/home-built-for/devops.png differ
diff --git a/src/images/home-built-for/managers-closed.png b/src/images/home-built-for/managers-closed.png
new file mode 100644
index 0000000000..f57a87238e
Binary files /dev/null and b/src/images/home-built-for/managers-closed.png differ
diff --git a/src/images/home-built-for/managers.png b/src/images/home-built-for/managers.png
new file mode 100644
index 0000000000..2de597c284
Binary files /dev/null and b/src/images/home-built-for/managers.png differ
diff --git a/src/images/how-it-works/how-it-works.png b/src/images/how-it-works/how-it-works.png
new file mode 100644
index 0000000000..505a73e9f5
Binary files /dev/null and b/src/images/how-it-works/how-it-works.png differ
diff --git a/src/images/how-it-works/stub.png b/src/images/how-it-works/stub.png
new file mode 100644
index 0000000000..bb474cce63
Binary files /dev/null and b/src/images/how-it-works/stub.png differ
diff --git a/src/images/icon.png b/src/images/icon.png
new file mode 100644
index 0000000000..0419299be0
Binary files /dev/null and b/src/images/icon.png differ
diff --git a/src/images/landscape-icon.png b/src/images/landscape-icon.png
new file mode 100644
index 0000000000..99232924a8
Binary files /dev/null and b/src/images/landscape-icon.png differ
diff --git a/src/images/native-automation-instruction/native-automation-fourth-step-image.png b/src/images/native-automation-instruction/native-automation-fourth-step-image.png
new file mode 100644
index 0000000000..52d097a618
Binary files /dev/null and b/src/images/native-automation-instruction/native-automation-fourth-step-image.png differ
diff --git a/src/images/native-automation-instruction/native-automation-instruction-image.png b/src/images/native-automation-instruction/native-automation-instruction-image.png
new file mode 100644
index 0000000000..8a708448e6
Binary files /dev/null and b/src/images/native-automation-instruction/native-automation-instruction-image.png differ
diff --git a/src/images/open-source-cases/open-source-use-cases-image.png b/src/images/open-source-cases/open-source-use-cases-image.png
new file mode 100644
index 0000000000..3ce5e59874
Binary files /dev/null and b/src/images/open-source-cases/open-source-use-cases-image.png differ
diff --git a/src/images/sample-image@2x.png b/src/images/sample-image@2x.png
new file mode 100644
index 0000000000..be69cdd0a9
Binary files /dev/null and b/src/images/sample-image@2x.png differ
diff --git a/src/images/vertical-image-query-test.jpg b/src/images/vertical-image-query-test.jpg
new file mode 100644
index 0000000000..ef9acfc894
Binary files /dev/null and b/src/images/vertical-image-query-test.jpg differ
diff --git a/src/layouts/default-layout/default-layout.module.scss b/src/layouts/default-layout/default-layout.module.scss
new file mode 100644
index 0000000000..f871958fe3
--- /dev/null
+++ b/src/layouts/default-layout/default-layout.module.scss
@@ -0,0 +1,18 @@
+.button {
+ margin-right: 5px;
+
+ &:last-child {
+ margin-right: 0;
+ }
+}
+
+.buttons-wrapper {
+ @include md-down {
+ display: none;
+ }
+}
+:global .react-cookie-banner {
+ background-color: $color-primary;
+ color: $color-tertiary;
+ padding: 10px 20px;
+}
diff --git a/src/layouts/default-layout/default-layout.view.js b/src/layouts/default-layout/default-layout.view.js
new file mode 100644
index 0000000000..b5c00d9b69
--- /dev/null
+++ b/src/layouts/default-layout/default-layout.view.js
@@ -0,0 +1,116 @@
+import React from 'react';
+import {
+ Cookies,
+ CookiesProvider,
+ CookieBannerUniversal,
+} from 'react-cookie-banner';
+import HelperWidget from 'components/shared/helper-widget';
+
+import {
+ Header,
+ HeaderNav,
+ HeaderLogo,
+ Burger,
+} from 'components/blocks/header';
+import { Footer } from 'components/blocks/footer';
+import { MobileNav } from 'components/blocks/mobile-nav';
+import { SEO } from 'components/shared/seo';
+import { Button } from 'components/shared/button';
+
+import styles from './default-layout.module.scss';
+import CookieConsent from 'components/shared/cookie-consent';
+import { app, main, blog, docs } from 'utils/urls';
+
+const cookies = new Cookies({ 'user-has-accepted-cookies': true });
+
+export const DefaultLayout = ({ pageMetadata, children }) => {
+ const [isMobileNavVisible, setIsMobileNavVisible] = React.useState(false);
+
+ React.useEffect(() => {
+ if (isMobileNavVisible) {
+ document.querySelector('html').style.overflow = 'hidden';
+ } else {
+ document.querySelector('html').style.overflow = '';
+ }
+ }, [isMobileNavVisible]);
+
+ const links = [
+ { label: 'Cloud', to: `${main}/cloud` },
+ { label: 'Open Source', to: `${main}/open-source` },
+ { label: 'Documentation', to: `${docs}` },
+ { label: 'Pricing', to: `${main}/pricing` },
+ {
+ label: 'About',
+ submenu: [
+ { label: 'Our Story', to: `${main}/about` },
+ { label: 'Our Beliefs', to: `${main}/our-beliefs` },
+ { label: 'Blog', to: `${blog}` },
+ { label: 'Contact Us', to: `${main}/contact` },
+ { label: 'Jobs', to: `${main}/jobs` },
+ ],
+ },
+ ];
+
+ return (
+ <>
+
+
+
+ {children}
+
+
+ cur.submenu ? acc.concat([...cur.submenu]) : acc.concat(cur),
+ [],
+ )}
+ isVisible={isMobileNavVisible}
+ onCloseButtonClick={() => setIsMobileNavVisible(false)}
+ />
+
+
+
+ {onAccept => }
+
+
+
+ >
+ );
+};
diff --git a/src/layouts/default-layout/index.js b/src/layouts/default-layout/index.js
new file mode 100644
index 0000000000..bc65bc3493
--- /dev/null
+++ b/src/layouts/default-layout/index.js
@@ -0,0 +1 @@
+export { DefaultLayout } from './default-layout.view';
diff --git a/src/layouts/doc-layout/doc-layout.module.scss b/src/layouts/doc-layout/doc-layout.module.scss
new file mode 100644
index 0000000000..6bab97ebfd
--- /dev/null
+++ b/src/layouts/doc-layout/doc-layout.module.scss
@@ -0,0 +1,165 @@
+.wrapper {
+ display: flex;
+ min-height: 100vh;
+ max-width: 1600px;
+}
+
+.sidebar {
+ width: 270px;
+ flex-shrink: 0;
+ background-color: $color-additional-3;
+ border-right: 1px solid $color-additional-2;
+ @include lg-down {
+ width: 230px;
+ }
+ @include md-down {
+ width: 195px;
+ }
+ @include sm-down {
+ display: none;
+ }
+}
+.search-box {
+ padding-bottom: 5px;
+}
+.dropdown-wrapper {
+ box-shadow: $light-block-shadow;
+ position: relative;
+ &:after {
+ content: '';
+ position: absolute;
+ right: 15px;
+ top: calc(50% - 2.5px);
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-top: 5px solid $color-secondary;
+ width: 0px;
+ height: 0px;
+ opacity: 0.5;
+ }
+}
+.dropdown {
+ appearance: none;
+ cursor: pointer;
+ border-radius: 0;
+ border: 1px solid $color-secondary;
+ padding: 12px 40px 11px 15px;
+ width: 100%;
+ background: transparent;
+ font-size: $font-size-sm;
+ line-height: $line-height-sm;
+ color: $color-primary;
+ font-weight: 500;
+ outline: none;
+ transition: 0.3s border-color;
+ &::-moz-focusring {
+ border: none;
+ }
+ &:focus {
+ border-color: $color-accent-primary;
+ }
+}
+
+.sidebar-section {
+ padding: 20px;
+ border-bottom: 1px solid $color-additional-2;
+ font-size: $font-size-sm;
+ line-height: $line-height-sm;
+}
+
+// bumping specifity
+h2.sidebar-section-title {
+ margin-bottom: 10px;
+ font-weight: 600;
+ text-align: start;
+ text-transform: none;
+ color: $color-secondary;
+}
+
+.sidebar-section-title_link {
+ text-decoration: none;
+ color: $color-secondary;
+ transition: 0.3s ease;
+ font-weight: 600;
+ &:hover {
+ color: $color-secondary;
+ border-color: rgba(90, 92, 135, 0.5);
+ }
+}
+
+.sidebarNodeWithChildren > a {
+ &.sidebar-node-title_active {
+ &:after {
+ content: '';
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 0 4px 4px 4px;
+ border-color: transparent transparent #5a5c87 transparent;
+ display: inline-block;
+ margin-left: 5px;
+ vertical-align: middle;
+ }
+ }
+ &:after {
+ content: '';
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 4px 0 4px 4px;
+ border-color: transparent transparent transparent #5a5c87;
+ display: inline-block;
+ margin-left: 5px;
+ vertical-align: middle;
+ }
+}
+
+.sidebar-node-title {
+ display: block;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ text-decoration: none;
+ color: $color-primary;
+ transition: font-weight 0.3s;
+ font-size: $font-size-sm;
+ line-height: $line-height-sm;
+ margin-bottom: 5px;
+ &:hover {
+ color: $color-accent-primary;
+ }
+}
+
+.sidebar-node-title_active {
+ color: $color-accent-primary;
+ font-weight: 500;
+}
+
+.sidebar-node-children {
+ padding-left: 10px;
+}
+
+a.sidebar-link {
+ color: $color-accent-primary;
+ font-weight: 500;
+}
+
+.main {
+ position: relative;
+ margin: 0 50px;
+ max-width: 1175px;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ @include lg-down {
+ max-width: calc(100% - 330px); // respecting sidebar width and margins
+ }
+ @include md-down {
+ max-width: calc(100% - 240px); // respecting sidebar width and margins
+ margin: 0 25px;
+ }
+ @include sm-down {
+ max-width: 100%;
+ margin: 0 auto;
+ }
+}
diff --git a/src/layouts/doc-layout/doc-layout.view.js b/src/layouts/doc-layout/doc-layout.view.js
new file mode 100644
index 0000000000..6a70314c20
--- /dev/null
+++ b/src/layouts/doc-layout/doc-layout.view.js
@@ -0,0 +1,290 @@
+import React, { useState, useEffect } from 'react';
+import { Link, navigate, withPrefix } from 'gatsby';
+import {
+ Cookies,
+ CookiesProvider,
+ CookieBannerUniversal,
+} from 'react-cookie-banner';
+import { Heading } from 'components/shared/heading';
+import HelperWidget from 'components/shared/helper-widget';
+import {
+ Header,
+ HeaderNav,
+ HeaderLogo,
+ Burger,
+} from 'components/blocks/header';
+import { SearchBox } from 'components/shared/search-box';
+import { Footer } from 'components/blocks/footer';
+import { SEO } from 'components/shared/seo';
+import { MobileNav } from 'components/blocks/mobile-nav';
+import { childrenToList } from 'utils';
+
+import styles from './doc-layout.module.scss';
+import { slugify, isInIFrame } from 'utils';
+
+import CookieConsent from 'components/shared/cookie-consent';
+import _ from 'lodash/lang';
+
+import { main } from 'utils/urls';
+import AlgoliaQueries from 'utils/algolia';
+
+const indexName = AlgoliaQueries[0].indexName;
+
+// local helper data
+//
+// algolia indices
+const searchIndices = [
+ { name: indexName, title: 'Doc Pages', hitComp: 'docPageHit' },
+];
+
+const cookies = new Cookies({ 'user-has-accepted-cookies': true });
+
+/*
+ * Local Helper Components
+ */
+
+// renders options from the passed children array, recursively
+const OptionsGroup = ({ node: { name, meta, children }, nested }) => {
+ const [isActive, setIsActive] = useState(false);
+
+ useEffect(() => {
+ const maybePrefixedPath = withPrefix(meta.path);
+ const doesPathMatchLocation =
+ maybePrefixedPath === window.location.pathname;
+ const isPathLocationPart =
+ meta.path === '/'
+ ? false
+ : window.location.pathname.startsWith(`${maybePrefixedPath}/`) ||
+ window.location.pathname.startsWith(
+ `${maybePrefixedPath
+ .split('/')
+ .slice(0, -1)
+ .concat(slugify(name))
+ .join('/')}/`,
+ );
+ setIsActive(doesPathMatchLocation || isPathLocationPart);
+ }, []);
+
+ const hasSubMenu = !_.isEmpty(children);
+ return (
+ <>
+
+ {meta.title || name}
+
+ {hasSubMenu && (
+ <>
+ {childrenToList(children).map(node => (
+
+ ))}
+ >
+ )}
+ >
+ );
+};
+
+// renders sidebar nodes from passed children prop, recursively
+const SidebarNode = props => {
+ const {
+ node: { name, meta, children },
+ } = props;
+
+ const isLink = meta && meta.path;
+ const [isActive, setIsActive] = useState(false);
+
+ useEffect(() => {
+ const maybePrefixedPath = withPrefix(meta.path);
+ const doesPathMatchLocation =
+ maybePrefixedPath === window.location.pathname;
+ const isPathLocationPart =
+ meta.path === '/'
+ ? false
+ : window.location.pathname.startsWith(`${maybePrefixedPath}/`) ||
+ window.location.pathname.startsWith(
+ `${maybePrefixedPath
+ .split('/')
+ .slice(0, -1)
+ .concat(slugify(name))
+ .join('/')}/`,
+ );
+ setIsActive(doesPathMatchLocation || isPathLocationPart);
+ }, []);
+
+ const hasSubMenu = !_.isEmpty(children);
+
+ return (
+
+ {isLink ? (
+ meta.redirect ? (
+
+ {meta.title}
+
+ ) : (
+
+ {meta.title}
+
+ )
+ ) : (
+
+ {name}
+
+ )}
+ {children && isActive && (
+
+ {childrenToList(children).map(node => (
+
+ ))}
+
+ )}
+
+ );
+};
+
+/*
+ * Main Component
+ */
+
+export const DocLayout = ({
+ pageMetadata,
+ sidebarTree,
+ navLinks: links,
+ children,
+}) => {
+ const [isMobileNavVisible, setIsMobileNavVisible] = useState(false);
+ const [showFooter, setShowFooter] = useState(true);
+ useEffect(() => {
+ if (isMobileNavVisible) {
+ document.querySelector('html').style.overflow = 'hidden';
+ } else {
+ document.querySelector('html').style.overflow = '';
+ }
+ }, [isMobileNavVisible]);
+
+ useEffect(() => setShowFooter(!isInIFrame()), []);
+
+ return (
+
+
+
+
+ {sidebarTree &&
+ childrenToList(sidebarTree.children).map(sectionNode => (
+
+ {sectionNode.meta.path ? (
+
+
+ {sectionNode.meta.title || sectionNode.name}
+
+
+ ) : (
+
+ {sectionNode.meta.title || sectionNode.name}
+
+ )}
+
+ {childrenToList(sectionNode.children).map(node => (
+
+ ))}
+
+
+ ))}
+
+
+
+
+
+
+ {children}
+ setIsMobileNavVisible(false)}
+ />
+ {showFooter && }
+
+
+
+ {onAccept => }
+
+
+
+
+ );
+};
diff --git a/src/layouts/doc-layout/index.js b/src/layouts/doc-layout/index.js
new file mode 100644
index 0000000000..d498dd0a61
--- /dev/null
+++ b/src/layouts/doc-layout/index.js
@@ -0,0 +1 @@
+export { DocLayout } from './doc-layout.view';
diff --git a/src/pages/404.js b/src/pages/404.js
new file mode 100644
index 0000000000..91427f4939
--- /dev/null
+++ b/src/pages/404.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import { DefaultLayout } from 'layouts/default-layout';
+import { NotFound } from 'components/pages/404/not-found';
+
+export default function(props) {
+ return (
+
+
+
+ );
+}
diff --git a/src/styles/fonts/tt-norms-pro/tt-pro-bold-webfont.woff b/src/styles/fonts/tt-norms-pro/tt-pro-bold-webfont.woff
new file mode 100644
index 0000000000..26ebdb1c41
Binary files /dev/null and b/src/styles/fonts/tt-norms-pro/tt-pro-bold-webfont.woff differ
diff --git a/src/styles/fonts/tt-norms-pro/tt-pro-bold-webfont.woff2 b/src/styles/fonts/tt-norms-pro/tt-pro-bold-webfont.woff2
new file mode 100644
index 0000000000..d176ca74f7
Binary files /dev/null and b/src/styles/fonts/tt-norms-pro/tt-pro-bold-webfont.woff2 differ
diff --git a/src/styles/fonts/tt-norms-pro/tt-pro-medium-webfont.woff b/src/styles/fonts/tt-norms-pro/tt-pro-medium-webfont.woff
new file mode 100644
index 0000000000..c2eba934a5
Binary files /dev/null and b/src/styles/fonts/tt-norms-pro/tt-pro-medium-webfont.woff differ
diff --git a/src/styles/fonts/tt-norms-pro/tt-pro-medium-webfont.woff2 b/src/styles/fonts/tt-norms-pro/tt-pro-medium-webfont.woff2
new file mode 100644
index 0000000000..e94f25bbbc
Binary files /dev/null and b/src/styles/fonts/tt-norms-pro/tt-pro-medium-webfont.woff2 differ
diff --git a/src/styles/fonts/tt-norms-pro/tt-pro-regular-webfont.woff b/src/styles/fonts/tt-norms-pro/tt-pro-regular-webfont.woff
new file mode 100644
index 0000000000..634d50c2f8
Binary files /dev/null and b/src/styles/fonts/tt-norms-pro/tt-pro-regular-webfont.woff differ
diff --git a/src/styles/fonts/tt-norms-pro/tt-pro-regular-webfont.woff2 b/src/styles/fonts/tt-norms-pro/tt-pro-regular-webfont.woff2
new file mode 100644
index 0000000000..0b9da6f5b6
Binary files /dev/null and b/src/styles/fonts/tt-norms-pro/tt-pro-regular-webfont.woff2 differ
diff --git a/src/styles/mixins.scss b/src/styles/mixins.scss
new file mode 100644
index 0000000000..c3b8e7b0bc
--- /dev/null
+++ b/src/styles/mixins.scss
@@ -0,0 +1,77 @@
+@mixin lg-down {
+ @media only screen and (max-width: $screen-lg-down) {
+ @content;
+ }
+}
+
+@mixin md-down {
+ @media only screen and (max-width: $screen-md-down) {
+ @content;
+ }
+}
+
+@mixin sm-down {
+ @media only screen and (max-width: $screen-sm-down) {
+ @content;
+ }
+}
+
+@mixin xs-down {
+ @media only screen and (max-width: $screen-xs-down) {
+ @content;
+ }
+}
+
+@mixin retina {
+ @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
+ @content;
+ }
+}
+
+// disable blue highlight on selected elements (mobile)
+@mixin no-highlights-on-tap {
+ -webkit-tap-highlight-color: transparent;
+ -webkit-touch-callout: none;
+}
+
+// mixing for wrapping hover logic to be put into effect only on desktops
+@mixin hover-supported {
+ @media not all and (hover: none) {
+ &:hover {
+ @content;
+ }
+ }
+}
+
+// detecting ios device, explanation https://stackoverflow.com/questions/30102792/css-media-query-to-target-only-ios-devices
+@mixin ios-specific {
+ @supports (-webkit-overflow-scrolling: touch) {
+ @content;
+ }
+}
+
+@mixin no-scrollbars {
+ -ms-overflow-style: none; // IE 10+
+ scrollbar-width: none; // Firefox
+ &::-webkit-scrollbar {
+ width: 0; // Safari and Chrome
+ height: 0;
+ }
+}
+
+@mixin default-section-spacing {
+ padding-bottom: $spacing-xl-down;
+ @include lg-down {
+ padding-bottom: $spacing-lg-down;
+ }
+ @include md-down {
+ padding-bottom: $spacing-md-down;
+ }
+ @include sm-down {
+ padding-bottom: $spacing-sm-down;
+ }
+}
+
+@mixin doc-section-spacing {
+ padding-bottom: $doc-spacing-xl-down;
+}
diff --git a/src/styles/parts/fonts.scss b/src/styles/parts/fonts.scss
new file mode 100644
index 0000000000..683a6f49fa
--- /dev/null
+++ b/src/styles/parts/fonts.scss
@@ -0,0 +1,50 @@
+/**
+ * @license
+ * MyFonts Webfont Build ID 3805822, 2019-09-10T00:13:33-0400
+ *
+ * The fonts listed in this notice are subject to the End User License
+ * Agreement(s) entered into by the website owner. All other parties are
+ * explicitly restricted from using the Licensed Webfonts(s).
+ *
+ * You may obtain a valid license at the URLs below.
+ *
+ * Webfont: TTNormsPro-Bold by TypeType
+ * URL: https://www.myfonts.com/fonts/type-type/tt-norms/bold/
+ *
+ * Webfont: TTNormsPro-Medium by TypeType
+ * URL: https://www.myfonts.com/fonts/type-type/tt-norms/medium/
+ *
+ * Webfont: TTNormsPro-Regular by TypeType
+ * URL: https://www.myfonts.com/fonts/type-type/tt-norms/regular/
+ *
+ *
+ * License: https://www.myfonts.com/viewlicense?type=web&buildid=3805822
+ * Licensed pageviews: 200,000
+ * Webfonts copyright: Copyright (c) 2016-2019 by TypeType. Designers Ivan Gladkikh, Pavel Emelyanov. Technical designers Vika Usmanova, Olexa Volochay, Nadyr Rakhimov. All rights reserved.
+ *
+ * © 2019 MyFonts Inc
+*/
+
+@font-face {
+ font-family: 'TT Norms Pro';
+ font-style: normal;
+ font-weight: 400;
+ src: url('./fonts/tt-norms-pro/tt-pro-regular-webfont.woff2') format('woff2'),
+ url('./fonts/tt-norms-pro/tt-pro-regular-webfont.woff') format('woff');
+}
+
+@font-face {
+ font-family: 'TT Norms Pro';
+ font-style: normal;
+ font-weight: 500;
+ src: url('./fonts/tt-norms-pro/tt-pro-medium-webfont.woff2') format('woff2'),
+ url('./fonts/tt-norms-pro/tt-pro-medium-webfont.woff') format('woff');
+}
+
+@font-face {
+ font-family: 'TT Norms Pro';
+ font-style: normal;
+ font-weight: 700;
+ src: url('./fonts/tt-norms-pro/tt-pro-bold-webfont.woff2') format('woff2'),
+ url('./fonts/tt-norms-pro/tt-pro-bold-webfont.woff') format('woff');
+}
diff --git a/src/styles/parts/global.scss b/src/styles/parts/global.scss
new file mode 100644
index 0000000000..4c60bb2a1b
--- /dev/null
+++ b/src/styles/parts/global.scss
@@ -0,0 +1,102 @@
+*,
+*:before,
+*:after {
+ box-sizing: border-box;
+ @include no-highlights-on-tap; //disable default blue background on tap
+}
+
+html {
+ overflow-x: hidden;
+ -ms-overflow-style: scrollbar;
+}
+
+body {
+ position: relative;
+ min-width: 320px;
+ min-height: 100vh;
+ font-family: $font-family-primary;
+ font-size: $font-size-base;
+ line-height: $line-height-base;
+ color: $color-primary;
+ overflow-x: hidden;
+ -ms-overflow-style: scrollbar;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+/**
+ * Default values for heading tags in k6 project, moved from
+ * Heading component
+**/
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-size: 30px;
+ line-height: 40px;
+ font-weight: 400;
+ margin: 0;
+ @include md-down {
+ font-size: 25px;
+ line-height: 30px;
+ }
+}
+
+a,
+button,
+input,
+img,
+video {
+ outline: none;
+ &:-moz-focusring {
+ outline: none;
+ border: none;
+ }
+ &:-moz-focus-inner {
+ outline: none;
+ border: none;
+ }
+}
+
+.link {
+ position: relative;
+ text-decoration: none;
+ color: $color-accent-primary;
+ font-weight: 500;
+ transition: 0.3s all;
+ outline: none;
+ font-size: inherit;
+ line-height: inherit;
+ cursor: pointer;
+ padding-bottom: 1px;
+ border-bottom: 1px solid rgba($color-accent-primary, 0);
+ transition: all 0.3s;
+ &:hover {
+ border-bottom: 1px solid rgba($color-accent-primary, 0.5);
+ }
+ // to treat links delivered to ui from json at pricing/faq
+ &--faq {
+ font-weight: 400;
+ }
+}
+
+body .show-md-down {
+ @include md-down {
+ display: initial;
+ }
+ @media only screen and (min-width: 992px) {
+ display: none;
+ }
+}
+
+body .hide-md-down {
+ @include md-down {
+ display: none;
+ }
+ @media only screen and (min-width: 992px) {
+ display: initial;
+ }
+}
diff --git a/src/styles/parts/root-doc.module.scss b/src/styles/parts/root-doc.module.scss
new file mode 100644
index 0000000000..b1cd3f2182
--- /dev/null
+++ b/src/styles/parts/root-doc.module.scss
@@ -0,0 +1,25 @@
+.main-content {
+ margin-top: 135px;
+ @include doc-section-spacing;
+ @include lg-down {
+ margin-top: 200px;
+ }
+ @include md-down {
+ margin-top: 245px;
+ }
+ @include sm-down {
+ margin-top: 265px;
+ }
+ @include doc-section-spacing;
+ &.short {
+ max-width: calc(
+ 100% - 325px
+ ); // preserving the spacing and width in adaptive
+ &_beliefs {
+ max-width: calc(100% - 400px);
+ }
+ @include lg-down {
+ max-width: 100%;
+ }
+ }
+}
diff --git a/src/styles/styles.scss b/src/styles/styles.scss
new file mode 100644
index 0000000000..f68e629d99
--- /dev/null
+++ b/src/styles/styles.scss
@@ -0,0 +1,10 @@
+@import "variables";
+@import "mixins";
+
+@import "vendor/normalize";
+@import "vendor/bootstrap-grid";
+@import '~prismjs/themes/prism.css';
+@import '~prismjs/plugins/line-numbers/prism-line-numbers.css';
+
+@import "parts/fonts";
+@import "parts/global";
diff --git a/src/styles/variables.scss b/src/styles/variables.scss
new file mode 100644
index 0000000000..cd11e2c3b6
--- /dev/null
+++ b/src/styles/variables.scss
@@ -0,0 +1,57 @@
+// Font families
+$font-family-primary: 'TT Norms Pro', sans-serif;
+$font-family-secondary: 'Roboto Mono', sans-serif;
+
+// Font sizes and line heights
+$font-size-base: 17px;
+$line-height-base: 25px;
+
+$font-size-xl: 30px;
+$line-height-xl: 40px;
+
+$font-size-lg: 20px;
+$line-height-lg: 25px;
+
+$font-size-sm: 15px;
+$line-height-sm: 20px;
+
+$font-size-xs: 12px;
+$line-height-xs: 18px; //changed from 15, wrong initial value
+
+$font-size-code: 14px;
+$line-height-code: 25px;
+
+// Box shadows
+
+$light-block-shadow: 0px 1px 5px rgba(60, 60, 100, 0.05);
+$dark-block-shadow: 0px 1px 5px rgba(60, 60, 100, 0.3);
+
+// Colors
+$color-primary: #3c3c64;
+$color-secondary: #5a5c87;
+$color-tertiary: #ffffff;
+
+$color-accent-primary: #7d64ff;
+$color-accent-secondary: #00cdff;
+
+$color-success: #28cd8c;
+$color-error: #fa3287;
+
+$color-additional-1: #beb9d7;
+$color-additional-2: #ece8f1;
+$color-additional-3: #f9f8fc;
+
+// Breakpoints
+$screen-xs-down: 575.98px;
+$screen-sm-down: 767.98px;
+$screen-md-down: 991.98px;
+$screen-lg-down: 1199.98px;
+
+// Section spacing
+$spacing-sm-down: 70px;
+$spacing-md-down: 90px;
+$spacing-lg-down: 120px;
+$spacing-xl-down: 150px;
+
+// Doc section spacing
+$doc-spacing-xl-down: 50px;
diff --git a/src/styles/vendor/bootstrap-grid.scss b/src/styles/vendor/bootstrap-grid.scss
new file mode 100644
index 0000000000..34258edef4
--- /dev/null
+++ b/src/styles/vendor/bootstrap-grid.scss
@@ -0,0 +1,1960 @@
+/*!
+* Bootstrap Grid v4.3.1 (https://getbootstrap.com/)
+* Copyright 2011-2019 The Bootstrap Authors
+* Copyright 2011-2019 Twitter, Inc.
+* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+*/
+
+.container {
+ width: 100%;
+ padding-right: 20px; // change requested by V.
+ padding-left: 20px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+@media (min-width: 576px) {
+ .container {
+ max-width: 540px;
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+
+@media (min-width: 768px) {
+ .container {
+ max-width: 720px;
+ }
+}
+
+@media (min-width: 992px) {
+ .container {
+ max-width: 960px;
+ }
+}
+
+@media (min-width: 1200px) {
+ .container {
+ max-width: 1175px;
+ }
+}
+
+.container-fluid {
+ width: 100%;
+ padding-right: 12.5px;
+ padding-left: 12.5px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.row {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ margin: -12.5px;
+}
+
+.no-gutters {
+ margin-right: 0;
+ margin-left: 0;
+}
+
+.no-gutters > .col,
+.no-gutters > [class*="col-"] {
+ padding-right: 0;
+ padding-left: 0;
+}
+
+.col-1,
+.col-2,
+.col-3,
+.col-4,
+.col-5,
+.col-6,
+.col-7,
+.col-8,
+.col-9,
+.col-10,
+.col-11,
+.col-12,
+.col,
+.col-auto,
+.col-sm-1,
+.col-sm-2,
+.col-sm-3,
+.col-sm-4,
+.col-sm-5,
+.col-sm-6,
+.col-sm-7,
+.col-sm-8,
+.col-sm-9,
+.col-sm-10,
+.col-sm-11,
+.col-sm-12,
+.col-sm,
+.col-sm-auto,
+.col-md-1,
+.col-md-2,
+.col-md-3,
+.col-md-4,
+.col-md-5,
+.col-md-6,
+.col-md-7,
+.col-md-8,
+.col-md-9,
+.col-md-10,
+.col-md-11,
+.col-md-12,
+.col-md,
+.col-md-auto,
+.col-lg-1,
+.col-lg-2,
+.col-lg-3,
+.col-lg-4,
+.col-lg-5,
+.col-lg-6,
+.col-lg-7,
+.col-lg-8,
+.col-lg-9,
+.col-lg-10,
+.col-lg-11,
+.col-lg-12,
+.col-lg,
+.col-lg-auto,
+.col-xl-1,
+.col-xl-2,
+.col-xl-3,
+.col-xl-4,
+.col-xl-5,
+.col-xl-6,
+.col-xl-7,
+.col-xl-8,
+.col-xl-9,
+.col-xl-10,
+.col-xl-11,
+.col-xl-12,
+.col-xl,
+.col-xl-auto {
+ position: relative;
+ width: 100%;
+ padding: 12.5px;
+}
+
+.col {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+}
+
+.col-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+}
+
+.col-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+}
+
+.col-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+}
+
+.col-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+}
+
+.col-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+}
+
+.col-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+}
+
+.col-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+}
+
+.col-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+}
+
+.col-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+}
+
+.col-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+}
+
+.col-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+}
+
+.col-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+}
+
+.col-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+}
+
+.order-first {
+ -ms-flex-order: -1;
+ order: -1;
+}
+
+.order-last {
+ -ms-flex-order: 13;
+ order: 13;
+}
+
+.order-0 {
+ -ms-flex-order: 0;
+ order: 0;
+}
+
+.order-1 {
+ -ms-flex-order: 1;
+ order: 1;
+}
+
+.order-2 {
+ -ms-flex-order: 2;
+ order: 2;
+}
+
+.order-3 {
+ -ms-flex-order: 3;
+ order: 3;
+}
+
+.order-4 {
+ -ms-flex-order: 4;
+ order: 4;
+}
+
+.order-5 {
+ -ms-flex-order: 5;
+ order: 5;
+}
+
+.order-6 {
+ -ms-flex-order: 6;
+ order: 6;
+}
+
+.order-7 {
+ -ms-flex-order: 7;
+ order: 7;
+}
+
+.order-8 {
+ -ms-flex-order: 8;
+ order: 8;
+}
+
+.order-9 {
+ -ms-flex-order: 9;
+ order: 9;
+}
+
+.order-10 {
+ -ms-flex-order: 10;
+ order: 10;
+}
+
+.order-11 {
+ -ms-flex-order: 11;
+ order: 11;
+}
+
+.order-12 {
+ -ms-flex-order: 12;
+ order: 12;
+}
+
+.offset-1 {
+ margin-left: 8.333333%;
+}
+
+.offset-2 {
+ margin-left: 16.666667%;
+}
+
+.offset-3 {
+ margin-left: 25%;
+}
+
+.offset-4 {
+ margin-left: 33.333333%;
+}
+
+.offset-5 {
+ margin-left: 41.666667%;
+}
+
+.offset-6 {
+ margin-left: 50%;
+}
+
+.offset-7 {
+ margin-left: 58.333333%;
+}
+
+.offset-8 {
+ margin-left: 66.666667%;
+}
+
+.offset-9 {
+ margin-left: 75%;
+}
+
+.offset-10 {
+ margin-left: 83.333333%;
+}
+
+.offset-11 {
+ margin-left: 91.666667%;
+}
+
+@media (min-width: 576px) {
+ .col-sm {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+ .col-sm-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+ }
+ .col-sm-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+ }
+ .col-sm-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+ .col-sm-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+ .col-sm-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+ .col-sm-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+ }
+ .col-sm-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+ .col-sm-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+ }
+ .col-sm-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+ }
+ .col-sm-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+ .col-sm-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+ }
+ .col-sm-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+ }
+ .col-sm-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+ .order-sm-first {
+ -ms-flex-order: -1;
+ order: -1;
+ }
+ .order-sm-last {
+ -ms-flex-order: 13;
+ order: 13;
+ }
+ .order-sm-0 {
+ -ms-flex-order: 0;
+ order: 0;
+ }
+ .order-sm-1 {
+ -ms-flex-order: 1;
+ order: 1;
+ }
+ .order-sm-2 {
+ -ms-flex-order: 2;
+ order: 2;
+ }
+ .order-sm-3 {
+ -ms-flex-order: 3;
+ order: 3;
+ }
+ .order-sm-4 {
+ -ms-flex-order: 4;
+ order: 4;
+ }
+ .order-sm-5 {
+ -ms-flex-order: 5;
+ order: 5;
+ }
+ .order-sm-6 {
+ -ms-flex-order: 6;
+ order: 6;
+ }
+ .order-sm-7 {
+ -ms-flex-order: 7;
+ order: 7;
+ }
+ .order-sm-8 {
+ -ms-flex-order: 8;
+ order: 8;
+ }
+ .order-sm-9 {
+ -ms-flex-order: 9;
+ order: 9;
+ }
+ .order-sm-10 {
+ -ms-flex-order: 10;
+ order: 10;
+ }
+ .order-sm-11 {
+ -ms-flex-order: 11;
+ order: 11;
+ }
+ .order-sm-12 {
+ -ms-flex-order: 12;
+ order: 12;
+ }
+ .offset-sm-0 {
+ margin-left: 0;
+ }
+ .offset-sm-1 {
+ margin-left: 8.333333%;
+ }
+ .offset-sm-2 {
+ margin-left: 16.666667%;
+ }
+ .offset-sm-3 {
+ margin-left: 25%;
+ }
+ .offset-sm-4 {
+ margin-left: 33.333333%;
+ }
+ .offset-sm-5 {
+ margin-left: 41.666667%;
+ }
+ .offset-sm-6 {
+ margin-left: 50%;
+ }
+ .offset-sm-7 {
+ margin-left: 58.333333%;
+ }
+ .offset-sm-8 {
+ margin-left: 66.666667%;
+ }
+ .offset-sm-9 {
+ margin-left: 75%;
+ }
+ .offset-sm-10 {
+ margin-left: 83.333333%;
+ }
+ .offset-sm-11 {
+ margin-left: 91.666667%;
+ }
+}
+
+@media (min-width: 768px) {
+ .col-md {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+ .col-md-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+ }
+ .col-md-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+ }
+ .col-md-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+ .col-md-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+ .col-md-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+ .col-md-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+ }
+ .col-md-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+ .col-md-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+ }
+ .col-md-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+ }
+ .col-md-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+ .col-md-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+ }
+ .col-md-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+ }
+ .col-md-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+ .order-md-first {
+ -ms-flex-order: -1;
+ order: -1;
+ }
+ .order-md-last {
+ -ms-flex-order: 13;
+ order: 13;
+ }
+ .order-md-0 {
+ -ms-flex-order: 0;
+ order: 0;
+ }
+ .order-md-1 {
+ -ms-flex-order: 1;
+ order: 1;
+ }
+ .order-md-2 {
+ -ms-flex-order: 2;
+ order: 2;
+ }
+ .order-md-3 {
+ -ms-flex-order: 3;
+ order: 3;
+ }
+ .order-md-4 {
+ -ms-flex-order: 4;
+ order: 4;
+ }
+ .order-md-5 {
+ -ms-flex-order: 5;
+ order: 5;
+ }
+ .order-md-6 {
+ -ms-flex-order: 6;
+ order: 6;
+ }
+ .order-md-7 {
+ -ms-flex-order: 7;
+ order: 7;
+ }
+ .order-md-8 {
+ -ms-flex-order: 8;
+ order: 8;
+ }
+ .order-md-9 {
+ -ms-flex-order: 9;
+ order: 9;
+ }
+ .order-md-10 {
+ -ms-flex-order: 10;
+ order: 10;
+ }
+ .order-md-11 {
+ -ms-flex-order: 11;
+ order: 11;
+ }
+ .order-md-12 {
+ -ms-flex-order: 12;
+ order: 12;
+ }
+ .offset-md-0 {
+ margin-left: 0;
+ }
+ .offset-md-1 {
+ margin-left: 8.333333%;
+ }
+ .offset-md-2 {
+ margin-left: 16.666667%;
+ }
+ .offset-md-3 {
+ margin-left: 25%;
+ }
+ .offset-md-4 {
+ margin-left: 33.333333%;
+ }
+ .offset-md-5 {
+ margin-left: 41.666667%;
+ }
+ .offset-md-6 {
+ margin-left: 50%;
+ }
+ .offset-md-7 {
+ margin-left: 58.333333%;
+ }
+ .offset-md-8 {
+ margin-left: 66.666667%;
+ }
+ .offset-md-9 {
+ margin-left: 75%;
+ }
+ .offset-md-10 {
+ margin-left: 83.333333%;
+ }
+ .offset-md-11 {
+ margin-left: 91.666667%;
+ }
+}
+
+@media (min-width: 992px) {
+ .col-lg {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+ .col-lg-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+ }
+ .col-lg-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+ }
+ .col-lg-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+ .col-lg-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+ .col-lg-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+ .col-lg-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+ }
+ .col-lg-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+ .col-lg-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+ }
+ .col-lg-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+ }
+ .col-lg-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+ .col-lg-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+ }
+ .col-lg-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+ }
+ .col-lg-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+ .order-lg-first {
+ -ms-flex-order: -1;
+ order: -1;
+ }
+ .order-lg-last {
+ -ms-flex-order: 13;
+ order: 13;
+ }
+ .order-lg-0 {
+ -ms-flex-order: 0;
+ order: 0;
+ }
+ .order-lg-1 {
+ -ms-flex-order: 1;
+ order: 1;
+ }
+ .order-lg-2 {
+ -ms-flex-order: 2;
+ order: 2;
+ }
+ .order-lg-3 {
+ -ms-flex-order: 3;
+ order: 3;
+ }
+ .order-lg-4 {
+ -ms-flex-order: 4;
+ order: 4;
+ }
+ .order-lg-5 {
+ -ms-flex-order: 5;
+ order: 5;
+ }
+ .order-lg-6 {
+ -ms-flex-order: 6;
+ order: 6;
+ }
+ .order-lg-7 {
+ -ms-flex-order: 7;
+ order: 7;
+ }
+ .order-lg-8 {
+ -ms-flex-order: 8;
+ order: 8;
+ }
+ .order-lg-9 {
+ -ms-flex-order: 9;
+ order: 9;
+ }
+ .order-lg-10 {
+ -ms-flex-order: 10;
+ order: 10;
+ }
+ .order-lg-11 {
+ -ms-flex-order: 11;
+ order: 11;
+ }
+ .order-lg-12 {
+ -ms-flex-order: 12;
+ order: 12;
+ }
+ .offset-lg-0 {
+ margin-left: 0;
+ }
+ .offset-lg-1 {
+ margin-left: 8.333333%;
+ }
+ .offset-lg-2 {
+ margin-left: 16.666667%;
+ }
+ .offset-lg-3 {
+ margin-left: 25%;
+ }
+ .offset-lg-4 {
+ margin-left: 33.333333%;
+ }
+ .offset-lg-5 {
+ margin-left: 41.666667%;
+ }
+ .offset-lg-6 {
+ margin-left: 50%;
+ }
+ .offset-lg-7 {
+ margin-left: 58.333333%;
+ }
+ .offset-lg-8 {
+ margin-left: 66.666667%;
+ }
+ .offset-lg-9 {
+ margin-left: 75%;
+ }
+ .offset-lg-10 {
+ margin-left: 83.333333%;
+ }
+ .offset-lg-11 {
+ margin-left: 91.666667%;
+ }
+}
+
+@media (min-width: 1200px) {
+ .col-xl {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+ .col-xl-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+ }
+ .col-xl-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+ }
+ .col-xl-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+ .col-xl-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+ .col-xl-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+ .col-xl-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+ }
+ .col-xl-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+ .col-xl-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+ }
+ .col-xl-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+ }
+ .col-xl-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+ .col-xl-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+ }
+ .col-xl-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+ }
+ .col-xl-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+ .order-xl-first {
+ -ms-flex-order: -1;
+ order: -1;
+ }
+ .order-xl-last {
+ -ms-flex-order: 13;
+ order: 13;
+ }
+ .order-xl-0 {
+ -ms-flex-order: 0;
+ order: 0;
+ }
+ .order-xl-1 {
+ -ms-flex-order: 1;
+ order: 1;
+ }
+ .order-xl-2 {
+ -ms-flex-order: 2;
+ order: 2;
+ }
+ .order-xl-3 {
+ -ms-flex-order: 3;
+ order: 3;
+ }
+ .order-xl-4 {
+ -ms-flex-order: 4;
+ order: 4;
+ }
+ .order-xl-5 {
+ -ms-flex-order: 5;
+ order: 5;
+ }
+ .order-xl-6 {
+ -ms-flex-order: 6;
+ order: 6;
+ }
+ .order-xl-7 {
+ -ms-flex-order: 7;
+ order: 7;
+ }
+ .order-xl-8 {
+ -ms-flex-order: 8;
+ order: 8;
+ }
+ .order-xl-9 {
+ -ms-flex-order: 9;
+ order: 9;
+ }
+ .order-xl-10 {
+ -ms-flex-order: 10;
+ order: 10;
+ }
+ .order-xl-11 {
+ -ms-flex-order: 11;
+ order: 11;
+ }
+ .order-xl-12 {
+ -ms-flex-order: 12;
+ order: 12;
+ }
+ .offset-xl-0 {
+ margin-left: 0;
+ }
+ .offset-xl-1 {
+ margin-left: 8.333333%;
+ }
+ .offset-xl-2 {
+ margin-left: 16.666667%;
+ }
+ .offset-xl-3 {
+ margin-left: 25%;
+ }
+ .offset-xl-4 {
+ margin-left: 33.333333%;
+ }
+ .offset-xl-5 {
+ margin-left: 41.666667%;
+ }
+ .offset-xl-6 {
+ margin-left: 50%;
+ }
+ .offset-xl-7 {
+ margin-left: 58.333333%;
+ }
+ .offset-xl-8 {
+ margin-left: 66.666667%;
+ }
+ .offset-xl-9 {
+ margin-left: 75%;
+ }
+ .offset-xl-10 {
+ margin-left: 83.333333%;
+ }
+ .offset-xl-11 {
+ margin-left: 91.666667%;
+ }
+}
+
+.d-none {
+ display: none !important;
+}
+
+.d-inline {
+ display: inline !important;
+}
+
+.d-inline-block {
+ display: inline-block !important;
+}
+
+.d-block {
+ display: block !important;
+}
+
+.d-table {
+ display: table !important;
+}
+
+.d-table-row {
+ display: table-row !important;
+}
+
+.d-table-cell {
+ display: table-cell !important;
+}
+
+.d-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+}
+
+.d-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+}
+
+@media (min-width: 576px) {
+ .d-sm-none {
+ display: none !important;
+ }
+ .d-sm-inline {
+ display: inline !important;
+ }
+ .d-sm-inline-block {
+ display: inline-block !important;
+ }
+ .d-sm-block {
+ display: block !important;
+ }
+ .d-sm-table {
+ display: table !important;
+ }
+ .d-sm-table-row {
+ display: table-row !important;
+ }
+ .d-sm-table-cell {
+ display: table-cell !important;
+ }
+ .d-sm-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+ .d-sm-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .d-md-none {
+ display: none !important;
+ }
+ .d-md-inline {
+ display: inline !important;
+ }
+ .d-md-inline-block {
+ display: inline-block !important;
+ }
+ .d-md-block {
+ display: block !important;
+ }
+ .d-md-table {
+ display: table !important;
+ }
+ .d-md-table-row {
+ display: table-row !important;
+ }
+ .d-md-table-cell {
+ display: table-cell !important;
+ }
+ .d-md-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+ .d-md-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+@media (min-width: 992px) {
+ .d-lg-none {
+ display: none !important;
+ }
+ .d-lg-inline {
+ display: inline !important;
+ }
+ .d-lg-inline-block {
+ display: inline-block !important;
+ }
+ .d-lg-block {
+ display: block !important;
+ }
+ .d-lg-table {
+ display: table !important;
+ }
+ .d-lg-table-row {
+ display: table-row !important;
+ }
+ .d-lg-table-cell {
+ display: table-cell !important;
+ }
+ .d-lg-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+ .d-lg-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .d-xl-none {
+ display: none !important;
+ }
+ .d-xl-inline {
+ display: inline !important;
+ }
+ .d-xl-inline-block {
+ display: inline-block !important;
+ }
+ .d-xl-block {
+ display: block !important;
+ }
+ .d-xl-table {
+ display: table !important;
+ }
+ .d-xl-table-row {
+ display: table-row !important;
+ }
+ .d-xl-table-cell {
+ display: table-cell !important;
+ }
+ .d-xl-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+ .d-xl-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+@media print {
+ .d-print-none {
+ display: none !important;
+ }
+ .d-print-inline {
+ display: inline !important;
+ }
+ .d-print-inline-block {
+ display: inline-block !important;
+ }
+ .d-print-block {
+ display: block !important;
+ }
+ .d-print-table {
+ display: table !important;
+ }
+ .d-print-table-row {
+ display: table-row !important;
+ }
+ .d-print-table-cell {
+ display: table-cell !important;
+ }
+ .d-print-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+ .d-print-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+.flex-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+}
+
+.flex-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+}
+
+.flex-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+}
+
+.flex-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+}
+
+.flex-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+}
+
+.flex-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+}
+
+.flex-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+}
+
+.flex-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+}
+
+.flex-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+}
+
+.flex-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+}
+
+.flex-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+}
+
+.flex-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+}
+
+.justify-content-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+}
+
+.justify-content-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+}
+
+.justify-content-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+}
+
+.justify-content-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+}
+
+.justify-content-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+}
+
+.align-items-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+}
+
+.align-items-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+}
+
+.align-items-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+}
+
+.align-items-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+}
+
+.align-items-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+}
+
+.align-content-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+}
+
+.align-content-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+}
+
+.align-content-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+}
+
+.align-content-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+}
+
+.align-content-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+}
+
+.align-content-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+}
+
+.align-self-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+}
+
+.align-self-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+}
+
+.align-self-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+}
+
+.align-self-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+}
+
+.align-self-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+}
+
+.align-self-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+}
+
+@media (min-width: 576px) {
+ .flex-sm-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+ }
+ .flex-sm-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+ }
+ .flex-sm-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+ }
+ .flex-sm-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+ }
+ .flex-sm-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+ }
+ .flex-sm-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+ }
+ .flex-sm-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+ }
+ .flex-sm-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+ }
+ .flex-sm-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+ }
+ .flex-sm-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+ }
+ .flex-sm-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+ }
+ .flex-sm-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+ }
+ .justify-content-sm-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+ }
+ .justify-content-sm-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+ }
+ .justify-content-sm-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+ }
+ .justify-content-sm-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+ }
+ .justify-content-sm-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+ }
+ .align-items-sm-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+ }
+ .align-items-sm-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+ }
+ .align-items-sm-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+ }
+ .align-items-sm-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+ }
+ .align-items-sm-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+ }
+ .align-content-sm-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+ }
+ .align-content-sm-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+ }
+ .align-content-sm-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+ }
+ .align-content-sm-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+ }
+ .align-content-sm-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+ }
+ .align-content-sm-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+ }
+ .align-self-sm-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+ }
+ .align-self-sm-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+ }
+ .align-self-sm-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+ }
+ .align-self-sm-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+ }
+ .align-self-sm-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+ }
+ .align-self-sm-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .flex-md-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+ }
+ .flex-md-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+ }
+ .flex-md-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+ }
+ .flex-md-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+ }
+ .flex-md-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+ }
+ .flex-md-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+ }
+ .flex-md-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+ }
+ .flex-md-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+ }
+ .flex-md-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+ }
+ .flex-md-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+ }
+ .flex-md-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+ }
+ .flex-md-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+ }
+ .justify-content-md-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+ }
+ .justify-content-md-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+ }
+ .justify-content-md-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+ }
+ .justify-content-md-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+ }
+ .justify-content-md-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+ }
+ .align-items-md-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+ }
+ .align-items-md-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+ }
+ .align-items-md-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+ }
+ .align-items-md-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+ }
+ .align-items-md-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+ }
+ .align-content-md-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+ }
+ .align-content-md-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+ }
+ .align-content-md-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+ }
+ .align-content-md-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+ }
+ .align-content-md-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+ }
+ .align-content-md-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+ }
+ .align-self-md-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+ }
+ .align-self-md-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+ }
+ .align-self-md-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+ }
+ .align-self-md-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+ }
+ .align-self-md-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+ }
+ .align-self-md-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+ }
+}
+
+@media (min-width: 992px) {
+ .flex-lg-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+ }
+ .flex-lg-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+ }
+ .flex-lg-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+ }
+ .flex-lg-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+ }
+ .flex-lg-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+ }
+ .flex-lg-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+ }
+ .flex-lg-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+ }
+ .flex-lg-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+ }
+ .flex-lg-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+ }
+ .flex-lg-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+ }
+ .flex-lg-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+ }
+ .flex-lg-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+ }
+ .justify-content-lg-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+ }
+ .justify-content-lg-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+ }
+ .justify-content-lg-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+ }
+ .justify-content-lg-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+ }
+ .justify-content-lg-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+ }
+ .align-items-lg-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+ }
+ .align-items-lg-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+ }
+ .align-items-lg-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+ }
+ .align-items-lg-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+ }
+ .align-items-lg-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+ }
+ .align-content-lg-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+ }
+ .align-content-lg-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+ }
+ .align-content-lg-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+ }
+ .align-content-lg-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+ }
+ .align-content-lg-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+ }
+ .align-content-lg-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+ }
+ .align-self-lg-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+ }
+ .align-self-lg-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+ }
+ .align-self-lg-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+ }
+ .align-self-lg-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+ }
+ .align-self-lg-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+ }
+ .align-self-lg-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .flex-xl-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+ }
+ .flex-xl-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+ }
+ .flex-xl-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+ }
+ .flex-xl-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+ }
+ .flex-xl-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+ }
+ .flex-xl-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+ }
+ .flex-xl-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+ }
+ .flex-xl-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+ }
+ .flex-xl-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+ }
+ .flex-xl-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+ }
+ .flex-xl-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+ }
+ .flex-xl-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+ }
+ .justify-content-xl-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+ }
+ .justify-content-xl-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+ }
+ .justify-content-xl-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+ }
+ .justify-content-xl-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+ }
+ .justify-content-xl-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+ }
+ .align-items-xl-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+ }
+ .align-items-xl-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+ }
+ .align-items-xl-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+ }
+ .align-items-xl-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+ }
+ .align-items-xl-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+ }
+ .align-content-xl-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+ }
+ .align-content-xl-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+ }
+ .align-content-xl-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+ }
+ .align-content-xl-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+ }
+ .align-content-xl-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+ }
+ .align-content-xl-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+ }
+ .align-self-xl-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+ }
+ .align-self-xl-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+ }
+ .align-self-xl-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+ }
+ .align-self-xl-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+ }
+ .align-self-xl-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+ }
+ .align-self-xl-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+ }
+}
diff --git a/src/styles/vendor/normalize.scss b/src/styles/vendor/normalize.scss
new file mode 100644
index 0000000000..192eb9ce43
--- /dev/null
+++ b/src/styles/vendor/normalize.scss
@@ -0,0 +1,349 @@
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
+
+/* Document
+ ========================================================================== */
+
+/**
+ * 1. Correct the line height in all browsers.
+ * 2. Prevent adjustments of font size after orientation changes in iOS.
+ */
+
+html {
+ line-height: 1.15; /* 1 */
+ -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/* Sections
+ ========================================================================== */
+
+/**
+ * Remove the margin in all browsers.
+ */
+
+body {
+ margin: 0;
+}
+
+/**
+ * Render the `main` element consistently in IE.
+ */
+
+main {
+ display: block;
+}
+
+/**
+ * Correct the font size and margin on `h1` elements within `section` and
+ * `article` contexts in Chrome, Firefox, and Safari.
+ */
+
+h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
+}
+
+/* Grouping content
+ ========================================================================== */
+
+/**
+ * 1. Add the correct box sizing in Firefox.
+ * 2. Show the overflow in Edge and IE.
+ */
+
+hr {
+ box-sizing: content-box; /* 1 */
+ height: 0; /* 1 */
+ overflow: visible; /* 2 */
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+pre {
+ font-family: monospace, monospace; /* 1 */
+ font-size: 1em; /* 2 */
+}
+
+/* Text-level semantics
+ ========================================================================== */
+
+/**
+ * Remove the gray background on active links in IE 10.
+ */
+
+a {
+ background-color: transparent;
+}
+
+/**
+ * 1. Remove the bottom border in Chrome 57-
+ * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+ */
+
+abbr[title] {
+ border-bottom: none; /* 1 */
+ text-decoration: underline; /* 2 */
+ text-decoration: underline dotted; /* 2 */
+}
+
+/**
+ * Add the correct font weight in Chrome, Edge, and Safari.
+ */
+
+b,
+strong {
+ font-weight: bolder;
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+code,
+kbd,
+samp {
+ font-family: monospace, monospace; /* 1 */
+ font-size: 1em; /* 2 */
+}
+
+/**
+ * Add the correct font size in all browsers.
+ */
+
+small {
+ font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` elements from affecting the line height in
+ * all browsers.
+ */
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+sup {
+ top: -0.5em;
+}
+
+/* Embedded content
+ ========================================================================== */
+
+/**
+ * Remove the border on images inside links in IE 10.
+ */
+
+img {
+ border-style: none;
+}
+
+/* Forms
+ ========================================================================== */
+
+/**
+ * 1. Change the font styles in all browsers.
+ * 2. Remove the margin in Firefox and Safari.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+ font-family: inherit; /* 1 */
+ font-size: 100%; /* 1 */
+ line-height: 1.15; /* 1 */
+ margin: 0; /* 2 */
+}
+
+/**
+ * Show the overflow in IE.
+ * 1. Show the overflow in Edge.
+ */
+
+button,
+input { /* 1 */
+ overflow: visible;
+}
+
+/**
+ * Remove the inheritance of text transform in Edge, Firefox, and IE.
+ * 1. Remove the inheritance of text transform in Firefox.
+ */
+
+button,
+select { /* 1 */
+ text-transform: none;
+}
+
+/**
+ * Correct the inability to style clickable types in iOS and Safari.
+ */
+
+button,
+[type="button"],
+[type="reset"],
+[type="submit"] {
+ -webkit-appearance: button;
+}
+
+/**
+ * Remove the inner border and padding in Firefox.
+ */
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+ border-style: none;
+ padding: 0;
+}
+
+/**
+ * Restore the focus styles unset by the previous rule.
+ */
+
+button:-moz-focusring,
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring {
+ outline: 1px dotted ButtonText;
+}
+
+/**
+ * Correct the padding in Firefox.
+ */
+
+fieldset {
+ padding: 0.35em 0.75em 0.625em;
+}
+
+/**
+ * 1. Correct the text wrapping in Edge and IE.
+ * 2. Correct the color inheritance from `fieldset` elements in IE.
+ * 3. Remove the padding so developers are not caught out when they zero out
+ * `fieldset` elements in all browsers.
+ */
+
+legend {
+ box-sizing: border-box; /* 1 */
+ color: inherit; /* 2 */
+ display: table; /* 1 */
+ max-width: 100%; /* 1 */
+ padding: 0; /* 3 */
+ white-space: normal; /* 1 */
+}
+
+/**
+ * Add the correct vertical alignment in Chrome, Firefox, and Opera.
+ */
+
+progress {
+ vertical-align: baseline;
+}
+
+/**
+ * Remove the default vertical scrollbar in IE 10+.
+ */
+
+textarea {
+ overflow: auto;
+}
+
+/**
+ * 1. Add the correct box sizing in IE 10.
+ * 2. Remove the padding in IE 10.
+ */
+
+[type="checkbox"],
+[type="radio"] {
+ box-sizing: border-box; /* 1 */
+ padding: 0; /* 2 */
+}
+
+/**
+ * Correct the cursor style of increment and decrement buttons in Chrome.
+ */
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/**
+ * 1. Correct the odd appearance in Chrome and Safari.
+ * 2. Correct the outline style in Safari.
+ */
+
+[type="search"] {
+ -webkit-appearance: textfield; /* 1 */
+ outline-offset: -2px; /* 2 */
+}
+
+/**
+ * Remove the inner padding in Chrome and Safari on macOS.
+ */
+
+[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/**
+ * 1. Correct the inability to style clickable types in iOS and Safari.
+ * 2. Change font properties to `inherit` in Safari.
+ */
+
+::-webkit-file-upload-button {
+ -webkit-appearance: button; /* 1 */
+ font: inherit; /* 2 */
+}
+
+/* Interactive
+ ========================================================================== */
+
+/*
+ * Add the correct display in Edge, IE 10+, and Firefox.
+ */
+
+details {
+ display: block;
+}
+
+/*
+ * Add the correct display in all browsers.
+ */
+
+summary {
+ display: list-item;
+}
+
+/* Misc
+ ========================================================================== */
+
+/**
+ * Add the correct display in IE 10+.
+ */
+
+template {
+ display: none;
+}
+
+/**
+ * Add the correct display in IE 10.
+ */
+
+[hidden] {
+ display: none;
+}
diff --git a/src/svg/arrow.inline.svg b/src/svg/arrow.inline.svg
new file mode 100644
index 0000000000..259bada5c1
--- /dev/null
+++ b/src/svg/arrow.inline.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/svg/azure.inline.svg b/src/svg/azure.inline.svg
new file mode 100644
index 0000000000..769d9b65f5
--- /dev/null
+++ b/src/svg/azure.inline.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/svg/check.inline.svg b/src/svg/check.inline.svg
new file mode 100644
index 0000000000..8c02ea41b1
--- /dev/null
+++ b/src/svg/check.inline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/svg/circleci.inline.svg b/src/svg/circleci.inline.svg
new file mode 100644
index 0000000000..4865348cd3
--- /dev/null
+++ b/src/svg/circleci.inline.svg
@@ -0,0 +1,52 @@
+
+
+
+image/svg+xml
\ No newline at end of file
diff --git a/src/svg/corner.inline.svg b/src/svg/corner.inline.svg
new file mode 100644
index 0000000000..84051546af
--- /dev/null
+++ b/src/svg/corner.inline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/svg/datadog.inline.svg b/src/svg/datadog.inline.svg
new file mode 100644
index 0000000000..3483728c3e
--- /dev/null
+++ b/src/svg/datadog.inline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/svg/github.inline.svg b/src/svg/github.inline.svg
new file mode 100644
index 0000000000..14481a7d8a
--- /dev/null
+++ b/src/svg/github.inline.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/svg/gitlab.inline.svg b/src/svg/gitlab.inline.svg
new file mode 100644
index 0000000000..1cea71dc29
--- /dev/null
+++ b/src/svg/gitlab.inline.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/svg/grafana.inline.svg b/src/svg/grafana.inline.svg
new file mode 100644
index 0000000000..67118dd5ce
--- /dev/null
+++ b/src/svg/grafana.inline.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/svg/icon-algolia.inline.svg b/src/svg/icon-algolia.inline.svg
new file mode 100644
index 0000000000..ae8cae4a28
--- /dev/null
+++ b/src/svg/icon-algolia.inline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/svg/influx.inline.svg b/src/svg/influx.inline.svg
new file mode 100644
index 0000000000..89fd1fac03
--- /dev/null
+++ b/src/svg/influx.inline.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/svg/jenkins.inline.svg b/src/svg/jenkins.inline.svg
new file mode 100644
index 0000000000..4fa3abf56d
--- /dev/null
+++ b/src/svg/jenkins.inline.svg
@@ -0,0 +1,282 @@
+
+
+
+image/svg+xml
\ No newline at end of file
diff --git a/src/svg/json.inline.svg b/src/svg/json.inline.svg
new file mode 100644
index 0000000000..3dc9509df2
--- /dev/null
+++ b/src/svg/json.inline.svg
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/svg/kafka.inline.svg b/src/svg/kafka.inline.svg
new file mode 100644
index 0000000000..16aee60acb
--- /dev/null
+++ b/src/svg/kafka.inline.svg
@@ -0,0 +1 @@
+
diff --git a/src/svg/loadimpact.inline.svg b/src/svg/loadimpact.inline.svg
new file mode 100644
index 0000000000..76dc1c1a09
--- /dev/null
+++ b/src/svg/loadimpact.inline.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/svg/logo.inline.svg b/src/svg/logo.inline.svg
new file mode 100644
index 0000000000..b18284f72e
--- /dev/null
+++ b/src/svg/logo.inline.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/svg/teamcity.inline.svg b/src/svg/teamcity.inline.svg
new file mode 100644
index 0000000000..b526b0f654
--- /dev/null
+++ b/src/svg/teamcity.inline.svg
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/svg/twitter.inline.svg b/src/svg/twitter.inline.svg
new file mode 100644
index 0000000000..8644ced03c
--- /dev/null
+++ b/src/svg/twitter.inline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/templates/doc-page.js b/src/templates/doc-page.js
new file mode 100644
index 0000000000..f4ed5f9e2a
--- /dev/null
+++ b/src/templates/doc-page.js
@@ -0,0 +1,47 @@
+import React from 'react';
+import { DocLayout } from 'layouts/doc-layout';
+import { Breadcrumbs } from 'components/templates/doc-page/breadcrumbs';
+import styles from 'components/templates/doc-page/doc-page.module.scss';
+import { styles as codeStyles } from 'components/shared/code';
+import { DocPageContent } from 'components/templates/doc-page/doc-page-content';
+import { DocPageTitleGroup } from 'components/pages/doc-page/doc-page-title-group';
+
+export default function (props) {
+ const {
+ pageContext: {
+ remarkNode: { html, frontmatter },
+ sidebarTree,
+ navLinks,
+ breadcrumbs,
+ },
+ location,
+ } = props;
+
+ const pageMetadata = {
+ data: {
+ title: frontmatter.title,
+ description: frontmatter.excerpt,
+ slug: frontmatter.slug,
+ },
+ };
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/templates/docs/breadcrumb-stub.js b/src/templates/docs/breadcrumb-stub.js
new file mode 100644
index 0000000000..b36736b443
--- /dev/null
+++ b/src/templates/docs/breadcrumb-stub.js
@@ -0,0 +1,39 @@
+import React from 'react';
+import { Link } from 'gatsby';
+import { DocLayout } from 'layouts/doc-layout';
+import { Breadcrumbs } from 'components/templates/doc-page/breadcrumbs';
+import styles from 'components/templates/doc-page/doc-page.module.scss';
+import { Heading } from 'components/shared/heading';
+import { childrenToList, slugify } from 'utils';
+
+export default function(props) {
+ const {
+ pageContext: { sidebarTree, breadcrumbs, navLinks, title, directChildren },
+ } = props;
+ return (
+
+
+
+
{title}
+
+ {childrenToList(directChildren).map(({ meta, name }, i) => (
+
+ {meta.redirect ? (
+
+ {name}
+
+ ) : (
+
+ {name}
+
+ )}
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/templates/docs/cloud.js b/src/templates/docs/cloud.js
new file mode 100644
index 0000000000..981a9a4ec5
--- /dev/null
+++ b/src/templates/docs/cloud.js
@@ -0,0 +1,222 @@
+import React, { useState, useEffect } from 'react';
+import { Link } from 'gatsby';
+import { Cloud, PageInfo } from 'components/pages/doc-welcome';
+import { DocLayout } from 'layouts/doc-layout';
+import { Trait } from 'components/shared/trait';
+import htmlStyles from 'components/blocks/html-content/html-content.module.scss';
+import { StickyContainer, Sticky } from 'react-sticky';
+import { whenElementAvailable, isInIFrame } from 'utils';
+import { default as docPageContent } from 'components/templates/doc-page/doc-page-content/doc-page-content.module.scss';
+import { default as docPageNav } from 'components/pages/doc-page/doc-page-nav/doc-page-nav.module.scss';
+import SeoMetadata from 'utils/seo-metadata';
+import { app } from 'utils/urls';
+import { useLandmark } from 'hooks';
+
+export default function({ pageContext: { sidebarTree, navLinks } }) {
+ const pageMetadata = SeoMetadata.cloud;
+
+ const [showFooter, setShowFooter] = useState(true);
+
+ const { links } = useLandmark({
+ selector: docPageContent.inner,
+ });
+ useEffect(() => setShowFooter(!isInIFrame()), []);
+
+ useEffect(() => {
+ // check if given url contains hash (therefore an anchor)
+ const scrollMark = location.hash;
+ if (scrollMark) {
+ // wait when html content adds all id to h2 then scroll to it
+ whenElementAvailable(scrollMark)(el =>
+ // no smooth scroll needed
+ window.scrollTo({
+ top: el.getBoundingClientRect().top + window.scrollY - 25,
+ }),
+ );
+ }
+ }, []);
+
+ const handleAnchorClick = (e, anchor) => {
+ e.preventDefault();
+ document.querySelector(anchor).scrollIntoView({
+ behavior: 'smooth',
+ block: 'start',
+ });
+ // changing hash without default jumps to anchor
+ if (history.pushState) {
+ history.pushState(false, false, anchor);
+ } else {
+ // old browser support
+ window.location.hash = anchor;
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
What is the k6 Cloud?
+
+ The k6 Cloud is a commercial SaaS product that we’ve designed
+ to be the perfect companion to k6 OSS. It brings ease-of-use
+ and convenience to your performance and load testing.
+
+
+ This knowledge base will help you learn how to use the
+ features and functionality of the k6 Cloud:
+
+
+ Running cloud tests.
+ Analyzing results.
+ Managing projects, teams and users.
+ Integrations.
+ FAQ.
+
+
How can it help me?
+
+ The k6 Cloud is a premium service to empower your team to
+ manage your load testing efforts:
+
+
+ Scaling your load tests.
+ Storing and visualizing your test results.
+ Detecting performance issues.
+ Correlating results between different tests.
+ Organizing testing in a central location.
+
+
+ We want you to spend time building and maintaining
+ well-performing applications. Don’t saddle your team with the
+ additional maintenance burden of your load testing
+ infrastructure.
+
+
Cloud Features
+
+
+
+ Run larger tests from multiple geographic locations.
+
+
+
+ Test Builder
+ {' '}
+ and{' '}
+
+ Script Editor
+
+ .
+
+
+ CLI and{' '}
+
+ Browser Session Recorder
+
+ .
+
+
+
+ Scheduling
+ {' '}
+ and{' '}
+
+ Notifications
+
+ .
+
+
+
+
+
+
+ Performance Insights
+ {' '}
+ and{' '}
+
+ Premium test result visualization
+
+ .
+
+
+
+ Compare tests{' '}
+ {' '}
+ and{' '}
+
+ performance trends
+
+ .
+
+
+
+ Export results
+
+ .
+
+
+
+ Team members
+ {' '}
+ and{' '}
+
+ Projects
+
+ .
+
+
+
+ Create notes and share results
+
+ .
+
+
+
+
+
+ {showFooter && (
+
+ )}
+
+
+
+ {({ style }) => (
+
+ )}
+
+
+
+
+ );
+}
diff --git a/src/templates/docs/examples.js b/src/templates/docs/examples.js
new file mode 100644
index 0000000000..59d1a61100
--- /dev/null
+++ b/src/templates/docs/examples.js
@@ -0,0 +1,123 @@
+import React, { useState, useEffect } from 'react';
+import { StickyContainer, Sticky } from 'react-sticky';
+import classNames from 'classnames';
+import { graphql, useStaticQuery } from 'gatsby';
+import { DocLayout } from 'layouts/doc-layout';
+import { PageInfo } from 'components/pages/doc-welcome/page-info';
+import { DocLinksBlock } from 'components/pages/doc-examples/doc-links-block';
+import { whenElementAvailable } from 'utils';
+import { default as docPageContent } from 'components/templates/doc-page/doc-page-content/doc-page-content.module.scss';
+import { default as docPageNav } from 'components/pages/doc-page/doc-page-nav/doc-page-nav.module.scss';
+import SeoMetadata from 'utils/seo-metadata';
+import { useLandmark } from 'hooks';
+
+export default function ({ pageContext: { sidebarTree, navLinks } }) {
+ const pageMetadata = SeoMetadata.examples;
+
+ const { links } = useLandmark({
+ selector: docPageContent.inner,
+ });
+
+ useEffect(() => {
+ // check if given url contains hash (therefore an anchor)
+ const scrollMark = location.hash;
+ if (scrollMark) {
+ // wait when html content adds all id to h2 then scroll to it
+ whenElementAvailable(scrollMark)(el =>
+ // no smooth scroll needed
+ window.scrollTo({
+ top: el.getBoundingClientRect().top + window.scrollY - 25,
+ }),
+ );
+ }
+ }, []);
+
+ const handleAnchorClick = (e, anchor) => {
+ e.preventDefault();
+ document.querySelector(anchor).scrollIntoView({
+ behavior: 'smooth',
+ block: 'start',
+ });
+ // changing hash without default jumps to anchor
+ if (history.pushState) {
+ history.pushState(false, false, anchor);
+ } else {
+ // old browser support
+ window.location.hash = anchor;
+ }
+ };
+ const {
+ docExamplesJson: { tutorialsBlockLinks, examplesBlockLinks },
+ } = useStaticQuery(graphql`
+ query blockLinksData {
+ docExamplesJson {
+ tutorialsBlockLinks: tutorials {
+ title
+ description
+ url
+ to
+ }
+ examplesBlockLinks: examples {
+ title
+ description
+ url
+ to
+ }
+ }
+ }
+ `);
+ return (
+
+
+
+
+
+
+
+
+
+ {({ style }) => (
+
+ )}
+
+
+
+
+ );
+}
diff --git a/src/templates/docs/guides.js b/src/templates/docs/guides.js
new file mode 100644
index 0000000000..f5efb4fda5
--- /dev/null
+++ b/src/templates/docs/guides.js
@@ -0,0 +1,122 @@
+import React, { useEffect, useState } from 'react';
+// import ReactDOM from 'react-dom';
+import classNames from 'classnames';
+import { DocLayout } from 'layouts/doc-layout';
+import { Sticky, StickyContainer } from 'react-sticky';
+import { whenElementAvailable } from 'utils';
+import docPageNav from 'components/pages/doc-page/doc-page-nav/doc-page-nav.module.scss';
+import docPageContent from 'components/templates/doc-page/doc-page-content/doc-page-content.module.scss';
+import { useLandmark } from 'hooks';
+import {
+ Cloud,
+ Features,
+ PageInfo,
+ Manifesto,
+ Quickstart,
+ WhatIs,
+} from 'components/pages/doc-welcome';
+import { K6DoesNot } from 'components/pages/doc-welcome/k6-does-not';
+import { UseCases } from 'components/pages/doc-welcome/use-cases';
+import SeoMetadata from 'utils/seo-metadata';
+import { docs } from 'utils/urls';
+
+const pageInfo = {
+ title: 'Welcome to the k6 documentation',
+ description:
+ 'This documentation will help you go from a total beginner to a seasoned k6 expert!',
+};
+
+export default function({ pageContext: { sidebarTree, navLinks } }) {
+ const { links } = useLandmark({
+ selector: docPageContent.inner,
+ });
+ const pageMetadata = SeoMetadata.guides;
+
+ useEffect(() => {
+ // check if given url contains hash (therefore an anchor)
+ const scrollMark = location.hash;
+ if (scrollMark) {
+ // wait when html content adds all id to h2 then scroll to it
+ whenElementAvailable(scrollMark)(el =>
+ // no smooth scroll needed
+ window.scrollTo({
+ top: el.getBoundingClientRect().top + window.scrollY - 25,
+ }),
+ );
+ }
+ }, []);
+
+ const handleAnchorClick = (e, anchor) => {
+ e.preventDefault();
+ document.querySelector(anchor).scrollIntoView({
+ behavior: 'smooth',
+ block: 'start',
+ });
+ // changing hash without default jumps to anchor
+ if (history.pushState) {
+ history.pushState(false, false, anchor);
+ } else {
+ // old browser support
+ window.location.hash = anchor;
+ }
+ };
+
+ const stickyContainerClasses = classNames(
+ docPageContent.mainDocContent,
+ docPageContent.contentWrapper,
+ );
+
+ const renderSidebar = ({ style }) => (
+
+ );
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {links && renderSidebar}
+
+
+
+
+ );
+}
diff --git a/src/templates/docs/integrations.js b/src/templates/docs/integrations.js
new file mode 100644
index 0000000000..37cea7c460
--- /dev/null
+++ b/src/templates/docs/integrations.js
@@ -0,0 +1,354 @@
+import React, { useState, useEffect } from 'react';
+import { StickyContainer, Sticky } from 'react-sticky';
+import { useStaticQuery, graphql } from 'gatsby';
+import classNames from 'classnames';
+import { whenElementAvailable } from 'utils';
+// components
+import { DocLayout } from 'layouts/doc-layout';
+import { PageInfo } from 'components/pages/doc-welcome/page-info';
+import { DocIconsRow } from 'components/pages/doc-integrations/doc-icons-row';
+import { ExternalLinksDashboard } from 'components/pages/doc-integrations/external-links-dashboard';
+import CustomContentContainer from 'components/shared/custom-content-container';
+// styles
+import styles from 'components/pages/doc-integrations/doc-integrations.module.scss';
+import { default as docPageContent } from 'components/templates/doc-page/doc-page-content/doc-page-content.module.scss';
+import { default as docPageNav } from 'components/pages/doc-page/doc-page-nav/doc-page-nav.module.scss';
+import { useLandmark } from 'hooks';
+// icons
+import Jenkins from 'svg/jenkins.inline.svg';
+import CircleCI from 'svg/circleci.inline.svg';
+import Gitlab from 'svg/gitlab.inline.svg';
+import GitHub from 'svg/github.inline.svg';
+import Azure from 'svg/azure.inline.svg';
+import TeamCity from 'svg/teamcity.inline.svg';
+import Json from 'svg/json.inline.svg';
+import Grafana from 'svg/grafana.inline.svg';
+import Influx from 'svg/influx.inline.svg';
+import Datadog from 'svg/datadog.inline.svg';
+import Kafka from 'svg/kafka.inline.svg';
+import Loadimpact from 'svg/loadimpact.inline.svg';
+import SeoMetadata from 'utils/seo-metadata';
+import { blog, main } from 'utils/urls';
+
+export default function({ pageContext: { sidebarTree, navLinks } }) {
+ const pageMetadata = SeoMetadata.integrations;
+ const { links } = useLandmark({
+ selector: docPageContent.inner,
+ });
+
+ useEffect(() => {
+ // check if given url contains hash (therefore an anchor)
+ const scrollMark = location.hash;
+ if (scrollMark) {
+ // wait when html content adds all id to h2 then scroll to it
+ whenElementAvailable(scrollMark)(el =>
+ // no smooth scroll needed
+ window.scrollTo({
+ top: el.getBoundingClientRect().top + window.scrollY - 25,
+ }),
+ );
+ }
+ }, []);
+
+ const handleAnchorClick = (e, anchor) => {
+ e.preventDefault();
+ document.querySelector(anchor).scrollIntoView({
+ behavior: 'smooth',
+ block: 'start',
+ });
+ // changing hash without default jumps to anchor
+ if (history.pushState) {
+ history.pushState(false, false, anchor);
+ } else {
+ // old browser support
+ window.location.hash = anchor;
+ }
+ };
+
+ const {
+ graphqlImg: {
+ childImageSharp: { fixed: graphqlImgData },
+ },
+ azureImg: {
+ childImageSharp: { fixed: azureImgData },
+ },
+ harImg: {
+ childImageSharp: { fixed: harImgData },
+ },
+ postmanImg: {
+ childImageSharp: { fixed: postmanImgData },
+ },
+ swaggerImg: {
+ childImageSharp: { fixed: swaggerImgData },
+ },
+ jmeterImg: {
+ childImageSharp: { fixed: jmeterImgData },
+ },
+ } = useStaticQuery(graphql`
+ query stubImageQuery {
+ graphqlImg: file(
+ absolutePath: { regex: "/images/doc-integrations/graphql/" }
+ ) {
+ childImageSharp {
+ fixed(width: 60, height: 60, cropFocus: CENTER) {
+ ...GatsbyImageSharpFixed_withWebp_noBase64
+ }
+ }
+ }
+ azureImg: file(
+ absolutePath: { regex: "/images/doc-integrations/azure/" }
+ ) {
+ childImageSharp {
+ fixed(width: 60, height: 60, cropFocus: CENTER) {
+ ...GatsbyImageSharpFixed_withWebp_noBase64
+ }
+ }
+ }
+ harImg: file(absolutePath: { regex: "/images/doc-integrations/har/" }) {
+ childImageSharp {
+ fixed(width: 60, height: 60, cropFocus: CENTER) {
+ ...GatsbyImageSharpFixed_withWebp_noBase64
+ }
+ }
+ }
+ postmanImg: file(
+ absolutePath: { regex: "/images/doc-integrations/postman/" }
+ ) {
+ childImageSharp {
+ fixed(width: 60, height: 60, cropFocus: CENTER) {
+ ...GatsbyImageSharpFixed_withWebp_noBase64
+ }
+ }
+ }
+ swaggerImg: file(
+ absolutePath: { regex: "/images/doc-integrations/openapi/" }
+ ) {
+ childImageSharp {
+ fixed(width: 60, height: 60, cropFocus: CENTER) {
+ ...GatsbyImageSharpFixed_withWebp_noBase64
+ }
+ }
+ }
+ jmeterImg: file(
+ absolutePath: { regex: "/images/doc-integrations/jmeter/" }
+ ) {
+ childImageSharp {
+ fixed(width: 60, height: 60, cropFocus: CENTER) {
+ ...GatsbyImageSharpFixed_withWebp_noBase64
+ }
+ }
+ }
+ }
+ `);
+ return (
+
+
+
+
+
+
+
+
+
(
+
+
+ +
+
+
+ ),
+ name: 'InfluxDB/Grafana',
+ to: '/results-visualization/influxdb-+-grafana',
+ // handling non-standard sizes
+ col: 2,
+ },
+ {
+ Icon: Kafka,
+ name: 'Apache Kafka',
+ to: '/getting-started/results-output/apache-kafka',
+ },
+ {
+ Icon: Datadog,
+ name: 'DataDog',
+ to: '/getting-started/results-output/datadog',
+ },
+ {
+ Icon: Loadimpact,
+ name: 'k6 Cloud',
+ to: '/getting-started/results-output/cloud',
+ col: 2,
+ },
+ ]}
+ />
+
+
+
+ Please get in touch with us on{' '}
+
+ Slack
+ {' '}
+ if you have a tool or add-on that works well with k6, and we
+ will list it here!
+
+
+
+
+ {({ style }) => (
+
+ )}
+
+
+
+
+ );
+}
diff --git a/src/templates/docs/javascript-api.js b/src/templates/docs/javascript-api.js
new file mode 100644
index 0000000000..cf1cf6df4c
--- /dev/null
+++ b/src/templates/docs/javascript-api.js
@@ -0,0 +1,268 @@
+import React, { useState, useEffect } from 'react';
+import classNames from 'classnames';
+import { graphql, Link } from 'gatsby';
+import { DocLayout } from 'layouts/doc-layout';
+import { PageInfo } from 'components/pages/doc-welcome/page-info';
+import { Sticky, StickyContainer } from 'react-sticky';
+import {
+ slugify,
+ stripDirectoryPath,
+ unorderify,
+ compose,
+ noSlugDuplication,
+ whenElementAvailable,
+} from 'utils';
+import htmlStyles from 'components/blocks/html-content/html-content.module.scss';
+import { default as docPageContent } from 'components/templates/doc-page/doc-page-content/doc-page-content.module.scss';
+import { default as docPageNav } from 'components/pages/doc-page/doc-page-nav/doc-page-nav.module.scss';
+import SeoMetadata from 'utils/seo-metadata';
+import { HtmlContent } from 'components/blocks/html-content';
+import { styles as codeStyles } from 'components/shared/code';
+import CustomContentContainer from 'components/shared/custom-content-container';
+import { CodeGroup } from 'components/templates/doc-page/code-group';
+import { DocBlockquote } from 'components/pages/doc-page/doc-blockquote';
+import jsApiStyles from 'components/pages/doc-javascript-api/doc-javascript-api.module.scss';
+import AnchorIcon from 'components/templates/doc-page/doc-page-content/svg/anchor.inline.svg';
+import docPage from 'templates/doc-page';
+
+// Return an array of { module: , classes: [
...], functions: [ ...] } objects.
+// Top-level documents (modules) as defined by the sidebarTree will be
+// rendered as header+content, and child documents as table rows.
+function buildIndex(nodes, sidebarTree) {
+ const index = [];
+
+ const components = {
+ '.code-group': CodeGroup,
+ h2: ({ mdBlockContent }) => (
+
+
+
+
+ {mdBlockContent}
+
+ ),
+ '.doc-blockquote': DocBlockquote,
+ table: ({ mdBlockContent }) => (
+
+ ),
+ };
+
+ for (let i = 0; i < nodes.length; i += 1) {
+ const node = nodes[i];
+ const md = node.children[0];
+ if (md.frontmatter.title.replace('/', '-') in sidebarTree.children) {
+ index.push({
+ module: (
+
+
{md.frontmatter.title}
+
+
+ ),
+ classes: [],
+ functions: [],
+ });
+ continue;
+ }
+
+ // FIXME: Hack to avoid showing methods in the Functions table.
+ // This needs a more structural fix (exclude by frontmatter field?),
+ // but I wasn't able to get an exclude filter working with allFile or
+ // allMarkdownRemark.
+ if (
+ md.frontmatter.title.match(
+ /(CookieJar|Counter|Gauge|Rate|Response|Selection|Socket|Trend)\./,
+ )
+ ) {
+ continue;
+ }
+
+ const path = `${stripDirectoryPath(
+ node.relativeDirectory,
+ 'docs',
+ )}/${md.frontmatter.title.replace(/\//g, '-')}`;
+ const slug = compose(unorderify, slugify)(path);
+
+ const row = (
+
+
+ {md.frontmatter.title}
+
+ {md.frontmatter.description}
+
+ );
+
+ let nodeType = 'functions';
+ if (md.frontmatter.category === 'k6api-class') {
+ nodeType = 'classes';
+ }
+
+ const last = index[index.length - 1];
+ // checking if here is identical slug already exists in main module
+ // skipping row addition if true
+ if (noSlugDuplication(last, slug)) {
+ last[nodeType].push(row);
+ }
+ }
+
+ return index;
+}
+
+function createTableMaybe(elements, header) {
+ if (elements.length > 0) {
+ return (
+
+
+
+
+ {header}
+ Description
+
+
+ {elements}
+
+
+ );
+ }
+ return null;
+}
+
+export default function({ data, pageContext: { sidebarTree, navLinks } }) {
+ const index = buildIndex(data.allFile.nodes, sidebarTree);
+ const pageMetadata = SeoMetadata['javascript-api'];
+
+ const [pageNavLinks, setPageNavLinks] = useState([]);
+
+ useEffect(() => {
+ const allHeadingMarks = document
+ .querySelector(`.${docPageContent.inner}`)
+ .querySelectorAll('h2');
+ setPageNavLinks(
+ Array.from(allHeadingMarks).map(({ innerHTML }) => ({
+ title: innerHTML,
+ anchor: `#${slugify(innerHTML).replace(/\//g, '-')}`,
+ })),
+ );
+ }, []);
+
+ useEffect(() => {
+ // check if given url contains hash (therefore an anchor)
+ const scrollMark = location.hash;
+ if (scrollMark) {
+ // wait when html content adds all id to h2 then scroll to it
+ whenElementAvailable(scrollMark)(el =>
+ // no smooth scroll needed
+ window.scrollTo({
+ top: el.getBoundingClientRect().top + window.scrollY - 25,
+ }),
+ );
+ }
+ }, []);
+ const handleAnchorClick = (e, anchor) => {
+ e.preventDefault();
+ document.querySelector(anchor).scrollIntoView({
+ behavior: 'smooth',
+ block: 'start',
+ });
+ // changing hash without default jumps to anchor
+ if (history.pushState) {
+ history.pushState(false, false, anchor);
+ } else {
+ // old browser support
+ window.location.hash = anchor;
+ }
+ };
+
+ const stickyContainerClasses = classNames(
+ docPageContent.mainDocContent,
+ docPageContent.contentWrapper,
+ );
+ const renderSidebar = ({ style }) => (
+
+ );
+ return (
+
+
+
+
+
+
+
+ {index.map(node => {
+ const out = [node.module];
+ out.push(createTableMaybe(node.classes, 'Class'));
+ out.push(createTableMaybe(node.functions, 'Function'));
+ return out;
+ })}
+
+
+
+
+ {renderSidebar}
+
+
+
+
+ );
+}
+
+export const query = graphql`
+ query IndexQuery {
+ allFile(
+ filter: {
+ ext: { eq: ".md" }
+ relativeDirectory: { regex: "/javascript api/" }
+ }
+ sort: { fields: absolutePath, order: ASC }
+ ) {
+ nodes {
+ id
+ relativeDirectory
+ children {
+ ... on MarkdownRemark {
+ html
+ frontmatter {
+ title
+ category
+ description
+ }
+ }
+ }
+ }
+ }
+ }
+`;
diff --git a/src/utils/algolia.js b/src/utils/algolia.js
new file mode 100644
index 0000000000..b687212988
--- /dev/null
+++ b/src/utils/algolia.js
@@ -0,0 +1,80 @@
+const utils = require('./utils');
+// this copypaste from gatsby-node sidebar node
+// generation, we have to
+// perform exactly the same operations for algolia
+
+// ensures that no trailing slash is left
+const noTrailingSlash = path =>
+ path === '/' ? '/' : path.replace(/(.+)\/$/, '$1');
+
+// duplication, removing it as well
+const dedupeExamples = path => path.replace(/examples\/examples/, 'examples');
+
+// no /guides route; welcome is redirecting to root path
+// difference from removeGuides: this one is for sidebar links processing and
+// the former is for creatingPages
+const removeGuidesAndRedirectWelcome = path =>
+ path.replace(/guides\/(getting-started\/welcome)?/, '');
+
+// helper
+const flatten = arr =>
+ arr.map(({ node: { frontmatter, fileAbsolutePath, ...rest } }) => {
+ const strippedDirectory = utils.stripDirectoryPath(
+ fileAbsolutePath,
+ 'docs',
+ );
+ // cut the last piece (the actual name of a file) to match the generation
+ // in node
+ const cuttedStrippedDirectory = strippedDirectory
+ .split('/')
+ .slice(0, -1)
+ .join('/');
+ const path = utils.slugify(
+ `/${cuttedStrippedDirectory}/${frontmatter.title.replace(/\//g, '-')}`,
+ );
+ return {
+ ...frontmatter,
+ ...rest,
+ // slug: utils.unorderify(path).replace(/guides\//, ''),
+ slug: utils.compose(
+ noTrailingSlash,
+ dedupeExamples,
+ removeGuidesAndRedirectWelcome,
+ utils.unorderify,
+ )(path),
+ };
+ });
+
+// main query
+const docPagesQuery = `{
+ docPages: allMarkdownRemark(
+ filter: { fileAbsolutePath: { regex: "/" } }
+ ) {
+ edges {
+ node {
+ objectID: id
+ frontmatter {
+ title
+ category
+ }
+ excerpt(pruneLength: 5000)
+ fileAbsolutePath
+ }
+ }
+ }
+}`;
+
+// additional config
+const settings = { attributesToSnippet: ['excerpt:20'] };
+
+const indexName = process.env.GATSBY_ALGOLIA_INDEX_NAME || 'dev_k6_docs';
+
+const queries = [
+ {
+ query: docPagesQuery,
+ transformer: ({ data }) => flatten(data.docPages.edges),
+ indexName,
+ settings,
+ },
+];
+module.exports = queries;
diff --git a/src/utils/index.js b/src/utils/index.js
new file mode 100644
index 0000000000..56b8da3106
--- /dev/null
+++ b/src/utils/index.js
@@ -0,0 +1,17 @@
+export {
+ getRandomNumberBetween,
+ slugify,
+ childrenToList,
+ getRandomKey,
+ stripDirectoryPath,
+ trimToLengthWithEllipsis,
+ getDateAndSlugFromPath,
+ getCurrentCta,
+ createMetaImagePath,
+ unorderify,
+ getAnchorLinks,
+ compose,
+ noSlugDuplication,
+ whenElementAvailable,
+ isInIFrame,
+} from './utils';
diff --git a/src/utils/seo-metadata.js b/src/utils/seo-metadata.js
new file mode 100644
index 0000000000..8f9b90dae5
--- /dev/null
+++ b/src/utils/seo-metadata.js
@@ -0,0 +1,31 @@
+/* eslint-disable max-len */
+const defaultDescription = 'k6 is an open-source load testing tool and cloud service providing the best developer experience for API performance testing.';
+const cloudDescription = 'The k6 Cloud is a cloud-based load testing service that complements k6 to accelerate and scale your performance testing.';
+
+
+export default {
+ 404: { data: {
+ title: '404 | k6 Documentation',
+ description: defaultDescription,
+ } },
+ 'javascript-api': { data: {
+ title: 'k6 JavaScript API',
+ description: 'This page provides the documentation of the k6 JavaScript API.',
+ } },
+ examples: { data: {
+ title: 'k6 Examples & Tutorials',
+ description: 'The k6 Examples & Tutorials is a directory with common k6 examples and the most popular tutorials using k6.',
+ } },
+ cloud: { data: {
+ title: 'k6 Cloud Documentation',
+ description: 'The k6 Cloud documentation helps you in your usage of the k6 Cloud. The k6 Cloud is the perfect companion to k6 with an aim to bring ease-of-use and convenience to scale your performance testing efforts.',
+ } },
+ guides: { data: {
+ title: 'k6 Documentation',
+ description: 'The k6 Documentation helps you to use k6 to get your performance testing on the right track. Learn more about load and performance testing. Get started in minutes.',
+ } },
+ integrations: { data: {
+ title: 'k6 Integrations',
+ description: 'The k6 Integrations page lists the most popular k6 integrations. Integrate k6 with CI tools, store results in different services, select different Grafana dashboards, or use converters for the auto-generation of your load test.',
+ } },
+}
\ No newline at end of file
diff --git a/src/utils/urls/index.js b/src/utils/urls/index.js
new file mode 100644
index 0000000000..06aad7587a
--- /dev/null
+++ b/src/utils/urls/index.js
@@ -0,0 +1,4 @@
+export const blog = process.env.GATSBY_DEFAULT_BLOG_URL;
+export const docs = process.env.GATSBY_DEFAULT_DOC_URL;
+export const main = process.env.GATSBY_DEFAULT_MAIN_URL;
+export const app = process.env.GATSBY_DEFAULT_APP_URL || 'https://app.k6.io';
diff --git a/src/utils/utils.js b/src/utils/utils.js
new file mode 100644
index 0000000000..0fc81e2458
--- /dev/null
+++ b/src/utils/utils.js
@@ -0,0 +1,278 @@
+/*
+ * General utility functions
+ */
+
+// container for default export (node-specific action)
+const utils = {};
+
+// getRandomNumberBetween(min: Number = 1, max: Number = 10) -> Number
+const getRandomNumberBetween = (min = 1, max = 10) => {
+ const [rangeStart, rangeFinish] = [Math.ceil(min), Math.ceil(max)];
+
+ return (
+ Math.floor(Math.random() * (rangeFinish - rangeStart + 1)) + rangeStart
+ );
+};
+
+// transforms path-like strings into slugs
+// slugify(path: String) -> String
+const slugify = path =>
+ path
+ .toLowerCase()
+ .replace(/[\s-;:!?&,\(\)\[\]]{1,}/g, '-')
+ .replace(/[%@~]/g, '')
+ .replace(/(-{1,})?(\.md)?$/, '') // removes parts like "*-.md" or "*.md" or "-" postfix
+ .replace(/(\/)\-{1,}/g, '$1') // removed '-' prefix from any path part
+ .replace(/\./g, '-'); // replace dots with '-' after we removed extension
+
+// buildBreadcrumbs(path: String) -> Array
+const buildBreadcrumbs = path => {
+ let accumulatedPath = '';
+ return path
+ .replace(/\/guides/g, '')
+ .replace(/examples\/examples/, 'examples')
+ .split('/')
+ .map(part => {
+ accumulatedPath += `/${part}`;
+ const slug = utils.slugify(accumulatedPath);
+ return {
+ name: utils.slugify(part),
+ path: slug,
+ };
+ });
+};
+
+// builds a single file tree node
+// buildFileTreeNode(name: String) -> Object
+const buildFileTreeNode = (name, meta = {}, children = {}) => ({
+ name,
+ meta,
+ children,
+});
+
+// creates a file tree structure
+// buildFileTree(nodeBuild: Function) -> Object
+const buildFileTree = nodeBuilder => {
+ const root = nodeBuilder('_root');
+
+ const addNode = (path, name, meta = {}) => {
+ let parent = root;
+ const parts = path.split('/');
+ parts.push(name);
+ parts.forEach(part => {
+ parent.children[part] = parent.children[part] || nodeBuilder(part);
+ parent = parent.children[part];
+ });
+
+ parent.meta = { ...meta };
+ };
+
+ const getTree = () => root;
+
+ return {
+ root,
+ addNode,
+ getTree,
+ };
+};
+
+// takes a nested object with file nodes and returns an array of values;
+// childrenToList(children: Object) -> Array
+const childrenToList = children => Object.values(children);
+
+// takes a string like 'docs/001-Directory/01-file' or just '001-Directory'
+// and removes all the order numbers like 'docs/Directory/file' or 'Directory'
+// unorderify(str: String, nameOnly?: Bool) -> String
+const unorderify = (str, nameOnly = false) => {
+ const unorderEntry = entry => entry.replace(/^(\d*\W*)(\.*)/, '$2');
+ return nameOnly
+ ? unorderEntry(str)
+ : str
+ .split('/')
+ .map(unorderEntry)
+ .join('/');
+};
+
+// makes a consequence of random digits in form of a string to be served as a key prop
+// getRandomKey() -> String
+const getRandomKey = () =>
+ `k${Math.random()
+ .toString()
+ .replace('.', '')}`;
+
+// takes a path string and a type of path and trims redundant directories according to a type
+// stripDirectoryPath(str: String, type?: String) -> String
+const stripDirectoryPath = (str, type) => {
+ switch (type) {
+ case 'docs':
+ return str.replace(/^.*docs\/(.*)$/, '$1');
+ case 'post':
+ return str.replace(/(.*)(\d{4}-\d{2}-\d{2}.*)(\/.*\.\w+$)/, '$2');
+ default:
+ return str;
+ }
+};
+
+// takes a string and an the desired length to which string should be trimmed;
+// return trimmed string with word wrapping on white spaces and an ellipsis at the end
+// trimToLengthWithEllipsis(str: String, ln?: Number) -> String
+const trimToLengthWithEllipsis = (str, ln = 140) =>
+ str.length <= ln
+ ? str
+ : str
+ .split(' ')
+ .reduce(
+ (acc, cur) =>
+ acc.length + cur.length + 1 > ln - 4 ? acc : (acc += ` ${cur}`),
+ '',
+ )
+ .concat(' ...');
+
+// post manipulation related function that extracts a date and a path
+// from given path
+// getDateAndSlugFromPath(path: String) -> Object
+const getDateAndSlugFromPath = path => {
+ const [date, slug] = stripDirectoryPath(path, 'post').split('--');
+ return { date, slug: slugify(slug) };
+};
+
+// post manipulation related function that determines how to deal with cta button based on given frontmatter value
+// getCurrentCta(ctaData: Array, ctaType: String|Null) -> Object
+const getCurrentCta = (ctaData, ctaType) => {
+ switch (ctaType) {
+ case 'none':
+ return false;
+ case null:
+ return ctaData.find(({ node: { type } }) => type === 'default').node;
+ default:
+ return ctaData.find(({ node: { type } }) => type === ctaType).node;
+ }
+};
+
+// seo-compoenent-specific fn that creates correct path for the og:image
+// createMetaImagePath(image: Object|String, defautlSiteUrl: String, defaultImage: String) -> String
+const createMetaImagePath = (image, defaultSiteUrl, defaultImage) => {
+ switch (typeof image) {
+ case 'object':
+ return defaultSiteUrl + image.childImageSharp.fluid.src;
+ case 'string':
+ return defaultSiteUrl + image;
+ default:
+ return defaultSiteUrl + defaultImage;
+ }
+};
+
+// docs-page-specific fn that creates a set of data to be used in docs component
+// getAnchorLinks(content: String) -> Array
+const getAnchorLinks = content => {
+ const rawHeadings = content.match(/.*<\/h2>/g);
+ const strippedHeadings = rawHeadings
+ ? rawHeadings.map(heading => heading.replace(/<\/?h2>/g, ''))
+ : [];
+ return strippedHeadings.map(heading => ({
+ title: heading,
+ anchor: `#${
+ slugify(heading).replace(
+ /\//g,
+ '-',
+ ) /* replace slashes in titles manually */
+ }`,
+ }));
+};
+
+// creating-docs-pages-specific function; extracts the category after
+// 'docs'; e.g. /whatever/some-more -> whatever
+// getDocSection(str: String) -> String
+const getDocSection = str => str.replace(/^(.*?)\/.*$/, '$1');
+
+// docs-sidebar-specific function; extracts a certain part of a sidebar which
+// which root key matches passed child
+// getChildSidebar(sidebar: Object -> child: String) -> Object
+const getChildSidebar = sidebar => child => sidebar.children[child];
+
+// basic compose function
+const compose = (...fns) => (...args) =>
+ fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0];
+
+const noSlugDuplication = (indexEntry, slug) => {
+ const src = indexEntry.module.props.children[1].props.content;
+ const check = src.includes(slug);
+ return !check;
+};
+
+// this function takes CSSSelector and callback which
+// is being executed as soon as passed selector matches DOM element
+const whenElementAvailable = elementSelector => cb =>
+ document.querySelector(elementSelector)
+ ? cb(document.querySelector(elementSelector))
+ : setTimeout(() => whenElementAvailable(elementSelector)(cb), 100);
+
+// simple fn that determines if the page is being rendered
+// into an iframe
+const isInIFrame = () =>
+ typeof window !== 'undefined' && window.location !== window.parent.location;
+
+Object.defineProperties(utils, {
+ isInIFrame: {
+ value: isInIFrame,
+ },
+ getRandomNumberBetween: {
+ value: getRandomNumberBetween,
+ },
+ slugify: {
+ value: slugify,
+ },
+ buildBreadcrumbs: {
+ value: buildBreadcrumbs,
+ },
+ buildFileTreeNode: {
+ value: buildFileTreeNode,
+ },
+ buildFileTree: {
+ value: buildFileTree,
+ },
+ childrenToList: {
+ value: childrenToList,
+ },
+ getRandomKey: {
+ value: getRandomKey,
+ },
+ stripDirectoryPath: {
+ value: stripDirectoryPath,
+ },
+ trimToLengthWithEllipsis: {
+ value: trimToLengthWithEllipsis,
+ },
+ getDateAndSlugFromPath: {
+ value: getDateAndSlugFromPath,
+ },
+ getCurrentCta: {
+ value: getCurrentCta,
+ },
+ createMetaImagePath: {
+ value: createMetaImagePath,
+ },
+ unorderify: {
+ value: unorderify,
+ },
+ getAnchorLinks: {
+ value: getAnchorLinks,
+ },
+ getDocSection: {
+ value: getDocSection,
+ },
+ getChildSidebar: {
+ value: getChildSidebar,
+ },
+ compose: {
+ value: compose,
+ },
+ noSlugDuplication: {
+ value: noSlugDuplication,
+ },
+ whenElementAvailable: {
+ value: whenElementAvailable,
+ },
+});
+
+module.exports = utils;
diff --git a/static/images/icon.png b/static/images/icon.png
new file mode 100644
index 0000000000..0419299be0
Binary files /dev/null and b/static/images/icon.png differ
diff --git a/static/images/landscape-icon.png b/static/images/landscape-icon.png
new file mode 100644
index 0000000000..99232924a8
Binary files /dev/null and b/static/images/landscape-icon.png differ
diff --git a/static/images/sample-image.png b/static/images/sample-image.png
new file mode 100644
index 0000000000..be69cdd0a9
Binary files /dev/null and b/static/images/sample-image.png differ