├── .github └── workflows │ ├── k6_cloud.yml │ └── main.yml ├── .gitignore ├── README.md ├── addToCart.js ├── main.js ├── navigateHomepage.js ├── navigateToCart.js ├── navigateToCheckout.js ├── submitCheckout.js ├── updateAddress.js └── utils.js /.github/workflows/k6_cloud.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: k6 Cloud Load Test 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [ main ] 10 | pull_request: 11 | branches: [ main ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "k6_load_test" 19 | k6_load_test: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - name: Checkout 27 | uses: actions/checkout@v1 28 | 29 | - name: Run Test 30 | uses: grafana/k6-action@v0.2.0 31 | id: runtest 32 | with: 33 | cloud: true 34 | filename: main.js 35 | flags: -v -q # verbose, quiet 36 | token: ${{ secrets.K6_CLOUD_TOKEN }} 37 | env: 38 | K6_CLOUD_PROJECT_ID: 3683206 39 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: k6 Load Test 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [ main ] 10 | pull_request: 11 | branches: [ main ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | k6_load_test: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - name: Checkout 27 | uses: actions/checkout@v1 28 | 29 | - name: Run Test 30 | uses: grafana/k6-action@v0.2.0 31 | with: 32 | filename: main.js 33 | flags: -v -q # verbose, quiet 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | workspace.code-workspace 2 | /misc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # k6 eCommerce Demo Scripts 2 | 3 | ## Introduction 4 | 5 | This repo contains some example k6 scripts interacting with a basic WooCommerce website hosted at http://ecommerce.test.k6.io. 6 | 7 | The scripts have been modularized so that each distinct "user action" manifests as its own source file, intended to be used from the entry script, in this case `main.js`. Doing so promotes code reuse and maintainability, as well as catering for some degree of flexibility over the order in which the scripts should run. Obviously, products need to have been added to the cart before checkout can be completed successfully, so there is some sequence that needs to be maintained for them to work as expected. 8 | 9 | ## Usage 10 | 11 | Please note that the server hosting the site is not scaled for heavy loads; the scripts are being provided as working examples. Only run them if you want to see what kind of feedback k6 provides when they are run as part of a test. 12 | 13 | 1. Install [k6](https://k6.io) (instructions [here](https://k6.io/docs/getting-started/installation/)) 14 | 2. Clone the repo 15 | 3. Navigate to the directory and `k6 run main.js` (make sure k6 is on your PATH) 16 | 17 | ## Contents 18 | 19 | The scripts, and their suggested order, are as follows: 20 | 21 | `main.js` 22 | 23 | The entry script, where k6 `options` would be set, and the script called as part of `k6 run` (see Usage below). Its `export default function` determines what the VUs will run. 24 | 25 | `utils.js` 26 | 27 | This utility script contains a single exported function `checkStatus` that can be used to verify HTTP status codes. Should the received status code be different from the expected one, a couple of booleans determine whether to print the `Response.body` (if available) and whether to `fail` (skip) the rest of the iteration. 28 | 29 | `navigateHomepage.js` 30 | 31 | Naturally the first script to be executed. As the homepage also lists the available products on the site, it is also where these are extracted from the response, with a product selected at random and stored in `globalThis.vars["selectedProduct"]`. 32 | 33 | `addToCart.js` 34 | 35 | Uses (and depends on) the randomly-selected product extracted in `navigateHomepage.js`. An enhancement here might be to make all products available to it (perhaps as input data via the function parameters), such that mutiple products can be added instead of just a single one. 36 | 37 | `navigateToCart.js` 38 | 39 | The equivalent of the user clicking "View Cart". 40 | 41 | `navigateToCheckout.js` 42 | 43 | Aside from proceeding to checkout, there are also two dynamic values that need to be extracted from the response and used in the subsequent checkout itself. 44 | 45 | `updateAddress.js` 46 | 47 | This script represents an AJAX call that takes place when the user enters their address prior to checkout. 48 | 49 | `submitCheckout.js` 50 | 51 | The final checkout of the cart. A `result: "success"` JSON value is expected in the response, and so a `check` ensures that is the case. The JSON is also expected to contain a `redirectUrl` that takes the user to the confirmation page. 52 | -------------------------------------------------------------------------------- /addToCart.js: -------------------------------------------------------------------------------- 1 | import { sleep, group } from "k6"; 2 | import http from "k6/http"; 3 | import { checkStatus } from "./utils.js"; 4 | import { randomIntBetween } from "https://jslib.k6.io/k6-utils/1.1.0/index.js"; 5 | 6 | export function addToCart() { 7 | group("Add to Cart", function () { 8 | const response = http.post( 9 | "http://ecommerce.test.k6.io/?wc-ajax=add_to_cart", 10 | { 11 | product_sku: globalThis.vars["selectedProduct"].sku, 12 | product_id: globalThis.vars["selectedProduct"].id, 13 | quantity: "1", 14 | }, 15 | { 16 | headers: { 17 | accept: "application/json, text/javascript, */*; q=0.01", 18 | "accept-encoding": "gzip, deflate", 19 | "accept-language": "en-US,en;q=0.9", 20 | connection: "keep-alive", 21 | "content-type": 22 | "application/x-www-form-urlencoded;type=content-type;mimeType=application/x-www-form-urlencoded", 23 | host: "ecommerce.test.k6.io", 24 | origin: "http://ecommerce.test.k6.io", 25 | "x-requested-with": "XMLHttpRequest", 26 | }, 27 | } 28 | ); 29 | 30 | checkStatus({ 31 | response: response, 32 | expectedStatus: 200, 33 | failOnError: true, 34 | printOnError: true 35 | }); 36 | }); 37 | 38 | sleep(randomIntBetween(pauseMin, pauseMax)); 39 | } -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import { navigateHomepage } from "./navigateHomepage.js"; 2 | import { addToCart } from "./addToCart.js"; 3 | import { navigateToCart } from "./navigateToCart.js"; 4 | import { navigateToCheckout } from "./navigateToCheckout.js"; 5 | import { updateAddress } from "./updateAddress.js"; 6 | import { submitCheckout } from "./submitCheckout.js"; 7 | 8 | export const options = { 9 | }; 10 | 11 | // used to store global variables 12 | globalThis.vars = []; 13 | 14 | // global min/max sleep durations (in seconds): 15 | globalThis.pauseMin = 5; 16 | globalThis.pauseMax = 15; 17 | 18 | export default function main() { 19 | navigateHomepage(); 20 | addToCart(); 21 | navigateToCart(); 22 | navigateToCheckout(); 23 | updateAddress(); 24 | submitCheckout(); 25 | } -------------------------------------------------------------------------------- /navigateHomepage.js: -------------------------------------------------------------------------------- 1 | import { sleep, group } from "k6"; 2 | import http from "k6/http"; 3 | import { checkStatus } from "./utils.js"; 4 | import { randomIntBetween } from "https://jslib.k6.io/k6-utils/1.1.0/index.js"; 5 | 6 | export function navigateHomepage() { 7 | group("Navigate to Homepage", function () { 8 | let response = http.get("http://ecommerce.test.k6.io/", { 9 | headers: { 10 | accept: 11 | "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", 12 | "accept-encoding": "gzip, deflate", 13 | "accept-language": "en-US,en;q=0.9", 14 | "cache-control": "max-age=0", 15 | connection: "keep-alive", 16 | host: "ecommerce.test.k6.io", 17 | "upgrade-insecure-requests": "1", 18 | }, 19 | }); 20 | 21 | checkStatus({ 22 | response: response, 23 | expectedStatus: 200, 24 | failOnError: true, 25 | printOnError: true 26 | }); 27 | 28 | // extract all of the available products using their "Add to Cart" buttons 29 | const addToCartButtons = response 30 | .html() 31 | .find("li[class*=product]") 32 | .find('a:contains("Add to Cart")') 33 | .toArray(); 34 | 35 | const products = addToCartButtons.map(i => { 36 | return { 37 | id: i.get(0).getAttribute("data-product_id"), 38 | sku: i.get(0).getAttribute("data-product_sku") 39 | }; 40 | }); 41 | 42 | products.forEach(i => { 43 | console.debug(`Product ID: '${i.id}' SKU: '${i.sku}'`); 44 | }); 45 | 46 | // select a random product and store in vars: 47 | globalThis.vars["selectedProduct"] = products[Math.floor(Math.random() * products.length)]; 48 | console.debug(`Selected Product with ID: '${globalThis.vars["selectedProduct"].id}' and SKU: '${globalThis.vars["selectedProduct"].sku}'`); 49 | 50 | response = http.post( 51 | "http://ecommerce.test.k6.io/?wc-ajax=get_refreshed_fragments", 52 | { 53 | time: Date.now(), 54 | }, 55 | { 56 | headers: { 57 | accept: "*/*", 58 | "accept-encoding": "gzip, deflate", 59 | "accept-language": "en-US,en;q=0.9", 60 | connection: "keep-alive", 61 | "content-type": 62 | "application/x-www-form-urlencoded;type=content-type;mimeType=application/x-www-form-urlencoded", 63 | host: "ecommerce.test.k6.io", 64 | origin: "http://ecommerce.test.k6.io", 65 | "x-requested-with": "XMLHttpRequest", 66 | }, 67 | } 68 | ); 69 | 70 | checkStatus({ 71 | response: response, 72 | expectedStatus: 200, 73 | failOnError: true, 74 | printOnError: true 75 | }); 76 | }); 77 | 78 | sleep(randomIntBetween(pauseMin, pauseMax)); 79 | } -------------------------------------------------------------------------------- /navigateToCart.js: -------------------------------------------------------------------------------- 1 | import { sleep, group } from "k6"; 2 | import http from "k6/http"; 3 | import { checkStatus } from "./utils.js"; 4 | import { randomIntBetween } from "https://jslib.k6.io/k6-utils/1.1.0/index.js"; 5 | 6 | export function navigateToCart() { 7 | group("Navigate to Cart", function () { 8 | const response = http.get("http://ecommerce.test.k6.io/cart/", { 9 | headers: { 10 | accept: 11 | "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", 12 | "accept-encoding": "gzip, deflate", 13 | "accept-language": "en-US,en;q=0.9", 14 | connection: "keep-alive", 15 | host: "ecommerce.test.k6.io", 16 | "upgrade-insecure-requests": "1", 17 | }, 18 | }); 19 | 20 | checkStatus({ 21 | response: response, 22 | expectedStatus: 200, 23 | failOnError: true, 24 | printOnError: true 25 | }); 26 | }); 27 | 28 | sleep(randomIntBetween(pauseMin, pauseMax)); 29 | } -------------------------------------------------------------------------------- /navigateToCheckout.js: -------------------------------------------------------------------------------- 1 | import { sleep, group } from "k6"; 2 | import http from "k6/http"; 3 | import { checkStatus } from "./utils.js"; 4 | import { randomIntBetween, findBetween } from "https://jslib.k6.io/k6-utils/1.1.0/index.js"; 5 | 6 | export function navigateToCheckout() { 7 | group("Navigate to Checkout", function () { 8 | const response = http.get("http://ecommerce.test.k6.io/checkout/", { 9 | headers: { 10 | accept: 11 | "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", 12 | "accept-encoding": "gzip, deflate", 13 | "accept-language": "en-US,en;q=0.9", 14 | connection: "keep-alive", 15 | host: "ecommerce.test.k6.io", 16 | "upgrade-insecure-requests": "1", 17 | }, 18 | }); 19 | 20 | checkStatus({ 21 | response: response, 22 | expectedStatus: 200, 23 | failOnError: true, 24 | printOnError: true 25 | }); 26 | 27 | // dynamic value: update_order_review_nonce 28 | globalThis.vars["securityToken"] = findBetween(response.body, 'update_order_review_nonce":"', '"'); 29 | 30 | // dynamic value: woocommerce-process-checkout-nonce 31 | globalThis.vars["checkoutToken"] = response 32 | .html("#woocommerce-process-checkout-nonce") 33 | .val(); 34 | 35 | console.debug("Security token: " + globalThis.vars["securityToken"]); 36 | console.debug("Checkout token: " + globalThis.vars["checkoutToken"]); 37 | }); 38 | 39 | sleep(randomIntBetween(pauseMin, pauseMax)); 40 | } -------------------------------------------------------------------------------- /submitCheckout.js: -------------------------------------------------------------------------------- 1 | import { sleep, group, check, fail } from "k6"; 2 | import http from "k6/http"; 3 | import { checkStatus } from "./utils.js"; 4 | import { randomIntBetween, findBetween } from "https://jslib.k6.io/k6-utils/1.1.0/index.js"; 5 | 6 | import jsonpath from "https://jslib.k6.io/jsonpath/1.0.2/index.js"; 7 | 8 | export function submitCheckout() { 9 | let response; 10 | 11 | group("Submit Checkout", function () { 12 | response = http.post( 13 | "http://ecommerce.test.k6.io/?wc-ajax=checkout", 14 | { 15 | billing_first_name: "k6", 16 | billing_last_name: "Test", 17 | billing_company: "", 18 | billing_country: "US", 19 | billing_address_1: "Street Address 1", 20 | billing_address_2: "", 21 | billing_city: "Frisco", 22 | billing_state: "CO", 23 | billing_postcode: "80443", 24 | billing_phone: "7201234567", 25 | billing_email: "anon@k6.io", 26 | order_comments: "", 27 | payment_method: "cod", 28 | "woocommerce-process-checkout-nonce": globalThis.vars["checkoutToken"], 29 | _wp_http_referer: "/?wc-ajax=update_order_review", 30 | }, 31 | { 32 | headers: { 33 | accept: "application/json, text/javascript, */*; q=0.01", 34 | "accept-encoding": "gzip, deflate", 35 | "accept-language": "en-US,en;q=0.9", 36 | connection: "keep-alive", 37 | "content-type": 38 | "application/x-www-form-urlencoded;type=content-type;mimeType=application/x-www-form-urlencoded", 39 | host: "ecommerce.test.k6.io", 40 | origin: "http://ecommerce.test.k6.io", 41 | "x-requested-with": "XMLHttpRequest", 42 | }, 43 | } 44 | ); 45 | 46 | checkStatus({ 47 | response: response, 48 | expectedStatus: 200, 49 | failOnError: true, 50 | printOnError: true 51 | }); 52 | 53 | let result; 54 | 55 | try { 56 | result = jsonpath.query( 57 | response.json(), 58 | "$['result']" 59 | )[0]; 60 | } catch (err) { 61 | // not JSON most likely, so print the response (if there was a response.body): 62 | if (response.body) { 63 | console.log(response.body); 64 | } 65 | fail(err); // ends the iteration 66 | } 67 | 68 | // another check to ensure the checkout response contained 'success' in the 'result' property 69 | check(result, { 70 | 'checkout completed successfully': (r) => r === 'success' 71 | }); 72 | 73 | globalThis.vars["redirectUrl"] = jsonpath.query( 74 | response.json(), 75 | "$['redirect']" 76 | )[0]; 77 | 78 | if (!globalThis.vars["redirectUrl"]) { 79 | fail(`Checkout failed: no redirect URL in response:\n${response.body}`); 80 | } 81 | 82 | console.debug("Checkout redirect URL: " + globalThis.vars["redirectUrl"]); 83 | 84 | // the order ID is in the redirectUrl 85 | globalThis.vars["orderId"] = findBetween(globalThis.vars["redirectUrl"], 'order-received/', '/'); 86 | globalThis.vars["key"] = globalThis.vars["redirectUrl"].substring(globalThis.vars["redirectUrl"].indexOf('key=') + 4); 87 | 88 | console.debug("orderId: " + globalThis.vars["orderId"]); 89 | console.debug("key: " + globalThis.vars["key"]); 90 | 91 | if (globalThis.vars["orderId"].length > 0) { 92 | console.log("Successfully placed order! ID: " + globalThis.vars["orderId"]); 93 | } else { 94 | if (response.body) { 95 | fail("Failed to place order: " + response.body); 96 | } else { 97 | fail("Failed to place order (no response.body)."); 98 | } 99 | } 100 | 101 | response = http.get( 102 | globalThis.vars["redirectUrl"], 103 | { 104 | tags: { 105 | name: "http://ecommerce.test.k6.io/checkout/order-received/" 106 | }, 107 | headers: { 108 | accept: 109 | "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", 110 | "accept-encoding": "gzip, deflate", 111 | "accept-language": "en-US,en;q=0.9", 112 | connection: "keep-alive", 113 | host: "ecommerce.test.k6.io", 114 | "upgrade-insecure-requests": "1", 115 | }, 116 | } 117 | ); 118 | 119 | checkStatus({ 120 | response: response, 121 | expectedStatus: 200, 122 | failOnError: true, 123 | printOnError: true, 124 | dynamicIds: [ 125 | globalThis.vars["orderId"], 126 | globalThis.vars["key"] 127 | ] 128 | }); 129 | 130 | response = http.post( 131 | "http://ecommerce.test.k6.io/?wc-ajax=get_refreshed_fragments", 132 | { 133 | time: "1613672584353", 134 | }, 135 | { 136 | headers: { 137 | accept: "*/*", 138 | "accept-encoding": "gzip, deflate", 139 | "accept-language": "en-US,en;q=0.9", 140 | connection: "keep-alive", 141 | "content-type": 142 | "application/x-www-form-urlencoded;type=content-type;mimeType=application/x-www-form-urlencoded", 143 | host: "ecommerce.test.k6.io", 144 | origin: "http://ecommerce.test.k6.io", 145 | "x-requested-with": "XMLHttpRequest", 146 | }, 147 | } 148 | ); 149 | 150 | checkStatus({ 151 | response: response, 152 | expectedStatus: 200, 153 | failOnError: true, 154 | printOnError: true 155 | }); 156 | }); 157 | 158 | sleep(randomIntBetween(pauseMin, pauseMax)); 159 | } -------------------------------------------------------------------------------- /updateAddress.js: -------------------------------------------------------------------------------- 1 | import { sleep, group } from "k6"; 2 | import http from "k6/http"; 3 | import { checkStatus } from "./utils.js"; 4 | import { randomIntBetween } from "https://jslib.k6.io/k6-utils/1.1.0/index.js"; 5 | 6 | export function updateAddress() { 7 | group("Update Address", function () { 8 | let response = http.post( 9 | "http://ecommerce.test.k6.io/?wc-ajax=update_order_review", 10 | { 11 | security: globalThis.vars["securityToken"], 12 | payment_method: "cod", 13 | country: "US", 14 | state: "CO", 15 | postcode: "", 16 | city: "", 17 | address: "", 18 | address_2: "", 19 | s_country: "US", 20 | s_state: "CO", 21 | s_postcode: "", 22 | s_city: "", 23 | s_address: "", 24 | s_address_2: "", 25 | has_full_address: "false", 26 | post_data: 27 | "billing_first_name=&billing_last_name=&billing_company=&billing_country=US&billing_address_1=&billing_address_2=&billing_city=&billing_state=CO&billing_postcode=&billing_phone=&billing_email=&order_comments=&payment_method=cod&woocommerce-process-checkout-nonce=" + globalThis.vars["checkoutToken"] + "&_wp_http_referer=%2Fcheckout%2F", 28 | }, 29 | { 30 | headers: { 31 | accept: "*/*", 32 | "accept-encoding": "gzip, deflate", 33 | "accept-language": "en-US,en;q=0.9", 34 | connection: "keep-alive", 35 | "content-type": 36 | "application/x-www-form-urlencoded;type=content-type;mimeType=application/x-www-form-urlencoded", 37 | host: "ecommerce.test.k6.io", 38 | origin: "http://ecommerce.test.k6.io", 39 | "x-requested-with": "XMLHttpRequest", 40 | }, 41 | } 42 | ); 43 | 44 | checkStatus({ 45 | response: response, 46 | expectedStatus: 200, 47 | failOnError: true, 48 | printOnError: true 49 | }); 50 | 51 | response = http.post( 52 | "http://ecommerce.test.k6.io/?wc-ajax=update_order_review", 53 | { 54 | security: globalThis.vars["securityToken"], 55 | payment_method: "cod", 56 | country: "US", 57 | state: "CO", 58 | postcode: "80443", 59 | city: "Frisco", 60 | address: "Street Address 1", 61 | address_2: "", 62 | s_country: "US", 63 | s_state: "CO", 64 | s_postcode: "80443", 65 | s_city: "Frisco", 66 | s_address: "Street Address 1", 67 | s_address_2: "", 68 | has_full_address: "true", 69 | post_data: 70 | "billing_first_name=Tom&billing_last_name=Test&billing_company=&billing_country=US&billing_address_1=Street%20Address%201&billing_address_2=&billing_city=Frisco&billing_state=CO&billing_postcode=80443&billing_phone=&billing_email=&order_comments=&payment_method=cod&woocommerce-process-checkout-nonce=" + globalThis.vars["checkoutToken"] + "&_wp_http_referer=%2F%3Fwc-ajax%3Dupdate_order_review", 71 | }, 72 | { 73 | headers: { 74 | accept: "*/*", 75 | "accept-encoding": "gzip, deflate", 76 | "accept-language": "en-US,en;q=0.9", 77 | connection: "keep-alive", 78 | "content-type": 79 | "application/x-www-form-urlencoded;type=content-type;mimeType=application/x-www-form-urlencoded", 80 | host: "ecommerce.test.k6.io", 81 | origin: "http://ecommerce.test.k6.io", 82 | "x-requested-with": "XMLHttpRequest", 83 | }, 84 | } 85 | ); 86 | 87 | checkStatus({ 88 | response: response, 89 | expectedStatus: 200, 90 | failOnError: true, 91 | printOnError: true 92 | }); 93 | }); 94 | 95 | sleep(randomIntBetween(pauseMin, pauseMax)); 96 | } -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | import { check, fail } from "k6"; 2 | 3 | export function checkStatus({ response, expectedStatus, expectedContent, failOnError, printOnError, dynamicIds }) { 4 | if (isEmpty(expectedStatus) && isEmpty(expectedContent)) { 5 | console.warn('No expected status or content specified in call to checkStatus for URL ' + response.url); 6 | return; 7 | } 8 | 9 | let contentCheckResult; 10 | let statusCheckResult; 11 | 12 | let url = response.url; 13 | 14 | if (dynamicIds) { 15 | dynamicIds.forEach((dynamicId) => { 16 | if (response.url.includes(dynamicId)) { 17 | url = url.replace(dynamicId, '[id]'); 18 | } 19 | }); 20 | } 21 | 22 | if (expectedContent) { 23 | contentCheckResult = check(response, { 24 | [`"${expectedContent}" in ${url} response`]: (r) => r.body.includes(expectedContent), 25 | }); 26 | } 27 | 28 | if (expectedStatus) { 29 | const obj = {}; 30 | obj[`${response.request.method} ${url} status ${expectedStatus}`] = (r) => r.status === expectedStatus; 31 | 32 | statusCheckResult = check(response, obj); 33 | } 34 | 35 | if (!statusCheckResult || !contentCheckResult && expectedContent) { 36 | if (printOnError && response.body) { 37 | console.log("Unexpected response: " + response.body); 38 | } 39 | if (failOnError) { 40 | if (!statusCheckResult && (!contentCheckResult && expectedContent)) { 41 | fail(`${response.request.method} ${url} status ${expectedStatus} and "${expectedContent}" not found in response`); 42 | } else { 43 | if (!statusCheckResult) { 44 | fail(`Received unexpected status code ${response.status} for URL: ${url}, expected ${expectedStatus}`); 45 | } else if (!contentCheckResult) { 46 | fail(`"${expectedContent}" not found in response for URL: ${url}`); 47 | } 48 | } 49 | } 50 | } 51 | } 52 | 53 | function isEmpty(str) { 54 | return (!str || str.length === 0); 55 | } 56 | --------------------------------------------------------------------------------