FunnelResult.test.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import { describe, expect, it } from 'vitest';
  2. import { render, screen } from '@testing-library/react';
  3. import type { FunnelQueryResponse } from '../../../api/types';
  4. import { FunnelResult } from '../components/FunnelResult';
  5. function renderResult(props: Parameters<typeof FunnelResult>[0]) {
  6. return render(<FunnelResult {...props} />);
  7. }
  8. const readyData: FunnelQueryResponse = {
  9. period: 'last_7d',
  10. snapshot_dt: '20260623',
  11. data_status: 'ready',
  12. results: [
  13. {
  14. step_index: 1,
  15. name: '启动',
  16. event_key: 'start',
  17. uv: 10000,
  18. conversion_rate: null,
  19. dropoff_rate: null,
  20. },
  21. {
  22. step_index: 2,
  23. name: '曝光',
  24. event_key: 'show',
  25. uv: 8200,
  26. conversion_rate: 0.82,
  27. dropoff_rate: 0.18,
  28. },
  29. ],
  30. };
  31. const baseProps = {
  32. period: 'last_7d' as const,
  33. isLoading: false,
  34. isError: false,
  35. };
  36. describe('FunnelResult states', () => {
  37. it('ready: renders funnel chart and result table', async () => {
  38. renderResult({ ...baseProps, data: readyData });
  39. // FunnelChart is lazy-loaded — await its appearance.
  40. expect(await screen.findByTestId('funnel-chart')).toBeInTheDocument();
  41. expect(screen.getByText('UV')).toBeInTheDocument();
  42. expect(screen.getByText('转化率')).toBeInTheDocument();
  43. expect(screen.getByText('流失率')).toBeInTheDocument();
  44. expect(screen.getByText(/启动/)).toBeInTheDocument();
  45. expect(screen.getByText(/曝光/)).toBeInTheDocument();
  46. });
  47. it('ready (7d): shows the rolling "数据截至 … (近 7 天)" caption', () => {
  48. renderResult({ ...baseProps, data: readyData });
  49. expect(
  50. screen.getByText(/数据截至 2026-06-23(近 7 天)/),
  51. ).toBeInTheDocument();
  52. expect(screen.queryByText(/数据快照日/)).not.toBeInTheDocument();
  53. });
  54. it('ready (30d): caption says (近 30 天)', () => {
  55. renderResult({
  56. ...baseProps,
  57. period: 'last_30d',
  58. data: { ...readyData, period: 'last_30d' },
  59. });
  60. expect(
  61. screen.getByText(/数据截至 2026-06-23(近 30 天)/),
  62. ).toBeInTheDocument();
  63. });
  64. it('ready (day): shows the "数据快照日" caption', () => {
  65. renderResult({
  66. ...baseProps,
  67. period: 'day',
  68. data: { ...readyData, period: 'day' },
  69. });
  70. expect(screen.getByText(/数据快照日:2026-06-23/)).toBeInTheDocument();
  71. expect(screen.queryByText(/数据截至/)).not.toBeInTheDocument();
  72. });
  73. it('ready: step 1 null conversion renders as "—", not 0%', () => {
  74. renderResult({ ...baseProps, data: readyData });
  75. const dashes = screen.getAllByText('—');
  76. // step 1 conversion + dropoff = two em-dashes
  77. expect(dashes.length).toBeGreaterThanOrEqual(2);
  78. expect(screen.queryByText('0%')).not.toBeInTheDocument();
  79. expect(screen.queryByText('0.00%')).not.toBeInTheDocument();
  80. // the real value still shows
  81. expect(screen.getByText('82.00%')).toBeInTheDocument();
  82. });
  83. it('missing: explicit 数据缺失 empty state, no zeros / no chart', () => {
  84. renderResult({
  85. ...baseProps,
  86. data: { ...readyData, results: [], data_status: 'missing' },
  87. });
  88. expect(screen.getByText(/数据缺失/)).toBeInTheDocument();
  89. expect(screen.queryByTestId('funnel-chart')).not.toBeInTheDocument();
  90. expect(screen.queryByText('0')).not.toBeInTheDocument();
  91. });
  92. it('request error: shows error state with message', () => {
  93. renderResult({
  94. ...baseProps,
  95. isError: true,
  96. errorMessage: '网络请求失败',
  97. });
  98. expect(screen.getByText('查询失败')).toBeInTheDocument();
  99. expect(screen.getByText('网络请求失败')).toBeInTheDocument();
  100. });
  101. it('loading / no data yet: shows loading card', () => {
  102. renderResult({ ...baseProps, isLoading: true });
  103. expect(screen.getByTestId('funnel-loading')).toBeInTheDocument();
  104. });
  105. });