{"id":16175917,"url":"https://github.com/elaichenkov/odottaa","last_synced_at":"2025-03-16T10:31:19.530Z","repository":{"id":37856219,"uuid":"476451021","full_name":"elaichenkov/odottaa","owner":"elaichenkov","description":"🦥 Custom playwright matchers to test the state of the API response","archived":false,"fork":false,"pushed_at":"2023-12-19T17:12:09.000Z","size":3454,"stargazers_count":32,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2023-12-20T12:10:35.918Z","etag":null,"topics":["api","assertions","e2e","expect","playwright","testing","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elaichenkov.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":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-03-31T19:33:44.000Z","updated_at":"2023-12-25T14:08:01.761Z","dependencies_parsed_at":"2023-10-10T14:24:15.258Z","dependency_job_id":"2b659c2e-ab98-4531-827d-c05cf39b986c","html_url":"https://github.com/elaichenkov/odottaa","commit_stats":{"total_commits":235,"total_committers":2,"mean_commits":117.5,"dds":0.2638297872340426,"last_synced_commit":"f0b3a9e4e01bfee876d6ea0d998a0d2499af7da1"},"previous_names":[],"tags_count":23,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elaichenkov%2Fodottaa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elaichenkov%2Fodottaa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elaichenkov%2Fodottaa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elaichenkov%2Fodottaa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elaichenkov","download_url":"https://codeload.github.com/elaichenkov/odottaa/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221662736,"owners_count":16859737,"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","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":["api","assertions","e2e","expect","playwright","testing","typescript"],"created_at":"2024-10-10T04:46:47.721Z","updated_at":"2024-10-27T10:25:31.946Z","avatar_url":"https://github.com/elaichenkov.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct-single.svg)](https://vshymanskyy.github.io/StandWithUkraine)\n\n\u003cdiv align=\"center\"\u003e\n\u003ch1 align=\"center\"\u003eodottaa\u003c/h1\u003e\n\n\u003ca href=\"https://www.joypixels.com/profiles/emoji/1f9a5\"\u003e\n  \u003cimg\n    height=\"80\"\n    width=\"80\"\n    alt=\"sloth\"\n    src=\"./assets/sloth.png\"\n  /\u003e\n\u003c/a\u003e\n\n\u003cp\u003eCustom playwright matchers to test the state of the API response\u003c/p\u003e\n\n\u003c/div\u003e\n\n---\n[![test](https://github.com/elaichenkov/odottaa/actions/workflows/tests.yml/badge.svg)](https://github.com/elaichenkov/odottaa/actions/workflows/tests.yml)\n[![NPM version](https://img.shields.io/npm/v/odottaa.svg?style=flat\u0026color=red)](https://www.npmjs.com/package/odottaa)\n[![monthly downloads](https://img.shields.io/npm/dm/odottaa.svg?style=flat\u0026color=orange\u0026label=monthly%20downloads)](https://www.npmjs.com/package/odottaa)\n[![downloads all time](https://img.shields.io/npm/dt/odottaa.svg?style=flat\u0026color=yellow\u0026label=lifetime%20downloads)](https://www.npmjs.com/package/odottaa)\n[![commits](https://img.shields.io/github/commit-activity/y/elaichenkov/playwright-expect.svg?style=flat\u0026color=purple)](https://github.com/elaichenkov/odottaa/commits/main)\n[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg?style=flat\u0026color=blue)](LICENSE)\n\n## Table of Contents\n\n- [Table of Contents](#table-of-contents)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [TypeScript](#typescript)\n  - [JavaScript](#javascript)\n- [API](#api)\n  - [toHaveStatusCode](#tohavestatuscode)\n  - [toHaveStatusText](#tohavestatustext)\n  - [toBeCreated](#tobecreated)\n  - [toBeUnauthorized](#tobeunauthorized)\n  - [toBeForbidden](#tobeforbidden)\n  - [toBeNotFound](#tobenotfound)\n  - [toHaveJSON](#tohavejson)\n  - [toContainJSON](#tocontainjson)\n  - [toMatchJSON](#tomatchjson)\n  - [toHaveHeader](#tohaveheader)\n  - [toHaveHeaders](#tohaveheaders)\n  - [toHaveContentType](#tohavecontenttype)\n  - [toContainTextContent](#tocontaintextcontent)\n  - [toHaveLocation](#tohavelocation)\n  - [toBeRedirected](#toberedirected)\n- [Author](#author)\n- [License](#license)\n\n## Installation\n\nThis module is distributed via [npm](https://npmjs.com/) which is bundled with [node](https://nodejs.org/) and\nshould be installed as one of your project's `devDependencies`:\n\n```bash\nnpm i -D odottaa\n```\n\n## Usage\n\n### TypeScript\n1. Import `odottaa` module\n2. Extend expect with custom API matchers\n\n```typescript\n// 1. In your playwright.config.ts\nimport { expect } from '@playwright/test';\nimport playwrightApiMatchers from 'odottaa';\n\n// 2. extend expect with custom API matchers\nexpect.extend(playwrightApiMatchers);\n```\n\n### JavaScript\n\n```javascript\n// 1. In your playwright.config.js\nconst { expect } = require('@playwright/test');\nconst { default: playwrightApiMatchers } = require('odottaa');\n\n// 2. extend expect with custom API matchers\nexpect.extend(playwrightApiMatchers);\n```\n\n## API\n\n### toHaveStatusCode\n\nUse `toHaveStatusCode` matcher to verify that the response's status code is equal to the expected status code\n\n```typescript\nconst response = await request.get('https://example.com/');\n\nawait expect(response).toHaveStatusCode(201);\n```\n\n### toHaveStatusText\n\nUse `toHaveStatusText` matcher to verify that the response' status text is equal to the expected status text\n\n```typescript\nconst response = await request.get('https://example.com/404');\n\nawait expect(response).toHaveStatusText('Not Found');\n```\n\n### toBeCreated\n\nUse `toBeCreated` matcher to verify that the response's status code is 201\n\n```typescript\nconst response = await request.post('https://example.com/create');\n\nawait expect(response).toBeCreated();\n```\n\n### toBeUnauthorized\n\nUse `toBeUnauthorized` matcher to verify that the response's status code is 401\n\n```typescript\nconst response = await request.post('https://example.com/create');\n\nawait expect(response).toBeUnauthorized();\n```\n\n### toBeForbidden\n\nUse `toBeForbidden` matcher to verify that the response's status code is 403\n\n```typescript\nconst response = await request.post('https://example.com/create');\n\nawait expect(response).toBeForbidden();\n```\n\n### toBeNotFound\n\nUse `toBeNotFound` matcher to verify that the response's status code is 404\n\n```typescript\nconst response = await request.post('https://example.com/list');\n\nawait expect(response).toBeNotFound();\n```\n\n### toHaveJSON\n\nUse `toHaveJSON` matcher to verify that the response's body json is equal to the all properties of object instances (also known as \"deep\" equality)\n\n```typescript\nconst response = await request.get('https://example.com/data.json');\n// e.g. response { name: 'Ben', age: 37 }\n\nawait expect(response).toHaveJSON({\n  name: 'Ben',\n  age: 37\n});\n```\n\n### toContainJSON\n\nUse `toContainJSON` matcher to verify that the response's body array contains that an item with a specific structure and values is contained in an array.\n\n```typescript\nconst response = await request.get('https://example.com/data.json');\n// e.g. response [{ name: 'Ben', age: 37 }, { name: 'Anna', age: 26 }]\n\nawait expect(response).toContainJSON({\n  name: 'Ben',\n  age: 37\n});\n```\n\n### toMatchJSON\n\nUse `toMatchJSON` matcher to verify that the response's body json matches a subset of the properties of an object. It'll match received objects with properties that are not in the expected object.\n\n```typescript\nconst response = await request.get('https://example.com/data.json');\n// e.g. response [{ name: 'Ben', age: 37 }, { name: 'Anna', age: 26 }]\n\nawait expect(response).toMatchJSON({\n  name: 'John Doe',\n});\n```\n\n### toHaveHeader\n\nUse `toHaveHeader` matcher to verify that the response's headers contains the expected header and value\n\n```typescript\nconst response = await request.get('https://example.com');\n\n// Asserts that the response's headers contains the header 'content-length'\nawait expect(response).toHaveHeader('content-length');\n\n// Asserts that the response's headers contains the header 'content-length' with value '22'\nawait expect(response).toHaveHeader('content-length', '22');\n```\n\n### toHaveHeaders\n\nUse `toHaveHeaders` matcher to verify that the response's headers contains the expected header\n\n```typescript\nconst response = await request.get('https://example.com');\n\n// Single\nawait expect(response).toHaveHeaders({ 'content-length': '22' });\n\n// Multiple\nawait expect(response).toHaveHeaders({ 'content-type': 'text/html', 'content-length': '22' });\n```\n\n### toHaveContentType\n\nUse `toHaveContentType` matcher to verify that the response' headers content type is equal to the expected type\n\n```typescript\nconst response = await request.get('https://example.com/');\n\nawait expect(response).toHaveContentType('text/html');\n```\n\n### toContainTextContent\n\nUse `toContainTextContent` matcher to verify that the response' body text contains the expected text\n\n```typescript\nconst response = await request.get('https://example.com/');\n\nawait expect(response).toContainTextContent('Hello, World!');\n```\n\n### toHaveLocation\n\nUse `toHaveLocation` matcher to verify that the response' headers location is equal to the expected location\n\n```typescript\nconst response = await request.get('https://example.com/');\n\nawait expect(response).toHaveLocation('/home');\n```\n\n### toBeRedirected\n\nUse `toBeRedirected` matcher to verify that the response' url is being redirected to the expected url\n\n```typescript\nconst response = await request.get('https://example.com/user/profile');\n\nawait expect(response).toBeRedirected('https://example.com/auth/login');\n```\n\n## Author\n\nYevhen Laichenkov \u003celaichenkov@gmail.com\u003e\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felaichenkov%2Fodottaa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felaichenkov%2Fodottaa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felaichenkov%2Fodottaa/lists"}