{"id":24831622,"url":"https://github.com/grafana/k6-jslib-testing","last_synced_at":"2025-10-14T05:30:59.600Z","repository":{"id":275030858,"uuid":"923073265","full_name":"grafana/k6-jslib-testing","owner":"grafana","description":"Playwright-compatible functional testing library for k6","archived":false,"fork":false,"pushed_at":"2025-10-02T09:15:53.000Z","size":185,"stargazers_count":13,"open_issues_count":15,"forks_count":1,"subscribers_count":100,"default_branch":"main","last_synced_at":"2025-10-02T11:25:00.971Z","etag":null,"topics":["deno","functional-testing","jslib","k6","playwright","typescript"],"latest_commit_sha":null,"homepage":"https://jslib.k6.io/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/grafana.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-01-27T15:46:31.000Z","updated_at":"2025-09-12T09:00:03.000Z","dependencies_parsed_at":"2025-01-30T20:15:15.202Z","dependency_job_id":"222faddc-2618-49ab-8a75-8c02224b671d","html_url":"https://github.com/grafana/k6-jslib-testing","commit_stats":null,"previous_names":["grafana/k6-jslib-testing"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/grafana/k6-jslib-testing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grafana%2Fk6-jslib-testing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grafana%2Fk6-jslib-testing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grafana%2Fk6-jslib-testing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grafana%2Fk6-jslib-testing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/grafana","download_url":"https://codeload.github.com/grafana/k6-jslib-testing/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grafana%2Fk6-jslib-testing/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279018011,"owners_count":26086235,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deno","functional-testing","jslib","k6","playwright","typescript"],"created_at":"2025-01-31T00:35:32.349Z","updated_at":"2025-10-14T05:30:59.594Z","avatar_url":"https://github.com/grafana.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# k6-testing\n\nA seamless way to write functional tests in k6 with Playwright-compatible\nassertions.\n\n\u003e ⚠️ **Note**: This project is under active development. While it is functional,\n\u003e it is not yet ready for production use, expect bugs, and potential breaking\n\u003e changes.\n\n## Why k6-testing?\n\n- **✨ Write once, run anywhere**: Copy-paste your Playwright test assertions\n  directly into k6 - they'll work out of the box\n- **🎯 Fail fast**: Tests interrupt immediately when assertions fail, giving you\n  quick, clear feedback\n- **🎭 Familiar API**: Familiar API for anyone coming from Playwright, Deno, or\n  Vite ecosystem\n- **🔍 Clear error messages**: Get detailed, actionable feedback when tests fail\n\n## Installation\n\nk6-testing is available as a [k6 jslib](https://jslib.k6.io). It can be directly\nimported as a dependency in your k6 script.\n\n```sh\nimport { expect } from \"https://jslib.k6.io/k6-testing/0.3.0/index.js\";\n```\n\n## Quick Start\n\nThe following example demonstrates how to use k6-testing in a k6 script.\n\nThe module exposes the `expect` function, which behaves in a similar way to\n[Playwright's `expect` function](https://playwright.dev/docs/test-assertions).\n\nTo make an assertion, call `expect(value)` and choose a matcher that reflects\nthe expectation.\n\n```javascript\nimport { browser } from \"k6/browser\";\nimport http from \"k6/http\";\n\nimport { expect } from \"https://jslib.k6.io/k6-testing/0.3.0/index.js\";\n\nexport const options = {\n  scenarios: {\n    // Protocol tests\n    protocol: {\n      executor: \"shared-iterations\",\n      vus: 1,\n      iterations: 1,\n      exec: \"protocol\",\n    },\n\n    // Browser tests\n    ui: {\n      executor: \"shared-iterations\",\n      options: {\n        browser: {\n          type: \"chromium\",\n        },\n      },\n      exec: \"ui\",\n    },\n  },\n};\n\nexport function protocol() {\n  // Get the home page of k6's Quick Pizza app\n  const response = http.get(\"https://quickpizza.grafana.com/\");\n\n  // Simple assertions\n  expect(response.status).toBe(200);\n}\n\nexport async function ui() {\n  const page = await browser.newPage();\n\n  try {\n    await page.goto(\"https://quickpizza.grafana.com/\");\n    await page.waitForLoadState(\"networkidle\"); // waits until the `networkidle` event\n\n    // Assert the \"Pizza Please\" button is visible\n    await expect(page.locator(\"button[name=pizza-please]\")).toBeVisible();\n  } finally {\n    await page.close();\n  }\n}\n```\n\nFor functional testing, metrics and performance are most likely irrelevant, and\nwe recommend executing k6 functional tests in headless mode:\n\n```sh\n# Run k6 in headless mode\nk6 run --no-summary --quiet examples/browser.js\n\n# If any assertion/expectation fail, a non-zero exit code will be returned\necho $status\n```\n\n## Features\n\n### 1. Playwright-Compatible Expectations\n\nUse the same assertions you know from Playwright:\n\n```javascript\n// These Playwright assertions work exactly the same in k6\nawait expect(page.locator(\".button\")).toBeVisible();\nawait expect(page.locator(\"input\")).toHaveValue(\"test\");\nawait expect(page).toHaveTitle(\"My Page Title\");\n```\n\n### 2. Auto-Retrying Assertions\n\nPerfect for UI testing, these assertions will retry until the assertion passes,\nor the assertion timeout is reached. Note that retrying assertions are async, so\nyou must await them.\n\nBy default, the timeout for assertions is set to 5 seconds, and the polling\ninterval is set to 100 milliseconds.\n\n**Element Assertions (for Locators):**\n\n| Assertion                            | Description                                           |\n| ------------------------------------ | ----------------------------------------------------- |\n| `toBeChecked(opts?)`                 | Element is checked                                    |\n| `toBeDisabled(opts?)`                | Element is disabled                                   |\n| `toBeEditable(opts?)`                | Element is editable                                   |\n| `toBeEmpty(opts?)`                   | Element is empty                                      |\n| `toBeEnabled(opts?)`                 | Element is enabled                                    |\n| `toBeHidden(opts?)`                  | Element is hidden                                     |\n| `toBeVisible(opts?)`                 | Element is visible                                    |\n| `toContainText(text, opts?)`         | Element contains text.                                |\n| `toHaveAttribute(attribute, value?)` | Element has specific attribute and, optionally, value |\n| `toHaveText(text, opts?)`            | Element has text.                                     |\n| `toHaveValue(value)`                 | Element has specific value                            |\n\n**Page Assertions (for Pages):**\n\n| Assertion                  | Description                          |\n| -------------------------- | ------------------------------------ |\n| `toHaveTitle(text, opts?)` | Page title matches the expected text |\n\nYou can customize these values by passing an options object as the second\nargument to the assertion function:\n\n```javascript\n// Element assertions on locators\nawait expect(page.locator(\".button\")).toBeVisible({\n  timeout: 10000,\n  interval: 500,\n});\n\n// Page assertions on page objects\nawait expect(page).toHaveTitle(\"Expected Title\", {\n  timeout: 5000,\n});\n```\n\n### 3. Standard Assertions\n\nThese assertions allow to test any conditions, but do not auto-retry.\n\n| Assertion                            | Description                                                                                                                               |\n| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |\n| `toBe(expected)`                     | Strict equality comparison                                                                                                                |\n| `toBeCloseTo(number, precision?)`    | Number comparison with precision                                                                                                          |\n| `toBeDefined()`                      | Asserts a value is defined                                                                                                                |\n| `toBeFalsy()`                        | Falsy value check                                                                                                                         |\n| `toBeGreaterThan(number)`            | Greater than comparison                                                                                                                   |\n| `toBeGreaterThanOrEqual(number)`     | Greater than or equal comparison                                                                                                          |\n| `toBeInstanceOf(expected)`           | Asserts a value is an instance of a class                                                                                                 |\n| `toBeLessThan(number)`               | Less than comparison                                                                                                                      |\n| `toBeLessThanOrEqual(number)`        | Less than or equal comparison                                                                                                             |\n| `toBeNaN()`                          | Asserts a value is NaN                                                                                                                    |\n| `toBeNull()`                         | Asserts a value is null                                                                                                                   |\n| `toBeTruthy()`                       | Asserts a value is truthy                                                                                                                 |\n| `toBeUndefined()`                    | Asserts a value is undefined                                                                                                              |\n| `toContain(expected)`                | When `expected` is a string, asserts the string contains a substring. When `expected` is an Array or Set, asserts it contains an element. |\n| `toContainEqual(expected)`           | Asserts an Array or Set contains a similar element                                                                                        |\n| `toEqual(expected)`                  | Deep equality comparison                                                                                                                  |\n| `toHaveLength(expected)`             | Asserts a value has a length property equal to expected                                                                                   |\n| `toHaveProperty(keyPath, expected?)` | Ensures that property at provided `keyPath` exists in the object and optionally checks that property is equal to `expected`.              |\n\n#### 4. Negating matchers\n\nYou can negate any matcher by adding `.not` before the matcher method. This\ninverts the assertion, checking that the condition is false rather than true:\n\n```javascript\n// Standard assertions\nexpect(response.status).not.toBe(404); // Assert status is NOT 404\nexpect(response.json().items).not.toBeEmpty(); // Assert items array is not empty\nexpect(user.permissions).not.toContain(\"admin\"); // Assert user doesn't have admin permission\n\n// Retrying assertions (must be awaited)\nawait expect(page.locator(\".error-message\")).not.toBeVisible(); // Assert error is not shown\nawait expect(page.locator('button[type=\"submit\"]')).not.toBeDisabled(); // Assert button is not disabled\n```\n\nNegation is particularly useful in k6 testing scenarios such as:\n\n- Verifying error conditions aren't present:\n  `await expect(page.locator('.error')).not.toBeVisible()`\n- Ensuring unauthorized access is blocked:\n  `expect(response.status).not.toBe(200)`\n- Confirming elements are removed after an action:\n  `await expect(page.locator('#item-1')).not.toBeVisible()`\n\n**Note:** When using negated retrying assertions, the assertion will keep\nretrying until the condition becomes false or the timeout is reached. For\nexample, `await expect(locator).not.toBeVisible()` will pass immediately if the\nelement is hidden, but will retry until timeout if the element is visible,\nhoping it will disappear.\n\n#### 5. Soft assertions\n\nBy default, failed assertions will terminate the test execution. The k6 testing\nlibrary also supports _soft assertions_: failed soft assertions **do not**\nterminate the test execution, but mark the test as failed, leading k6 to\neventually exit with code `110`.\n\n```javascript\nimport exec from \"k6/execution\";\nimport { expect } from \"https://jslib.k6.io/k6-testing/0.4.0/index.js\";\n\nexport const options = {\n  vus: 2,\n  iterations: 10,\n};\n\nexport default function () {\n  // Iteration 3 will mark the test as failed, but the test execution\n  // will keep going until its end condition, and eventually exit with\n  // code 110.\n  if (exec.scenario.iterationInInstance === 3) {\n    expect.soft(false).toBeTruthy();\n  }\n}\n```\n\nNote that soft assertions can be\n[configured to throw an exception](#6-configuration), and effectively failing\nthe iteration where it happens instead.\n\n#### 5. Custom expect messages\n\nWhen writing tests, clear and informative error messages can significantly speed\nup debugging. You can specify a custom error message as the second argument to\nthe expect function. This message will be displayed whenever the assertion\nfails, providing additional context about the failure.\n\n**Example:**\n\n```javascript\nexpect(value, \"Custom message\").toHaveProperty(\"a.b[0]\", 43);\n```\n\nIf this assertion fails, the error message will clearly indicate the issue along\nwith your custom message:\n\n```\n                     Error: Custom message\n                        At: /Users/me/myProject/expectNonRetrying.ts:555:15\n\n             Property path: a.b[0]\nExpected property to equal: 43\n           Received object: {\"a\":{\"b\":[42]},\"c\":true}\n\n                  Filename: expectNonRetrying.ts\n                      Line: 555\n```\n\n#### 6. Configuration\n\nYou can create a new expect instance with the `.configure` method. This will\nallow you to configure the behavior of the assertions. The configuration is\napplied to all assertions made using the expect instance.\n\n##### Available configuration options\n\nThe available configuration options are:\n\n| Option     | Default  | Environment variable   | Description                                                                                                                                       |\n| ---------- | -------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `colorize` | true     | `K6_TESTING_COLORIZE`  | Whether to colorize the output of the expect function.                                                                                            |\n| `display`  | \"pretty\" | `K6_TESTING_DISPLAY`   | The display format to use. \"pretty\" (default) or \"inline\".                                                                                        |\n| `timeout`  | 5000     | `K6_TESTING_TIMEOUT`   | Specific to retrying assertions. The timeout for assertions, in milliseconds.                                                                     |\n| `interval` | 100      | `K6_TESTING_INTERVAL`  | Specific to retrying assertions. The polling interval for assertions, in milliseconds.                                                            |\n| `softMode` | \"fail\"   | `K6_TESTING_SOFT_MODE` | Customize soft assertions behavior: `fail`(default) will mark the test as failed, `throw` will throw an exception and fail the iteration instead. |\n\n##### Example with inline display and no colorization\n\n```javascript\nexport default function () {\n  // Create a new expect instance with the default configuration\n  const myExpect = expect.configure({\n    // Display assertions using an inline format, aimed towards making them more readable in logs\n    display: \"inline\",\n\n    // Disable colorization of the output of the expect function\n    colorize: false,\n  });\n\n  // Use myExpect instead of expect, and it will use the configured display format and colorization\n  await myExpect(true).toBe(false);\n\n  // Note that you're still free to use the default expect instance, and it will not be affected by the configuration\n  expect(true).toBe(false);\n}\n```\n\n##### Example of controlling retrying assertions' timeout and polling interval\n\nYou can configure the default timeout and polling interval for assertions by\ninstantiating a new expect instance with the `.configure` method.\n\n```javascript\nexport default function () {\n  const myExpect = new expect.configure({ timeout: 10000, interval: 500 });\n\n  // Use myExpect instead of expect, and it will use the configured timeout and interval\n  // for all assertions.\n  //\n  // In this specific case, the assertion will retry until the button is visible, or the timeout is reached: every\n  // 500ms, for a maximum of 10 seconds.\n  await myExpect(page.locator(\".button\")).toBeVisible();\n}\n```\n\n## Examples\n\n### API Testing\n\n```javascript\nimport { expect } from \"https://jslib.k6.io/k6-testing/0.3.0/index.js\";\nimport http from \"k6/http\";\n\nexport function setup() {\n  // Ensure the API is up and running before running the tests\n  // If the response is not 200, the test will fail immediately with\n  // a non-zero exit code, display a user-friendly message, and stop the test.\n  const response = http.get(\"https://api.example.com/health\");\n  expect(response.status).toBe(200);\n}\n\nexport default function () {\n  const response = http.get(\"https://api.example.com/users\");\n  expect(response.status).toBe(200);\n\n  const json = response.json();\n  expect(json.users).toBeDefined();\n  expect(json.users).toBeInstanceOf(Array);\n  expect(json.users[0].id).toBeGreaterThan(0);\n}\n```\n\n### UI Testing\n\n```javascript\nimport { expect } from \"https://jslib.k6.io/k6-testing/0.3.0/index.js\";\nimport { browser } from \"k6/browser\";\n\nexport const options = {\n  scenarios: {\n    ui: {\n      executor: \"shared-iterations\",\n      options: {\n        browser: {\n          type: \"chromium\",\n        },\n      },\n    },\n  },\n};\n\nexport default async function () {\n  const page = await browser.newPage();\n\n  try {\n    // Navigate to the page\n    await page.goto(\"https://test.k6.io/my_messages.php\");\n\n    // Type into the login input field: 'testlogin'\n    const loc = await page.locator('input[name=\"login\"]');\n    await loc.type(\"testlogin\");\n\n    // Assert that the login input field is visible\n    await expect(page.locator('input[name=\"login\"]')).toBeVisible();\n\n    // Expecting this to fail as we have typed 'testlogin' into the input instead of 'foo'\n    await expect(page.locator('input[name=\"login\"]')).toHaveValue(\"foo\");\n  } finally {\n    await page.close();\n  }\n}\n```\n\n## Contributing\n\nWe welcome contributions! Here's how you can help:\n\n1. **Report Issues**: File bugs or feature requests on our GitHub issues page\n2. **Submit PRs**: Code contributions are welcome\n3. **Improve Docs**: Documentation improvements are always valuable\n\n### Development Setup\n\nThe project supports development using\n[Dev Containers](https://containers.dev/), which provides a consistent,\npre-configured development environment with all necessary tools installed. This\nis the recommended way to develop k6-testing.\n\nTo use the Dev Container:\n\n1. Ensure you have Docker installed\n2. Use a Dev Containers compatible editor:\n   - VS Code with the \"Remote - Containers\" extension\n   - JetBrains IDEs with the \"Remote Development\" plugin\n   - Any other editor that supports Dev Containers\n3. Open the project in your editor - it should automatically detect the Dev\n   Container configuration and prompt you to reopen in container\n\nThe Dev Container comes with:\n\n- Deno\n- k6\n- chromium (for browser testing)\n\n### Development Workflow\n\nk6-testing is built with [Deno](https://deno.land), and\n[esbuild](https://esbuild.github.io/). Deno is used for the development of the\nlibrary itself, as well as unit testing, and the output distributable files are\nbuilt with esbuild.\n\nThe following commands are used throughout the development process:\n\n- `deno task build` - Build the distributable files\n- `deno task release` - Build the distributable files in release mode\n- `deno test` - Run unit tests\n- `deno task test` - Run integration tests\n- `deno lint *.ts` - Report linting errors\n- `deno fmt *.ts` - Format the code\n\nThe following files are must known when working on the project:\n\n- `mod.ts` - The main entry point for the expect library, defines the public API\n- `expect.ts` - The main entry point for the expect library\n- `expectNonRetrying.ts` - Contains the non-retrying assertions definition and\n  implementation\n- `expectRetrying.ts` - Contains the retrying assertions definition and\n  implementation\n- The `tests/` directory contains the integration tests for the expect library\n\nDuring development, a typical workflow would consist in the following steps:\n\n1. Make changes to the code\n2. Run `deno fmt *.ts` to format the code\n3. Run `deno lint *.ts` to report linting errors\n4. Run `deno task build` to (verify) build the code\n5. Run `deno test` to run unit tests\n6. Run `deno task test` to run integration tests\n7. (optional) import `dist/index.js` in a k6 script and run it with\n   `k6 run --no-summary --quiet \u003cscript\u003e.js` to verify that the library works as\n   expected\n\n## License\n\n[Apache 2.0 License](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrafana%2Fk6-jslib-testing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgrafana%2Fk6-jslib-testing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrafana%2Fk6-jslib-testing/lists"}