npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

test-automation-framework

v3.3.2

Published

This Test Automation Framework is a comprehensive solution for implementing automated tests across various domains, including end-to-end, API, and performance testing. It's designed to provide a standardized and scalable approach to testing in multiple pr

Downloads

21

Readme

Test Automation Framework

This Test Automation Framework is a comprehensive solution for implementing automated tests across various domains, including end-to-end, API, and performance testing. It's designed to provide a standardized and scalable approach to testing in multiple projects.

Installation

To install the framework in your project, use npm:

npm install test-automation-framework

Usage

The framework is divided into modules, each targeting different testing needs:


Module 1: End-to-End Testing with Playwright (/e2e)

This module leverages Playwright for end-to-end testing and implements the Page Object Model (POM) for maintainability and readability.

Extending the BasePage

BasePage is the foundational class for creating page-specific classes. Extend BasePage for each page in your application with relevant selectors and methods.

Example: SearchPage

import { Page } from 'playwright';
import { BasePage } from 'test-automation-framework';

export class SearchPage extends BasePage {
    private searchFieldSelector: string;
    
    constructor(page: Page) {
        super(page, '/search');
        this.searchFieldSelector = '#searchbox_input';
    }
    
    async search(searchKeyword: string): Promise<void> {
        await this.page.fill(this.searchFieldSelector, searchKeyword);
        await this.page.keyboard.press('Enter');
    }
}

Example: ResultsPage

import { Page } from 'playwright';
import { BasePage } from 'test-automation-framework';

export class ResultsPage extends BasePage {
    private resultsSelector: string;
    
    constructor(page: Page) {
        super(page, '/results');
        this.resultsSelector = '#results';
    }
    
    getDuckBar(): Locator {
        return this.page.locator(this.duckBarSelector);
    }
}

Using BasePageProvider

BasePageProvider manages multiple page objects, allowing easy access within test cases.

Example Usage of BasePageProvider

import { Browser, Page } from 'playwright';
import { BasePageProvider } from 'test-automation-framework';
import { SearchPage } from './SearchPage';
import { ResultsPage } from './ResultsPage';

export class PageProvider extends BasePageProvider {
    constructor(browser: Browser, page: Page) {
        super(browser, page);
        this.registerPageObject('search', SearchPage);
        this.registerPageObject('results', ResultsPage);
    }

    getSearchPage(): SearchPage {
        return this.getPageObject<SearchPage>('search');
    }

    getResultsPage(): ResultsPage {
        return this.getPageObject<ResultsPage>('results');
    }
}

Example Test Using BasePageProvider

import { test, expect } from '@playwright/test';
import { PageProvider } from '../src/pages/PageProvider';

test.describe('Search', () => {
    let provider: PageProvider;

    test.beforeEach(async ({ browser, page }) => {
        provider = new PageProvider(browser, page);
    });

    test('Should search for \'Potato\' and see relevant results', async () => {
        const searchPage = provider.getSearchPage();
        const resultsPage = provider.getResultsPage();

        await searchPage.navigateToPage();
        await searchPage.search('Potato');
        await expect(ResultsPage.getDuckBar()).toBeVisible()
    });
});

Module 2: API Testing with GraphQL (/api/graphql)

This module provides the necessary tools for testing GraphQL APIs, including a GraphQL client helper for sending requests and handling responses.

First, create a config.ts for the API test configurations:

import { PackageConfig } from 'your-package/config';

export const apiTestsConfig: ApiTestsConfig = {
    graphqlApiUrl: "https://countries.trevorblades.com/",
    // restApiUrl: "another URL for rest API testing?",
    // apiKey: "your-api-key",
};

Then, create a testData directory per test suit/scenario, containing the test data in a file within that directory:

tests/scneario-one/test-example.spec.ts
tests/scenario-one/testData/test-example.testdata.ts

test-example.testdata.ts would contain and export the needed test data for your tests:

export const sampleQueries = {
    getCountries: `
        query {
        countries {
            code
            name
        }
        }
    `,
    getCountry: `
        query getCountry($code: ID!) {
        country(code: $code) {
            code
            name
            native
            capital
            currency
        }
        }
    `,
    invalidQuery: `query { invalidField }`,
};
  
