Giter Site home page Giter Site logo

elaichenkov / odottaa Goto Github PK

View Code? Open in Web Editor NEW
34.0 2.0 1.0 1.75 MB

๐Ÿฆฅ Custom playwright matchers to test the state of the API response

License: MIT License

TypeScript 84.70% JavaScript 15.30%
typescript playwright e2e testing assertions expect api

odottaa's Introduction

Stand With Ukraine

odottaa

sloth

Custom playwright matchers to test the state of the API response


test NPM version monthly downloads downloads all time commits MIT licensed

Table of Contents

Installation

This module is distributed via npm which is bundled with node and should be installed as one of your project's devDependencies:

npm i -D odottaa

Usage

TypeScript

  1. Import odottaa module
  2. Extend expect with custom API matchers
// 1. In your playwright.config.ts
import { expect } from '@playwright/test';
import playwrightApiMatchers from 'odottaa';

// 2. extend expect with custom API matchers
expect.extend(playwrightApiMatchers);

JavaScript

// 1. In your playwright.config.js
const { expect } = require('@playwright/test');
const { default: playwrightApiMatchers } = require('odottaa');

// 2. extend expect with custom API matchers
expect.extend(playwrightApiMatchers);

API

toHaveStatusCode

Use toHaveStatusCode matcher to verify that the response's status code is equal to the expected status code

const response = await request.get('https://example.com/');

await expect(response).toHaveStatusCode(201);

toHaveStatusText

Use toHaveStatusText matcher to verify that the response' status text is equal to the expected status text

const response = await request.get('https://example.com/404');

await expect(response).toHaveStatusText('Not Found');

toBeCreated

Use toBeCreated matcher to verify that the response's status code is 201

const response = await request.post('https://example.com/create');

await expect(response).toBeCreated();

toBeUnauthorized

Use toBeUnauthorized matcher to verify that the response's status code is 401

const response = await request.post('https://example.com/create');

await expect(response).toBeUnauthorized();

toBeForbidden

Use toBeForbidden matcher to verify that the response's status code is 403

const response = await request.post('https://example.com/create');

await expect(response).toBeForbidden();

toBeNotFound

Use toBeNotFound matcher to verify that the response's status code is 404

const response = await request.post('https://example.com/list');

await expect(response).toBeNotFound();

toHaveJSON

Use toHaveJSON matcher to verify that the response's body json is equal to the all properties of object instances (also known as "deep" equality)

const response = await request.get('https://example.com/data.json');
// e.g. response { name: 'Ben', age: 37 }

await expect(response).toHaveJSON({
  name: 'Ben',
  age: 37
});

toContainJSON

Use 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.

const response = await request.get('https://example.com/data.json');
// e.g. response [{ name: 'Ben', age: 37 }, { name: 'Anna', age: 26 }]

await expect(response).toContainJSON({
  name: 'Ben',
  age: 37
});

toMatchJSON

Use 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.

const response = await request.get('https://example.com/data.json');
// e.g. response [{ name: 'Ben', age: 37 }, { name: 'Anna', age: 26 }]

await expect(response).toMatchJSON({
  name: 'John Doe',
});

toHaveHeader

Use toHaveHeader matcher to verify that the response's headers contains the expected header and value

const response = await request.get('https://example.com');

// Asserts that the response's headers contains the header 'content-length'
await expect(response).toHaveHeader('content-length');

// Asserts that the response's headers contains the header 'content-length' with value '22'
await expect(response).toHaveHeader('content-length', '22');

toHaveHeaders

Use toHaveHeaders matcher to verify that the response's headers contains the expected header

const response = await request.get('https://example.com');

// Single
await expect(response).toHaveHeaders({ 'content-length': '22' });

// Multiple
await expect(response).toHaveHeaders({ 'content-type': 'text/html', 'content-length': '22' });

toHaveContentType

Use toHaveContentType matcher to verify that the response' headers content type is equal to the expected type

const response = await request.get('https://example.com/');

await expect(response).toHaveContentType('text/html');

toContainTextContent

Use toContainTextContent matcher to verify that the response' body text contains the expected text

const response = await request.get('https://example.com/');

