diff --git a/CHANGELOG.md b/CHANGELOG.md index a6c1de0..6e6fb2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - "Copy details" button on each route card — copies the route path, fee, and time as plain text to the clipboard (resolves #21). +- Unit test for `availableCurrencies()` (resolves #27). +- Component tests for `RouteList` covering the not-searched, empty, and + populated states (resolves #25). ### Fixed diff --git a/tests/RouteList.test.jsx b/tests/RouteList.test.jsx new file mode 100644 index 0000000..f3c2275 --- /dev/null +++ b/tests/RouteList.test.jsx @@ -0,0 +1,73 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest' +import { act } from 'react' +import { createRoot } from 'react-dom/client' +import RouteList from '../src/components/RouteList' + +const routes = [ + { + hops: [ + { anchorId: 'a', anchorName: 'MockAnchor US', fromCurrency: 'USD', toCurrency: 'USDC', feePercent: 0.1, estimatedMinutes: 5 }, + ], + totalFeePercent: 0.1, + totalEstimatedMinutes: 5, + outputAmount: 99.9, + totalCost: 15, + }, + { + hops: [ + { anchorId: 'b', anchorName: 'MockAnchor UK', fromCurrency: 'USDC', toCurrency: 'GBP', feePercent: 0.4, estimatedMinutes: 20 }, + ], + totalFeePercent: 0.4, + totalEstimatedMinutes: 20, + outputAmount: 99.6, + totalCost: 60, + }, +] + +function setup({ searched, routes: list }) { + const container = document.createElement('div') + document.body.appendChild(container) + const root = createRoot(container) + act(() => { + root.render() + }) + return { container, root } +} + +function cleanup(root) { + act(() => { + root.unmount() + }) +} + +beforeEach(() => { + globalThis.IS_REACT_ACT_ENVIRONMENT = true +}) + +afterEach(() => { + globalThis.IS_REACT_ACT_ENVIRONMENT = false + document.body.innerHTML = '' +}) + +describe('RouteList', () => { + it('renders nothing when not searched', () => { + const { container, root } = setup({ searched: false, routes: [] }) + expect(container.textContent).toBe('') + cleanup(root) + }) + + it('shows a no-route message when searched with no results', () => { + const { container, root } = setup({ searched: true, routes: [] }) + expect(container.textContent).toContain('No route found for that currency pair and amount.') + cleanup(root) + }) + + it('renders a card for each route when populated', () => { + const { container, root } = setup({ searched: true, routes }) + const cards = container.querySelectorAll('.route-card') + expect(cards.length).toBe(2) + expect(container.textContent).toContain('USD → USDC') + expect(container.textContent).toContain('USDC → GBP') + cleanup(root) + }) +}) diff --git a/tests/availableCurrencies.test.js b/tests/availableCurrencies.test.js new file mode 100644 index 0000000..0b6a40b --- /dev/null +++ b/tests/availableCurrencies.test.js @@ -0,0 +1,31 @@ +import { describe, it, expect } from 'vitest' +import { mockAnchors, availableCurrencies } from '../src/data/mockAnchors' + +describe('availableCurrencies', () => { + it('returns a deduplicated, sorted list of every currency in mockAnchors', () => { + const expected = [...new Set(mockAnchors.flatMap((a) => [a.fromCurrency, a.toCurrency]))].sort() + expect(availableCurrencies()).toEqual(expected) + }) + + it('includes every currency appearing as either fromCurrency or toCurrency', () => { + const currencies = availableCurrencies() + mockAnchors.forEach((anchor) => { + expect(currencies).toContain(anchor.fromCurrency) + expect(currencies).toContain(anchor.toCurrency) + }) + }) + + it('does not duplicate currencies that appear on both sides', () => { + const currencies = availableCurrencies() + expect(new Set(currencies).size).toBe(currencies.length) + }) + + it('returns a sorted list', () => { + const currencies = availableCurrencies() + expect(currencies).toEqual([...currencies].sort()) + }) + + it('matches the exact expected list for the current dataset', () => { + expect(availableCurrencies()).toEqual(['EUR', 'GBP', 'NGN', 'USD', 'USDC']) + }) +})