export const sampleVariables = {
    countryVariables: { code: 'BR' },
};
  

You would also need a types directory that contains both the types needs by your tests, as well as the schema to compare and assert the API responses against. As a result, the structure of your tests would look like below:

├───config.ts
│
├───example.spec
│   │   GraphQLCountryTest.spec.ts
│   │
│   └───testData
│           GraphQLTestExample.testdata.ts
│
└───types
        graphqlTypes.ts
        schemas.ts

Finally, end this is how your test would look like:

import { expect } from 'chai';
import { GraphQLClientHelper } from 'test-automation-framework';
import { sampleQueries, sampleVariables } from './testData/GraphQLTestExample.testdata';
import { graphqlTestsConfig } from '../config';
import { CountriesData, CountryData } from '../types/graphqlTypes';
import {
    countriesResponseSchema,
    countryResponseSchema,
  } from '../types/schemas';

describe('GraphQL Country API Tests', () => {
  const client = new GraphQLClientHelper(graphqlTestsConfig.apiUrl);

  before(() => {
    client.setHeaders({ Authorization: `Bearer ${graphqlTestsConfig.apiKey}` });
  });

  it("should fetch countries data correctly", async () => {
    const data = await client.sendQuery<CountriesData>(
      sampleQueries.getCountries,
      undefined,
      countriesResponseSchema,
    );
    expect(data.countries).to.be.an("array");
    expect(data.countries.length).to.be.greaterThan(0);
  });

  it("should fetch a specific country", async () => {
    const data = await client.sendQuery<CountryData>(
      sampleQueries.getCountry,
      sampleVariables.countryVariables,
      countryResponseSchema,
    );
    expect(data.country).to.be.an("object");
    expect(data.country.name).to.equal("Brazil");
  });

  it("should handle errors gracefully", async () => {
    try {
      await client.sendQuery(sampleQueries.invalidQuery, undefined, null); // No schema needed for invalid queries
      throw new Error("Expected an error but none was thrown");
    } catch (error) {
      expect(error).to.be.an.instanceOf(Error);
      expect((error as Error).message).to.include("Cannot query field");
    }
  });
});

Modules 3: REST API Testing (/api/rest)

This module provides tools and setups for testing REST APIs using JSONPlaceholder as an example. It includes a REST API client helper for sending requests and handling responses.

Configuration

Create an example.config.ts file in the api/config directory for REST API test configurations:

import { ApiTestsConfig } from "../../../../src/api/config/types";

export const apiTestsConfig: ApiTestsConfig = {
    restApiUrl: "https://countries.trevorblades.com/",
    // graphqlApiUrl: "another URL for graphql API testing?",
    // apiKey: "your-api-key",
};

Defining Endpoints

Define your API endpoints in the api/endpoints directory:

// example/tests/api/endpoints/endpoints.ts
export const postEndpoints = {
    getPosts: '/posts',
    getPost: '/posts/1', // Endpoint to fetch a specific post
    invalidEndpoint: '/invalidEndpoint', // Example of an invalid endpoint
};

Writing Tests

Write your REST API tests in the api/rest directory. Here's an example of how to structure and write a REST API test:

// example/tests/api/rest/posts.spec.ts
import { expect } from 'chai';
import { RestApiClientHelper } from 'test-automation-framework';
import { postEndpoints } from '../endpoints/endpoints';
import { apiTestsConfig } from '../config';
import { Post, postsResponseSchema } from '../types/requestTypes';
import { postResponseSchema } from '../types/responseTypes';

describe("REST API Posts Tests", () => {
    const client = new RestApiClientHelper(apiTestsConfig.restApiUrl);

    it("should fetch posts correctly", async () => {
        const posts: Post[] = await client.sendRequest('get', postEndpoints.getPosts, undefined, postsResponseSchema);
        expect(posts).to.be.an('array');
        expect(posts.length).to.be.greaterThan(0);
    });

    // Additional test cases...
});

Directory Structure for REST API Tests

The structure for REST API tests is organized as follows:

├───api
│   ├───config
│   │   │   example.config.ts
│   │   │
│   ├───endpoints
│   │   │   endpoints.ts
│   │   
│   ├───rest
│   │   └───posts.spec
│   │       │   posts.spec.ts
│   │       
│   └───types
│       │   requestTypes.ts
│       │   responseTypes.ts