await expect(response).toContainTextContent('Hello, World!');

toHaveLocation

Use toHaveLocation matcher to verify that the response' headers location is equal to the expected location

const response = await request.get('https://example.com/');

await expect(response).toHaveLocation('/home');

toBeRedirected

Use toBeRedirected matcher to verify that the response' url is being redirected to the expected url

const response = await request.get('https://example.com/user/profile');

await expect(response).toBeRedirected('https://example.com/auth/login');

Author

Yevhen Laichenkov [email protected]

License

MIT

odottaa's People

Contributors

dependabot[bot] avatar elaichenkov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

afanasievkirill

odottaa's Issues

Cant use toHaveHeaders for single header + value verification

Steps to reproduce
const response: APIResponse = await request.post(https://reqres.in/api/users, {
data:
{
"name": "morpheus",
"job": "leader"
},
failOnStatusCode: true,
});

await expect(response).toHaveHeaders({'Connection': 'close'});

Actual result
fails with

  • Expected - 1
  • Received + 14

    Object {

  • "Connection": "close",
  • "access-control-allow-origin": "*",
  • "cf-cache-status": "DYNAMIC", .... }

Expected result
toHaveHeaders can be used to verify part of headers object, not all object.

or to add new matcher that will verify part of the headers object.

odottaa has no default export

Recently working with Playwright and saw that odottaa adds some helpful assertions. So I decided I'd give it a try and see if I liked it.

After installing and following the instructions to add it to my playwright.config.ts.

import { defineConfig, devices, expect } from '@playwright/test';
import playwrightApiMatchers from 'odottaa';

My IDE Is warning me about this. TS1192: Module '"/node_modules/odottaa/lib/src/index"' has no default export.

Doesn't seem to cause an issue, but would be nice if this didn't show as an issue on that file.

Unable to use odottaa in Es6 project

When I try using odotta in a test, I run into the following error:

TypeError: expect.extend: `default` is not a valid matcher. Must be a function, is "object"

I noticed a similar issue had been created, so I tried the suggested change, but it didn't work. I've also tried various import styles, but I can't find one that works. I'm still learning my way around playwright with eslint and typescript, so I may have overlooked something.

Project code: odottaa-example.zip

api.spec.js

import {expect, test} from "@playwright/test"
import playwrightApiMatchers from 'odottaa'

const API_URL = 'https://swapi.dev/api/people/1'

test.describe("Test API asserts", () => {
    test('with odottaa', async ({context}) => {
        let response = await context.request.get(API_URL)
        expect.extend(playwrightApiMatchers)
        await expect(response).toBeOK()
        await expect(response.json()).toMatchJSON({name: "Luke Skywalker"})
    })
})

package.json

{
  "name": "odottaa-example",
  "version": "1.0.0",
  "description": "",
  "type": "module",
  "main": "index.js",
  "scripts": {},
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@playwright/test": "^1.43.1",
    "@types/node": "^20.12.8"
  },
  "dependencies": {
    "eslint": "^9.1.1",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-import": "^2.29.1",
    "eslint-plugin-playwright": "^1.6.0",
    "eslint-plugin-prettier": "^5.1.3",
    "odottaa": "^1.1.19",
    "prettier": "^3.2.5"
  }
}

jsconfig.json

{
  "compilerOptions": {
    "target": "ES6",
    "module": "node16",
    "moduleResolution": "node16",
    "esModuleInterop": true
  },
  "exclude": [
    "node_modules",
    "playwright.config"
  ]
}

playwright.config.js

// eslint-disable-next-line imported/no-unresolved
import playwright from 'eslint-plugin-playwright'
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
import imported from 'eslint-plugin-import'

export default [
  playwright.configs['flat/recommended'],
  eslintPluginPrettierRecommended,
  {
    files: ['**/*.*js'],
    languageOptions: {
      ecmaVersion: 2024,
      sourceType: 'module',
      globals: {
        process: 'readonly',
      },
    },
    plugins: {
      imported,
    },
    rules: {
      'playwright/no-networkidle': 'off',
      'playwright/no-wait-for-timeout': 'off',
      'playwright/expect-expect': 'error',
      'playwright/no-skipped-test': 'off',
      // 'imported/no-unused-import': 'off',
      // 'imported/prefer-default-export': 'off',
      // 'imported/no-default-export': 'off',
      // 'imported/no-unresolved': 'error',
    },
  },
  {
    ignores: ['node_modules/', '/playwright/.cache/', '/playwright-report/', 'src/memfs/assets/*', '/test-results/'],
  },
]

