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....

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);

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.