0

Goal: Call a function that invokes a fetch call to validate it works with my backend rest-api (end to end testing basically).

Project: node module built to be imported into several react web application. The module contains only fetch calls and minimal logic. Its basically a glorified wrapper for URLs and settings. Created to cut down work required to implement common end points used in applications.

Setup: I have a docker compose building a docker test container and pulling in my rest-api docker image (built in a different system). The test container pulls in the packed module and installs it with dependencies. Then it brings up the tests alongside the backend + other images needed for the backend (DB, login system, etc).

Problem: How to implement the tests to handle the calls.

Currently I've tried calling the fetch methods directly. This works for my login fetch but any additional call fails to send the cookie. As far as I understand the code I have depends on the browser for the cookie. I've tried several solutions to get said cookie but i've been unable to get fetch of node-fetch to send it properly. My best guess is each test was creating a new cookie but I lack the knowledge to full debug this solution path.

my send solution path was to attempt to use puppeteer to load a fake page and then evaluate the function in page following examples like: https://github.com/puppeteer/puppeteer/issues/2579 How to use imported function inside page.evaluate in Puppeteer with Jest?

Problem with this is the tests kept failing to load libraries required or some other problem.

Code:

Here is the call I'm trying to test for the most part. Each function I have wraps around this providing {url: "api/endpoint/path", method: "GET"}. With some passing in a body for larger data posts.

 export function request(options) {

    //Build options
    options = {
        headers: {
            'Content-Type': 'application/json'
        },
        ...options
    };

    return fetch(options.url, options)
        .then(response => {
            //...

            //Handle errors
            if (!response.ok) {
                return Promise.reject(`${response.status} - ${response.statusText}`);
            }

            try {
                return response.json();
            } catch (e) {
                if (e.name === 'SyntaxError') {
                    return Promise.reject(response.text());
                }
            }
        });
}

Test example i've tried:

import puppeteer from "puppeteer";
import {myCall} from "my-rest-bridge";

it('Check response', async () => {

    //Load browser
    const browser = await puppeteer.launch({
        headless: true,
        args: ['--no-sandbox']
    });
    const page = await browser.newPage();

    //Load page
    await page.goto('http://docker:8888/index.html');

    //Do login
    expect(await page.evaluate(() => login('user', 'password')).toBe(expectedUserResponseJson);

    //Make call
    expect(await page.evaluate(() => myCall(input1, input2)).toBe(expectedCallResponseJson);

    //Close page
    await page.close();
})

DarkGuardsman
  • 126
  • 4
  • 15

1 Answers1

0

Took me a while but I built a solution to my own question. Its not perfect so if anyone has a better idea please answer.

So my solution works as follows. I built an addition git project to create a shell reactjs application inside a docker image. This application pulls in my node module, iterates through all the exports, and then generates a component per function.

import React from 'react';
import * as magicNodeModule from "magic-node-module"; //obviously not the real name
import CallRest from "./CallRest";

/** Core component for the application */
export default class App extends React.Component {

    /** Renders the core application */
    render() {
        const restCalls = Object.keys(magicNodeModule);
        return (
            <div id={"App"}>
                <div>
                    Functions found:
                    <ul id={"function-list"}>
                        {restCalls.map(restCall => <li>{restCall}</li>)}
                    </ul>
                    <hr/>
                </div>
                {
                    restCalls.map(restCall => {
                        return (
                            <CallRest restName={restCall} restCall={magicNodeModule[restCall]}/>
                        );
                    })
                }
            </div>
        )
    }
}


This component (CallRest) contains an input box, submit button, and output div. A user, or in my use case puppeteer, can input data into the input. Then by clicking submit it will run the fetch call and insert the resulting output into the div. Works very much like swagger 2 for those that are familiar with the system.

The solution is still built up as a series of docker images inside of a compose. Though it did require setting up a reverse proxy to allow the react app to communicate with backend API. As well it pulls in a fresh copy of the node module as a pack zip and installs it into the docker. This way I only have to build the shell react docker once in a blue moon.

DarkGuardsman
  • 126
  • 4
  • 15