3

I am trying to prove that the price is being set on the component.

I have the following HTML:

<template>
    <h1>Current price of a bitcoin in Euro</h1>
    <lightning-formatted-number currency-code="EUR"
                                format-style="currency"
                                value={euroPrice}
    ></lightning-formatted-number>
</template>

And the following JavaScript:

import {LightningElement, api, track} from 'lwc';

const bitcoinPriceProvider = 'https://api.coinbase.com/v2/prices/spot?currency=EUR';
export default class BitcoinEuroPrice extends LightningElement {
    @api
    refreshRateInSeconds;

    @track
    euroPrice;

    connectedCallback() {
        this.fetchPrice();
        setInterval(this.fetchPrice, this.refreshRateInSeconds * 1000);
    }

    fetchPrice() {
        fetch(bitcoinPriceProvider)
            .then(response => response.json())
            .then(json => {
                this.euroPrice = json.data.amount;
                console.log('######## this.euroPrice: ', this.euroPrice);
            });
    }
}

This is my test:

import {createElement} from 'lwc';
import BitcoinEuroPrice from 'c/bitcoinEuroPrice';

describe('c-bitcoinEuroPrice', () => {
    afterEach(() => {
        while (document.body.firstChild) {
            document.body.removeChild(document.body.firstChild);
        }
    });

    it('fetches price', () => {
        // Arrange
        const testAmount = '8448.947391885';
        const mockApiResponse = {
            'data': {
                'base': 'BTC',
                'currency': 'EUR',
                'amount': testAmount
            }
        };
        fetch = global.fetch = mockFetch(mockApiResponse);

        // Act
        const priceUnderTest = createElement('c-bitcoinEuroPrice', {
            is: BitcoinEuroPrice
        });
        document.body.appendChild(priceUnderTest);

        // Assert
        expect(global.fetch).toHaveBeenCalledTimes(1);
        expect(global.fetch).toHaveBeenCalledWith('https://api.coinbase.com/v2/prices/spot?currency=EUR');

        const displayPrice = priceUnderTest.shadowRoot.querySelector('lightning-formatted-number');
        console.log('##### priceUnderTest', priceUnderTest);
        console.log('##### displayPrice', displayPrice);
        expect(displayPrice.value).toEqual(testAmount);
    });

    function mockFetch(mockApiResponse) {
        return jest
            .fn()
            .mockImplementationOnce(() =>
                Promise.resolve({
                    ok: true,
                    json: () => Promise.resolve(mockApiResponse),
                    jsonResponse: mockApiResponse
                })
            );
    }
});

This fails with:

expect(received).toEqual(expected) // deep equality

Expected: "8448.947391885"
Received: undefined

  34 |         console.log('##### priceUnderTest', priceUnderTest);
  35 |         console.log('##### displayPrice', displayPrice);
> 36 |         expect(displayPrice.value).toEqual(testAmount);
     |         ^
  37 |     });
  38 |

but further down the log I see:

console.log force-app/main/default/lwc/bitcoinEuroPrice/bitcoinEuroPrice.js:21 ######## this.euroPrice: 8448.947391885

My hypothesis is that the test is not waiting for the asynchronous chain to complete before checking the value.

How can I make it wait? Or is there some other way I should check the value?

6

Figured out a way to flush the promises by applying answer from https://stackoverflow.com/questions/44741102/how-to-make-jest-wait-for-all-asynchronous-code-to-finish-execution-before-expec

essentially just needed to:

  1. Add this function/constant:
const flushPromises = () => new Promise(setImmediate);
  1. Make the test async:
it('fetches price', async () => {
  1. Wait for the flush:
        document.body.appendChild(priceUnderTest);
        await flushPromises();
5
  • 1
    setImmediate no longer works with the version of jsdom lwc uses Aug 18 '21 at 19:39
  • Just hit the same issue after updating lwc-jest. @TemporaryFix have you found alternative?
    – dzh
    Aug 25 '21 at 10:49
  • 1
    import 'setImmediate' seemingly solved it
    – dzh
    Sep 6 '21 at 23:58
  • 2
    Or better yet put "import { setImmediate } from 'timers';" at the top of your test.js program. If that errors out then install the node timer module. Sep 7 '21 at 9:50
  • Just wanted to come back and say there's an updated way to do this with process.nextTick. See more here and here yesterday

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.