import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; import { render, settled, waitUntil } from '@ember/test-helpers'; import hbs from 'htmlbars-inline-precompile'; import Layer1TestWeb3Strategy from '@cardstack/web-client/utils/web3-strategies/test-layer1'; import Layer2TestWeb3Strategy from '@cardstack/web-client/utils/web3-strategies/test-layer2'; import BN from 'bn.js'; import Layer1Network from '../../../app/services/layer1-network'; import Layer2Network from '../../../app/services/layer2-network'; import { setupOnerror, resetOnerror } from '@ember/test-helpers'; import { defer } from 'rsvp'; import { toWei } from 'web3-utils'; module('Integration | Helper | token-to-usd', function (hooks) { setupRenderingTest(hooks); let layer1Service!: Layer1Network; let layer1Strategy!: Layer1TestWeb3Strategy; let layer2Service!: Layer2Network; let layer2Strategy!: Layer2TestWeb3Strategy; hooks.beforeEach(function () { // Simulate being connected on layer 2 -- prereq to converting bridged tokens to USD layer2Service = this.owner.lookup('service:layer2-network'); layer2Strategy = layer2Service.strategy as Layer2TestWeb3Strategy; let layer2AccountAddress = '0x182619c6Ea074C053eF3f1e1eF81Ec8De6Eb6E44'; layer2Strategy.test__simulateAccountsChanged([layer2AccountAddress]); // Simulate being connected on layer 1 -- prereq to converting layer 1 tokens to USD layer1Service = this.owner.lookup('service:layer1-network'); layer1Strategy = layer1Service.strategy as Layer1TestWeb3Strategy; let layer1AccountAddress = '0xa1b219c6Ea074C053eF3f1e1eF81Ec8De6Eb6E44'; layer1Strategy.test__simulateAccountsChanged( [layer1AccountAddress], 'metamask' ); }); hooks.afterEach(function () { resetOnerror(); }); test('it returns 0 without fetching converters if the amount requested is 0', async function (assert) { this.set('inputValue', new BN(0)); await render( hbs`
{{token-to-usd 'DAI.CPXD' this.inputValue}}
{{token-to-usd 'CARD.CPXD' this.inputValue}}
{{token-to-usd 'DAI' this.inputValue}}
{{token-to-usd 'CARD' this.inputValue}}
{{token-to-usd 'ETH' this.inputValue}}
` ); assert.dom('.dai-cpxd').containsText('0'); assert.dom('.card-cpxd').containsText('0'); assert.dom('.dai').containsText('0'); assert.dom('.card').containsText('0'); assert.dom('.eth').containsText('0'); assert.deepEqual( layer1Strategy.test__lastSymbolsToUpdate, [], 'It should not fetch any layer 1 converters' ); assert.deepEqual( layer2Strategy.test__lastSymbolsToUpdate, [], 'It should not fetch any layer 2 converters' ); }); test('it does not fetch converters if the helper is not used', async function (assert) { await render(hbs`Look mom, no hands!`); assert.deepEqual( layer1Strategy.test__lastSymbolsToUpdate, [], 'It should not fetch any layer 1 converters' ); assert.deepEqual( layer2Strategy.test__lastSymbolsToUpdate, [], 'It should not fetch any layer 2 converters' ); }); test('it fetches two converters when DAI.CPXD and CARD.CPXD are requested', async function (assert) { this.set('inputValue', new BN(100)); await render( hbs`{{token-to-usd 'DAI.CPXD' this.inputValue}} {{token-to-usd 'CARD.CPXD' this.inputValue}}` ); assert.deepEqual( layer1Strategy.test__lastSymbolsToUpdate, [], 'It should fetch no layer 1 converters' ); assert.deepEqual( layer2Strategy.test__lastSymbolsToUpdate, ['CARD.CPXD', 'DAI.CPXD'], 'It should fetch two layer 2 converters' ); }); test('it fetches two converters when DAI and CARD are requested', async function (assert) { this.set('inputValue', new BN(100)); await render( hbs`{{token-to-usd 'DAI' this.inputValue}} {{token-to-usd 'CARD' this.inputValue}}` ); assert.deepEqual( layer1Strategy.test__lastSymbolsToUpdate, [], 'It should fetch no layer 1 converters' ); assert.deepEqual( layer2Strategy.test__lastSymbolsToUpdate, ['CARD.CPXD', 'DAI.CPXD'], 'It should fetch two layer 2 converters' ); }); test('it fetches one converter when ETH is requested', async function (assert) { this.set('inputValue', new BN(100)); await render(hbs`{{token-to-usd 'ETH' this.inputValue}}`); assert.deepEqual( layer1Strategy.test__lastSymbolsToUpdate, ['ETH'], 'It should fetch one layer 1 converter' ); assert.deepEqual( layer2Strategy.test__lastSymbolsToUpdate, [], 'It should fetch no layer 2 converters' ); }); test('it fetches only DAI.CPXD when only DAI.CPXD is requested', async function (assert) { this.set('inputValue', new BN(100)); await render(hbs`{{token-to-usd 'DAI.CPXD' this.inputValue}}`); assert.deepEqual( layer1Strategy.test__lastSymbolsToUpdate, [], 'It should fetch no layer 1 converters' ); assert.deepEqual( layer2Strategy.test__lastSymbolsToUpdate, ['DAI.CPXD'], 'It should fetch only layer 2 DAI.CPXD conversion' ); }); test('it fetches only CARD.CPXD when only CARD.CPXD is requested', async function (assert) { this.set('inputValue', new BN(100)); await render(hbs`{{token-to-usd 'CARD.CPXD' this.inputValue}}`); assert.deepEqual( layer1Strategy.test__lastSymbolsToUpdate, [], 'It should fetch no layer 1 converters' ); assert.deepEqual( layer2Strategy.test__lastSymbolsToUpdate, ['CARD.CPXD'], 'It should fetch only CARD conversion from layer 2' ); }); test('it fetches only DAI.CPXD and ETH when only DAI and ETH are requested', async function (assert) { this.set('inputValue', new BN(100)); await render( hbs`{{token-to-usd 'DAI' this.inputValue}} {{token-to-usd 'ETH' this.inputValue}}` ); assert.deepEqual( layer1Strategy.test__lastSymbolsToUpdate, ['ETH'], 'It should fetch only ETH converter from layer 1' ); assert.deepEqual( layer2Strategy.test__lastSymbolsToUpdate, ['DAI.CPXD'], 'It should fetch only DAI conversion from layer 2' ); }); test('it throws an error if a token other than DAI, DAI.CPXD, CARD, CARD.CPXD, or ETH is requested', async function (assert) { setupOnerror(function (err: Error) { assert.equal( err.message, 'Invalid symbol LGTM passed to {{token-to-usd}}' ); }); await render(hbs`{{token-to-usd 'LGTM' this.inputValue}}`); }); test('it renders conversion result to 2 decimal places', async function (assert) { this.set('tokenSymbol', 'DAI.CPXD'); this.set('inputValue', new BN('123000000000000000000')); await render(hbs`{{token-to-usd this.tokenSymbol this.inputValue}}`); assert.dom(this.element).hasText('24.6'); this.set('inputValue', new BN('223000000000000000000')); assert.dom(this.element).hasText('44.6'); }); test('it updates conversion result when DAI exchange rate changes', async function (assert) { this.set('tokenSymbol', 'DAI.CPXD'); this.set('inputValue', new BN('123000000000000000000')); await render(hbs`{{token-to-usd this.tokenSymbol this.inputValue}}`); assert.dom(this.element).hasText('24.6'); // based on default exchange rate layer2Strategy.test__simulatedExchangeRate = 0.3; // allow time for TokenToUsd service interval to expire await waitUntil(() => this.element.textContent?.trim() !== '24.6'); assert.dom(this.element).hasText('36.9'); }); test('it updates conversion result when ETH exchange rate changes', async function (assert) { this.set('tokenSymbol', 'ETH'); this.set('inputValue', new BN(toWei('2'))); await render(hbs`{{token-to-usd this.tokenSymbol this.inputValue}}`); assert.dom(this.element).hasText('6000'); // based on default exchange rate layer1Strategy.test__simulatedExchangeRate = 3052.22; // allow time for TokenToUsd service interval to expire await waitUntil(() => this.element.textContent?.trim() !== '6000'); assert.dom(this.element).hasText('6104.44'); }); test('it renders an empty string while the conversion function is not yet set', async function (assert) { layer2Strategy.test__updateUsdConvertersDeferred = defer(); this.set('tokenSymbol', 'DAI.CPXD'); this.set('inputValue', new BN('123000000000000000000')); await render(hbs`{{token-to-usd this.tokenSymbol this.inputValue}}`); assert.dom(this.element).hasText(''); layer2Strategy.test__updateUsdConvertersDeferred.resolve(); await settled(); assert.dom(this.element).hasText('24.6'); }); });