Modules 4: Performance Testing with k6 (/performance)

Our framework integrates performance testing using k6, a powerful tool for load testing. Due to k6's limitations in importing npm modules, this module relies on direct imports of raw files from this repository in the consumer's k6 scripts.

IMPORTANT DISCLAIMER: K6 Limitations

In the realm of performance testing with k6, there are specific limitations that we need to acknowledge and work around. Firstly, k6 is designed to execute JavaScript (.js) files exclusively. This inherent design choice means that TypeScript files are not directly supported. As a consequence of this limitation, our performance testing scripts are written directly in JavaScript rather than TypeScript. This approach allows us to leverage the full capabilities of k6 without the complexities introduced by a transpilation step from TypeScript to JavaScript.

Furthermore, k6 intentionally restricts the import of Node.js modules. This design decision is primarily to prevent complications with the internal memory usage of the Virtual Users (VUs) that k6 generates during tests. Such restrictions ensure that the performance tests are lean and more closely simulate real-world user interaction scenarios. Due to this limitation, any necessary modules or libraries that are not natively supported by k6 must be imported via HTTP(S) from external sources, such as GitHub's raw content. This method of importing ensures that the necessary functionalities are accessible without the need for npm publishing of performance modules, which in our case would be redundant and inefficient.

For instance, in our consumer-side performance tests (located at tests/performance/requests/jsonPlaceholder.js), we import modules like defaultHeader, handleResponseError, and parseJsonResponse directly from the hosted JavaScript files on GitHub:

import { defaultHeader, handleResponseError, parseJsonResponse } from 'https://raw.githubusercontent.com/mohsenny/test-automation-framework/main/dist/src/perfomrance/index.js';

export const getPosts = () => {
    // Test implementation
};

Configuring Performance Tests

A config/configure.js file is provided to manage environment variables essential for running the tests. This file contains common mandatory environment variables applicable across different projects. Additional project-specific environment variables can be added on the consumer side as needed.

Writing Performance Tests

All performance tests should be written in JavaScript, as k6 does not smoothly support TypeScript. This ensures compatibility with k6's runtime environment and its import mechanism.

How to Use

Import the necessary scripts directly from the raw GitHub content in your k6 test scripts. This allows you to leverage the latest updates and functionalities of the performance testing module without npm module dependencies.

Configuration (/config)

Define load stages in load.ts:

// config/load.ts
export const loads = {
  // Define your load configurations here
  performance: {
    jsonPlaceholder: {
      getPosts: [{ target: 5, duration: "20s" }],
      // Additional scenarios
    },
  },
  // Other environments like 'sanity'
};

// Function to retrieve the appropriate load configuration
export function get(env) { /* ... */ }

Customize environment-specific settings in customConfig.ts:

// config/customConfig.ts
export const customOptions = {
  // Define your custom options here
  host: getEnvVariable("MY_HOSTNAME"),
  // ...
};

Endpoints (/requests)

Define API endpoints:

// requests/endpoints.ts
export const list = {
  jsonPlaceholderPosts: {
    getPosts: `${customOptions.host}/posts`,
    // Additional endpoints
  },
};

Implement request functions:

// requests/jsonPlaceholder.ts
export const getPosts = () => {
  // Function to fetch posts
};

export const createPost = (payload) => {
  // Function to create a post
};

Scenarios (/scenarios)

Write test scenarios using the defined requests and configurations:

// scenarios/example.spec/jsonPlaceholder.ts
import { sleep, group } from "k6";
import * as jsonPlaceholderRequests from "../../requests/jsonPlaceholder.js";

export default function () {
  group("Get all Posts", () => {
    jsonPlaceholderRequests.getPosts();
    sleep(1);
  });

  // Additional test groups
};

Setup Scripts (/setup)

Use ci.sh, local.ps1, and local.sh for setting up test environments and execution.

Types (/types)

Define types for requests and responses in TypeScript for better code structure and understanding.

Contributing

Contributions to this framework are welcome! If you'd like to contribute, please submit a pull request or open an issue for discussion.