Assert schemas with the union type and optional properties?

Hi! ๐Ÿ‘‹
Thx for sharing this package and describing the use case in the article. ๐Ÿฅ‡

I'm playing around with Playwright since few weeks and the tool looks ๐Ÿš€ Right now I'm researching options for API testing. So far so good. This morning I hit following case:

I have a local server running with some dummy API. There is a GET endpoint /my/guitars - no auth required. The endpoint returns an Array of guitars where a single object from an array has a schema that can be represented with the following types :

export type Condition = 'New' | 'As good as new' | 'Good' | 'Used' | 'Damaged';

export type Guitar = {
 guitar: {
   manufacturer: string;
   model: string;
   buildYear: number;
   signedByFamousGuitarist: boolean;
   signedBy?: string | null; // optional parameter, once present can be string or null 
   condition: {
     keys: Condition;
     fretboard: Condition;
     front: Condition;
     back: Condition;
   };
 };
};

I've written example API test where I'm trying to assert the schema using methods provided in this package. I'm using toContainJSON () for the assertion. To have more generic object for the assertion I'm using expect.any() for all of the properties.

Here is the test:

import { expect, test } from "@playwright/test";

test.describe("/get my guitars endpoint", () => {
  test("fetches all guitars and check schema", async ({ request }) => {
    const guitarSchema = {
      guitar: {
        manufacturer: expect.any(String),
        model: expect.any(String),
        buildYear: expect.any(Number),
        signedByFamousGuitarist: expect.any(Boolean),
        signedBy: null,
        condition: {
          keys: expect.any(String),
          fretboard: expect.any(String),
          front: expect.any(String),
          back: expect.any(String),
        },
      },
    };

    const response = await request.get("/my/guitars");

    expect(response).toHaveStatusCode(200);
    expect(response).toContainJSON(guitarSchema);
  });
});

The overall goal of the test would be to check if the API returns the object matching defined schema.

This test passed when returned response included optional parameter signedBy with value null:

[
  {
    "guitar": {
      "buildYear": 2017,
      "condition": {
        "back": "As good as new",
        "fretboard": "As good as new",
        "front": "As good as new",
        "keys": "As good as new"
      },
      "manufacturer": "Maton",
      "model": "EBG808C W.A.",
      "signedBy": null,
      "signedByFamousGuitarist": false
    }
  }
]

With such object-schema in place (const guitarSchema) the are two scenarios when the test fails:

  1. When signedBy value is a String
  2. When signedBy is not present in the response

I was trying to change the const guitarSchema definition and modify it to be able to accept both null and string values. I've tried this code:

 signedBy: null || expect.any(String)

and

 signedBy: expect.any(String || null)

None of this works.

In fact this could only cover scenario where the parameter is either null or String. I guess allowing that would be sufficient for testing most of the API system. This is quite typical backend/API flow:

  1. entity is created in the database with some params,
  2. initial value of param a is set to null,
  3. entity is returned via API as an object with param a: null
  4. entity is modified (by the system or the user)
  5. entity is returned in the API as an object with param a being string, number etc.

There might be also system which is not returning the param if the value is null.

Therefor I have 2 questions:

  1. Is there any way to mark property in the object-schema ( const guitarSchema ) that allows more than one type with expect.any() or other syntax ?
  2. Is there a way I could mark the signedBy param optional within the object-schema ( const guitarSchema ) definition?

ร would appreciate any suggestions. Thx!

Not working with Playwright 1.30

After updating Playwright from 1.29.2 to 1.30, I'm getting errors on first 'oddotta' function encounter i.e. toHaveJSON()

TypeError: this.customTesters is not iterable

at Object.toEqual (/node_modules/expect/build/matchers.js:752:15)
at Object.toHaveJSON (/node_modules/odottaa/lib/src/index.js:48:99)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.