domains-delete.test.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { App as AntApp, ConfigProvider } from 'antd';
  2. import { render, screen, waitFor, within } from '@testing-library/react';
  3. import userEvent from '@testing-library/user-event';
  4. import { MemoryRouter, Route, Routes } from 'react-router-dom';
  5. import { describe, expect, it, vi } from 'vitest';
  6. import { AppContext } from '../../src/frontend/app-context';
  7. import { I18nProvider } from '../../src/frontend/i18n/react';
  8. import { api } from '../../src/frontend/services/api';
  9. import { mailhubTheme } from '../../src/frontend/theme';
  10. import Domains from '../../src/pages/Domains';
  11. import type { AppContextValue } from '../../src/frontend/app-context';
  12. import type { Domain } from '../../src/frontend/types';
  13. describe('Domains dangerous actions', () => {
  14. it('requires the exact domain before deletion', async () => {
  15. const user = userEvent.setup();
  16. const deleteDomain = vi.spyOn(api, 'deleteDomain').mockResolvedValue({ deleted: true });
  17. vi.spyOn(api, 'domains').mockResolvedValue({ domains: [domain] });
  18. vi.spyOn(api, 'dnsCredentials').mockResolvedValue({ credentials: [] });
  19. vi.spyOn(api, 'smtpRelays').mockResolvedValue({ relays: [] });
  20. vi.spyOn(api, 'events').mockResolvedValue({ events: [], total: 0, page: 1, pageSize: 100 });
  21. renderPage();
  22. await screen.findAllByText('example.com');
  23. await user.click(screen.getByRole('button', { name: '更多操作 example.com' }));
  24. await user.click(await screen.findByText('删除域名'));
  25. const dialog = await screen.findByRole('dialog');
  26. const confirmation = within(dialog).getByLabelText('域名删除确认');
  27. const destructiveButton = within(dialog).getByRole('button', { name: '删除域名' });
  28. expect((destructiveButton as HTMLButtonElement).disabled).toBe(true);
  29. await user.type(confirmation, 'wrong.example.com');
  30. expect((destructiveButton as HTMLButtonElement).disabled).toBe(true);
  31. await user.clear(confirmation);
  32. await user.type(confirmation, 'example.com');
  33. expect((destructiveButton as HTMLButtonElement).disabled).toBe(false);
  34. await user.click(destructiveButton);
  35. await waitFor(() => expect(deleteDomain).toHaveBeenCalledWith(7));
  36. });
  37. });
  38. function renderPage() {
  39. const context: AppContextValue = {
  40. user: { id: 1, username: 'operator', email: 'operator@example.test', role: 'admin', status: 'active' },
  41. config,
  42. refreshBootstrap: vi.fn(async () => undefined),
  43. logout: vi.fn(async () => undefined)
  44. };
  45. return render(
  46. <ConfigProvider theme={mailhubTheme}>
  47. <AntApp>
  48. <I18nProvider>
  49. <AppContext.Provider value={context}>
  50. <MemoryRouter initialEntries={['/domains']}>
  51. <Routes><Route path="/domains" element={<Domains />} /></Routes>
  52. </MemoryRouter>
  53. </AppContext.Provider>
  54. </I18nProvider>
  55. </AntApp>
  56. </ConfigProvider>
  57. );
  58. }
  59. const config = {
  60. appBaseUrl: 'https://mail.example.test',
  61. mailHostname: 'mail.example.test',
  62. sendingIp: '192.0.2.10',
  63. defaultSpfMechanisms: '',
  64. dmarcPolicy: 'none',
  65. dmarcRua: '',
  66. registrationRequiresApproval: false,
  67. sendRequiresVerified: true,
  68. engagementTrackingEnabled: true,
  69. listUnsubscribeMailto: '',
  70. listUnsubscribeUrl: '',
  71. listUnsubscribePostEnabled: false,
  72. feedbackIdEnabled: false,
  73. reportAbuseTo: '',
  74. csaComplaintsTo: '',
  75. bounceAddress: '',
  76. bounceEnvelopeEnabled: false
  77. };
  78. const domain: Domain = {
  79. id: 7,
  80. userId: 1,
  81. dnsCredentialId: null,
  82. smtpRelayId: null,
  83. domain: 'example.com',
  84. selector: 'mh202607',
  85. verificationToken: 'verification',
  86. dkimPublic: 'public-key',
  87. senderHost: 'mail.example.com',
  88. sendingIp: '192.0.2.10',
  89. spfExtra: '',
  90. dmarcPolicy: 'none',
  91. dmarcRua: '',
  92. catchAllAddress: '',
  93. status: {
  94. verified: true,
  95. checkedAt: '2026-07-14T00:00:00.000Z',
  96. records: [
  97. { key: 'spf', label: 'SPF', host: 'example.com', type: 'TXT', status: 'ok' },
  98. { key: 'dkim', label: 'DKIM', host: 'mh._domainkey.example.com', type: 'TXT', status: 'ok' },
  99. { key: 'dmarc', label: 'DMARC', host: '_dmarc.example.com', type: 'TXT', status: 'ok' }
  100. ]
  101. },
  102. createdAt: '2026-07-14T00:00:00.000Z',
  103. updatedAt: '2026-07-14T00:00:00.000Z'
  104. };