diff --git a/.changelog/1012.bugfix.md b/.changelog/1012.bugfix.md new file mode 100644 index 000000000..ea449891f --- /dev/null +++ b/.changelog/1012.bugfix.md @@ -0,0 +1 @@ +Prevent app crash when rendering new event types diff --git a/playwright/tests/accounts.spec.ts b/playwright/tests/accounts.spec.ts new file mode 100644 index 000000000..8e718b0fd --- /dev/null +++ b/playwright/tests/accounts.spec.ts @@ -0,0 +1,123 @@ +import { Page, expect, test } from '@playwright/test' +import { RuntimeAccount, RuntimeEventList } from '../../src/oasis-nexus/api' + +async function setup(page: Page) { + await page.route( + 'https://api.coingecko.com/api/v3/simple/price?ids=oasis-network&vs_currencies=usd', + route => { + // Don't respond + }, + ) + await page.route('**/v1/', route => { + // Don't respond + }) + await page.route('**/v1/sapphire/status', route => { + // Don't respond + }) + await page.route( + '**/v1/sapphire/transactions?limit=10&offset=0&rel=oasis1qq2v39p9fqk997vk6742axrzqyu9v2ncyuqt8uek', + route => { + route.fulfill({ + body: JSON.stringify({ + is_total_count_clipped: false, + total_count: 0, + transactions: [], + }), + }) + }, + ) + await page.route('**/v1/sapphire/accounts/oasis1qq2v39p9fqk997vk6742axrzqyu9v2ncyuqt8uek', route => { + route.fulfill({ + body: JSON.stringify({ + address: 'oasis1qq2v39p9fqk997vk6742axrzqyu9v2ncyuqt8uek', + balances: [ + { + balance: '116404417198000000000', + token_decimals: 18, + token_symbol: 'TEST', + }, + ], + evm_balances: [], + stats: { + num_txns: 0, + total_received: '0', + total_sent: '0', + }, + } satisfies Partial), + }) + }) + await page.route('**/v1/sapphire/events?rel=oasis1qq2v39p9fqk997vk6742axrzqyu9v2ncyuqt8uek', route => { + route.fulfill({ + body: JSON.stringify({ + is_total_count_clipped: false, + total_count: 3, + events: [ + { + body: { + amount: { + Amount: '100000000000000000000', + Denomination: '', + }, + from: 'oasis1qq235lqj77855qcemcr5w2qm372s4amqcc4v3ztc', + nonce: 29, + to: 'oasis1qrwncs459lauc77zw23efdn9dmfcp23cxv095l5z', + }, + evm_log_name: '', + round: 3038913, + timestamp: '2023-10-16T13:13:47Z', + tx_hash: null, + type: 'consensus_accounts.delegate', + layer: 'sapphire', + network: 'testnet', + }, + { + body: { + debond_end_time: 30013, + from: 'oasis1qrwncs459lauc77zw23efdn9dmfcp23cxv095l5z', + nonce: 30, + shares: '100', + to: 'oasis1qq235lqj77855qcemcr5w2qm372s4amqcc4v3ztc', + }, + evm_log_name: '', + round: 3038944, + timestamp: '2023-10-16T13:18:23Z', + tx_hash: null, + type: 'consensus_accounts.undelegate_start', + layer: 'sapphire', + network: 'testnet', + }, + { + body: { + amount: { + Amount: '100000281888000000000', + Denomination: '', + }, + from: 'oasis1qrwncs459lauc77zw23efdn9dmfcp23cxv095l5z', + shares: '100000281888', + to: 'oasis1qq235lqj77855qcemcr5w2qm372s4amqcc4v3ztc', + }, + evm_log_name: '', + round: 3216917, + timestamp: '2023-10-29T09:06:05Z', + tx_hash: null, + type: 'consensus_accounts.undelegate_done', + layer: 'sapphire', + network: 'testnet', + }, + ], + } satisfies Partial), + }) + }) + + await page.goto('http://localhost:1234/mainnet/sapphire/address/0x0000000000000000000000000000000000000000') +} + +test.describe('Account details page', () => { + test('does not crash when rendering new event types', async ({ page }) => { + await setup(page) + await expect(page.getByText('Unknown error')).not.toBeVisible() + await expect(page.getByText('Delegate to consensus')).toBeVisible() + await expect(page.getByText('Start to undelegate from consensus')).toBeVisible() + await expect(page.getByText('Undelegate from consensus finished')).toBeVisible() + }) +}) diff --git a/src/app/components/RuntimeEvents/RuntimeEventDetails.tsx b/src/app/components/RuntimeEvents/RuntimeEventDetails.tsx index 0858c8eed..a283dc0c6 100644 --- a/src/app/components/RuntimeEvents/RuntimeEventDetails.tsx +++ b/src/app/components/RuntimeEvents/RuntimeEventDetails.tsx @@ -264,13 +264,18 @@ export const RuntimeEventDetails: FC<{ /> {addressSwitchOption === AddressSwitchOption.Oasis && } -
{t('runtimeEvent.fields.amount')}
-
- {t('common.valueInToken', { - ...getPreciseNumberFormat(event.body.amount.Amount), - ticker: event.body.amount.Denomination, - })} -
+ {/* TODO check is needed because some consensus events temporarily use this for rendering */} + {event.body.amount?.Amount && event.body.amount?.Denomination && ( + <> +
{t('runtimeEvent.fields.amount')}
+
+ {t('common.valueInToken', { + ...getPreciseNumberFormat(event.body.amount.Amount), + ticker: event.body.amount.Denomination, + })} +
+ + )} )