Promise Never Resolves Using Axios

1.7k Views Asked by At

I am using WDIO and defining a customer reporter to integrate with the testrails api. The plan was to use axios to make these requests inside the testing hooks.

Unfortunately, I am unable to get axios to return any valid data on requests. In most cases when we await a response, the thread just stops executing entirely without any logging output. If I jimmy it enough sometimes I can get it to return an unresolved promise, but nothing I can do ultimately resolves the promise.

Also in none of my attempts have the requests been received by testrails (I've tested a few other urls as well, I'm fairly certain the issue is not at the destination).

I've made sure that network access and security are not factors. We have also attempted using both the axios post, and the straight up axios() methods, no luck there.

I'll copy the file below, I've added roughly a dozen attempts/configurations with notes on each as to what we're getting. The meat of the issue is in the addRun() method.

In most cases we never appear to resolve the promise. there is one exception, where we don't interact at all with the response, just log inside the then() statement. If we do that, we can see those logs, but the results of the axios call never take effect (the run is not created in testrails).

const WDIOReporter = require('@wdio/reporter').default
const axios = require('axios').default;

module.exports = class TestrailsReporter extends WDIOReporter{
    constructor(options) {
        /*
         * make reporter to write to the output stream by default
         */
        options = Object.assign(options, { stdout: true })
        super(options)
    }

    // I have tried marking this as both async and not, no difference
    async onRunnerEnd(suite) {
        console.log("CHECKPOINT RUNNER END")
        this.recordResults(caseIds[5], results[5], 'renters api tests', 5);
    }
    /**
     * takes the results from a test suite and records them in testrails
     * @param suiteId -- the suite defined in the testrails project
     * @param projectId -- the project id defined in the testrails project
     * @param caseIds -- a list of cases with which to create the test run
     * @param results -- a list of case:result pairings
    */
    async recordResults(caseIds, results, name, projectId) {
        console.log(`CHECKPOINT RECORDING RESULTS ${projectId}`)
        let testRun = await this.addRun(results['suiteId'], caseIds['cases'], name, projectId);
        testRun.then(console.log)
        await this.addResults(testRun, results['cases']);
    }

    async addRun(suiteId, caseIds, name = '', projectId) {
        console.log("CHECKPOINT ADD RUN")
        let addRunConfig = {
            method: 'post',
            url: `https://REDACTED.testrail.io/index.php?/api/v2/add_run/${projectId}`,
            headers: {
                'Content-Type': 'application/json',
                Authorization: token,
                Cookie: 'tr_session=041c4349-688f-440a-95a3-afc29d39320a'
            },
            data: JSON.stringify({
                suite_id: suiteId,
                include_all: false,
                case_ids: caseIds,
                name: name
            })
        };

        // let x = axios.get('https://www.google.com/')
        // console.log(x)

        axios.defaults.timeout = 1000;

        // THIS DOES NOT EXECUTE THE CODE INSIDE THE THEN STATEMENT, RETURNS PENDING PROMISE TO RESPONSE
        // let response = axios(addRunConfig)
        //     .then(function (response) {
        //         console.log("WHAAAT?")
        //         return response.data.id;
        //     })
        //     .catch(function (error) {
        //         console.log("HELP!")
        //         console.log(error);
        //     });
        // THIS DOES NOT EXECUTE THE CODE INSIDE THE THEN STATEMENT, NO LOGGING APPEARS AFTER
        let response = await axios(addRunConfig)
            .then(function (response) {
                console.log("WHAAAT?")
                return response.data.id;
            })
            .catch(function (error) {
                console.log("HELP!")
                console.log(error);
            });

        // THIS DOES NOT EXECUTE THE CODE INSIDE THE THEN STATEMENT
        // await axios.post(`https://REDACTED.testrail.io/index.php?/api/v2/add_run/${projectId}`, addRunConfig)
        //     .then(
        //         function (response){
        //             console.log('WHAAAT?')
        //             console.log(response)
        //             console.log('NO WAY?')
        //         })

        // THIS DOES NOT EXECUTE THE CODE INSIDE THE THEN STATEMENT, BUT RETURNS A PENDING PROMISE TO RESPONSE
        // let response = axios.post(`https://REDACTED.testrail.io/index.php?/api/v2/add_run/${projectId}`, addRunConfig)
        //     .then(
        //         function (run){
        //             console.log('WHAAAT?')
        //             console.log(run)
        //             console.log('NO WAY?')
        //         })

        // THIS DOES NOT EXECUTE THE CODE INSIDE THE THEN STATEMENT, BUT RETURNS A PENDING PROMISE TO RESPONSE
        // let response = axios.post(`https://REDACTED.testrail.io/index.php?/api/v2/add_run/${projectId}`, addRunConfig)
        //     .then(
        //         function (run){
        //             console.log('WHAAAT?')
        //         })

        // THIS DOES NOT EXECUTE THE CODE INSIDE THE THEN STATEMENT, BUT RETURNS A PENDING PROMISE TO RESPONSE
        // let response = axios.post(`https://REDACTED.testrail.io/index.php?/api/v2/add_run/${projectId}`, addRunConfig)
        //     .then(run => {
        //             console.log('WHAAAT?')
        //         })

        // THIS EXECUTES THE CONSOLE.LOG INSIDE THE THEN STATEMENT, BUT NOT AFTER
        // let response = await axios.post(`https://REDACTED.testrail.io/index.php?/api/v2/add_run/${projectId}`, addRunConfig)
        //     .then(console.log('WHAAAT?'))

        // THIS EXECUTES THE CONSOLE.LOG INSIDE THE THEN STATEMENT, AND AFTER
        // let response = axios.post(`https://REDACTED.testrail.io/index.php?/api/v2/add_run/${projectId}`, addRunConfig)
        //     .then(console.log('WHAAAT?'))

        // EXECUTES THE CONSOLE.LOG INSIDE THE THEN STATEMENT, NOTHING FROM THE CATCH, AND NOTHING AFTER
        // const response = await axios(addRunConfig).then(console.log("HI")).catch(function (error) {
        //     console.log("HELP!")
        //     console.log(error);
        // });

        console.log("ANYTHING")
        console.log(response)
        return response
    }```
3

There are 3 best solutions below

1
On

Have you tried calling the method using await axios.post(...) instead of defining everything in the addRunConfig object?

Not sure if it makes a difference, but it's something to try.

0
On

There is some confusion around the concepts of defining functions, calling functions and asynchronous functions here.

First of all: If you are calling an asynchronous function and do not want your calling function to return before that asynchronous function has returned, you want to await that call.

In this case, your recordResults function awaits something, and is thus async. Therefor, you probably want that onRunnerEnd awaits your call to recordResults. If you dont do that, the function will terminate prematurely and likely not wait for a result.

async onRunnerEnd(suite) {
    console.log("CHECKPOINT RUNNER END")
    await this.recordResults(caseIds[5], results[5], 'renters api tests', 5);
}

Secondly, if you use then and await together, the value returned by the await expression is whatever the function inside the then returns. Thus, all your attempts that have no return value inside the function called by then will never return anything but a void promise. There is no reason to combine these concepts.

Thirdly, putting a function call (rather than a declaration or reference) inside a then clause, will immediately call that function. I.e. .then(console.log('WHAAAT?')) just immediately calls console.log and also registers a non existent function as the callback for then (since console.log does not return a function reference).

Lastly, passing unbound functions is not going to work in general. Doing things like testRun.then(console.log) will not work, depending on the implementation of then and console.log. Either do testRun.then(console.log.bind(console) or testRun.then((x) => console.log(x)) to be on the safe side.


So, first of all, add that await inside onRunnerEnd, and then just use the await result without any then or catch in your addRun:

async addRun(suiteId, caseIds, name = '', projectId) {
    console.log("CHECKPOINT ADD RUN")
    let addRunConfig = {
        method: 'post',
        url: `https://REDACTED.testrail.io/index.php?/api/v2/add_run/${projectId}`,
        headers: {
            'Content-Type': 'application/json',
            Authorization: token,
            Cookie: 'tr_session=041c4349-688f-440a-95a3-afc29d39320a'
        },
        data: JSON.stringify({
            suite_id: suiteId,
            include_all: false,
            case_ids: caseIds,
            name: name
        })
    };

    // let x = axios.get('https://www.google.com/')
    // console.log(x)

    axios.defaults.timeout = 1000;

    let response = await axios(addRunConfig);
    console.log(response);
    console.log(response.data.id);
    return response.data.id;
}
0
On

Figured this out, it was the WDIOReporter parent class not playing nice with the asynchronous calls from axios. Solution found here: https://github.com/webdriverio/webdriverio/issues/5701