import {QueryClient, QueryClientProvider} from '@tanstack/react-query' import {render, screen, within} from '@testing-library/react' import userEvent from '@testing-library/user-event' import {beforeEach, expect, test, vi} from 'vitest' import {Route53RecordsPanel} from './Route53RecordsPanel' import type {CloudResource} from '@/types/resource' const api = vi.hoisted(() => ({ deleteFlatItem: vi.fn(), getCloudResource: vi.fn(), listFlatItems: vi.fn(), putFlatItem: vi.fn(), })) vi.mock('@/api/cloudProxyClient', () => api) vi.mock('@/lib/accountStore', () => ({useAccountId: () => '000000000000'})) const zone: CloudResource = { id: 'ZG9PAE0M9KRWJ4F', name: 'example.test', cloud: 'aws', service: 'dns', type: 'hosted-zone', region: null, createdAt: null, metadata: {visibility: 'public', recordCount: 3}, } function item(id: string, body: Record) { return {id, collectionId: null, timestamp: null, metadata: {}, body: {ttl: 300, values: [], alias: null, setIdentifier: null, managed: false, editable: true, ...body}} } const records = [ item('example.test|NS', {name: 'example.test', type: 'NS', ttl: 172800, values: ['ns-1.awsdns-01.org.'], managed: true}), item('www.example.test|A', {name: 'www.example.test', type: 'A', values: ['10.0.0.1']}), item('example.test|TXT', {name: 'example.test', type: 'TXT', values: ['"v=spf1 -all"']}), item('cdn.example.test|A', {name: 'cdn.example.test', type: 'A', ttl: null, alias: {dnsName: 'd111.cloudfront.net', hostedZoneId: 'Z2'}, editable: false}), ] function renderPanel() { const client = new QueryClient({defaultOptions: {queries: {retry: false}}}) return render() } beforeEach(() => { vi.clearAllMocks() api.getCloudResource.mockResolvedValue({...zone, metadata: {...zone.metadata, nameServers: ['ns-1.awsdns-01.org', 'ns-2.awsdns-02.net']}}) api.listFlatItems.mockResolvedValue({items: records, nextCursor: null}) api.putFlatItem.mockImplementation(async (_cloud, _service, _zone, body) => item('x', {name: `${body.name}.example.test`, type: body.type})) api.deleteFlatItem.mockResolvedValue(undefined) }) test('shows the name servers and records, keeping the zone defaults read-only', async () => { renderPanel() expect(await screen.findByText('ns-2.awsdns-02.net')).toBeInTheDocument() expect(await screen.findByText('10.0.0.1')).toBeInTheDocument() expect(screen.getByText('Alias → d111.cloudfront.net')).toBeInTheDocument() expect(screen.getByText('zone default')).toBeInTheDocument() expect(screen.queryByRole('button', {name: 'Delete NS example.test'})).not.toBeInTheDocument() expect(screen.queryByRole('button', {name: 'Edit A cdn.example.test'})).not.toBeInTheDocument() expect(screen.getByRole('button', {name: 'Delete A cdn.example.test'})).toBeInTheDocument() }) test('creates a record from a name inside the zone', async () => { const user = userEvent.setup() renderPanel() const form = await screen.findByRole('form', {name: 'Create record'}) await user.type(within(form).getByLabelText(/Record name/), 'api') await user.selectOptions(within(form).getByLabelText('Type'), 'CNAME') await user.clear(within(form).getByLabelText(/TTL/)) await user.type(within(form).getByLabelText(/TTL/), '60') await user.type(within(form).getByLabelText(/Values/), 'app.example.net') await user.click(within(form).getByRole('button', {name: 'Create record'})) expect(api.putFlatItem).toHaveBeenCalledWith('aws', 'dns', zone.id, {name: 'api', type: 'CNAME', ttl: 60, values: ['app.example.net']}) expect(await screen.findByText(/Created CNAME api.example.test/)).toBeInTheDocument() }) test('edits a record with its name and type fixed, showing TXT text without quotes', async () => { const user = userEvent.setup() renderPanel() await user.click(await screen.findByRole('button', {name: 'Edit TXT example.test'})) const form = screen.getByRole('form', {name: 'Edit record'}) expect(within(form).getByLabelText(/Record name/)).toHaveValue('@') expect(within(form).getByLabelText(/Record name/)).toBeDisabled() expect(within(form).getByLabelText('Type')).toBeDisabled() expect(within(form).getByLabelText(/Values/)).toHaveValue('v=spf1 -all') }) test('deletes a record by its name and type', async () => { const user = userEvent.setup() renderPanel() await user.click(await screen.findByRole('button', {name: 'Delete A www.example.test'})) expect(api.deleteFlatItem).toHaveBeenCalledWith('aws', 'dns', zone.id, 'www.example.test|A') expect(await screen.findByText('Deleted A www.example.test.')).toBeInTheDocument() })