2

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>

I have 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;
            });
    }
}

And I have the following metadata:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <description>Bitcoin Euro Price</description>
    <isExposed>true</isExposed>
    <masterLabel>Bitcoin Euro Price</masterLabel>
    <targets>
        <target>lightningCommunity__Page</target>
        <target>lightningCommunity__Default</target>
    </targets>
    <targetConfigs>
        <targetConfig  targets="lightningCommunity__Default">
            <property name="refreshRateInSeconds" type="Integer" default="10"/>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

I am currently trying to figure out how to enhance my test to confirm setInterval(this.fetchPrice, this.refreshRateInSeconds * 1000); is executing as expected.

In the metadata, I have given refreshRateInSeconds a default of 10.

My test currently looks like this:

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

const priceUnderTest = createElement('c-bitcoinEuroPrice', {
    is: BitcoinEuroPrice
});

const testAmount = '8448.947391885';

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

    it('fetches price and sets the value', async () => {
        // Arrange
        jest.useFakeTimers();
        fetch = global.fetch = mockFetch({
            'data': {
                'base': 'BTC',
                'currency': 'EUR',
                'amount': testAmount
            }
        });

        // Act
        document.body.appendChild(priceUnderTest);

        // Assert
        await flushPromises();

        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');
        expect(displayPrice.value).toEqual(testAmount);

        jest.advanceTimersByTime(10000);
        expect(global.fetch).toHaveBeenCalledTimes(2);
    });
});

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

function flushPromises() {
    return new Promise(setImmediate);
}

The test fails with:

expect(jest.fn()).toHaveBeenCalledTimes(expected)

Expected number of calls: 2
Received number of calls: 1

  39 | 
  40 |         jest.advanceTimersByTime(10000);
> 41 |         expect(global.fetch).toHaveBeenCalledTimes(2);
     |         ^
  42 |     });
  43 | });
  44 |

I can tell from my log that refreshRateInSeconds is not catcing the value from the metadata:

console.log force-app/main/default/lwc/bitcoinEuroPrice/bitcoinEuroPrice.js:13 #### this.refreshRateInSeconds:undefined

How can I ensure refreshRateInSeconds has a value?

If I hardcode a value for refreshRateInSeconds and change mockImplementationOnce to mockImplementation, this test should work....

1 Answer 1

2

Turns out, setting the public property is surprisingly straight-forward.

All I needed to do is:

        // Act
        priceUnderTest.refreshRateInSeconds = 10;
        document.body.appendChild(priceUnderTest);

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .