ArchivesHighlightParam.test.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /**
  2. * /archives?highlight=<id>, the queue card's archive link (#3336). The page
  3. * used to ignore the parameter and just open the list.
  4. */
  5. import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
  6. import { fireEvent, screen, waitFor } from '@testing-library/react';
  7. import { useNavigate } from 'react-router-dom';
  8. import { http, HttpResponse } from 'msw';
  9. import { render } from '../utils';
  10. import { ArchivesPage } from '../../pages/ArchivesPage';
  11. import { server } from '../mocks/server';
  12. import { setAuthToken } from '../../api/client';
  13. function archive(id: number, printName: string) {
  14. return {
  15. id,
  16. filename: `${printName}.gcode.3mf`,
  17. print_name: printName,
  18. printer_id: 1,
  19. printer_name: 'X1 Carbon',
  20. print_time_seconds: 3600,
  21. filament_used_grams: 10,
  22. status: 'completed',
  23. started_at: '2024-01-01T10:00:00Z',
  24. completed_at: '2024-01-01T11:00:00Z',
  25. thumbnail_path: null,
  26. notes: null,
  27. rating: null,
  28. project_id: null,
  29. project_name: null,
  30. project_color: null,
  31. print_count: 1,
  32. tags: '',
  33. created_at: '2024-01-01T09:00:00Z',
  34. updated_at: '2024-01-01T11:00:00Z',
  35. has_f3d: false,
  36. };
  37. }
  38. // The list without archive 3, as cached before its queued job started.
  39. let beforeTheJobStarted = false;
  40. /** Follows a link inside the running app, as the queue page's does. */
  41. function GoTo({ to }: { to: string }) {
  42. const navigate = useNavigate();
  43. return <button onClick={() => navigate(to)}>go</button>;
  44. }
  45. function setViewMode(mode: string) {
  46. (localStorage.getItem as ReturnType<typeof vi.fn>).mockImplementation((key: string) =>
  47. key === 'archiveViewMode' ? mode : null,
  48. );
  49. }
  50. function card(printName: string): HTMLElement {
  51. return screen.getByText(printName).closest('[data-archive-id]') as HTMLElement;
  52. }
  53. describe('ArchivesPage ?highlight=', () => {
  54. beforeEach(() => {
  55. setAuthToken(null);
  56. setViewMode('grid');
  57. beforeTheJobStarted = false;
  58. server.use(
  59. http.get('/api/v1/archives/', async () => {
  60. if (beforeTheJobStarted) return HttpResponse.json([archive(1, 'Benchy'), archive(2, 'Bracket')]);
  61. // Slower than the page's 100 ms scroll check, so nothing can be decided
  62. // on a list that is still on its way.
  63. await new Promise((resolve) => setTimeout(resolve, 300));
  64. return HttpResponse.json([archive(1, 'Benchy'), archive(2, 'Bracket'), archive(3, 'Just started')]);
  65. }),
  66. http.get('/api/v1/archives/stats', () => HttpResponse.json({ total_archives: 2 })),
  67. http.get('/api/v1/printers/', () => HttpResponse.json([{ id: 1, name: 'X1 Carbon' }])),
  68. http.get('/api/v1/projects/', () => HttpResponse.json([])),
  69. http.get('/api/v1/archives/tags', () => HttpResponse.json([])),
  70. );
  71. });
  72. afterEach(() => {
  73. window.history.pushState({}, '', '/');
  74. });
  75. it('marks the linked archive', async () => {
  76. window.history.pushState({}, '', '/archives?highlight=2');
  77. render(<ArchivesPage />);
  78. await screen.findByText('Bracket');
  79. await waitFor(() => expect(card('Bracket').style.outline).toContain('solid'));
  80. expect(card('Benchy').style.outline).toBe('');
  81. });
  82. it('leaves the log view for the grid, where the archive can be shown', async () => {
  83. setViewMode('log');
  84. window.history.pushState({}, '', '/archives?highlight=2');
  85. render(<ArchivesPage />);
  86. await waitFor(() => expect(card('Bracket').style.outline).toContain('solid'));
  87. });
  88. it('warns when the linked archive is not in the list', async () => {
  89. // Not merged into another either: its own lookup finds nothing.
  90. server.use(http.get('/api/v1/archives/99', () => HttpResponse.json({ detail: 'Archive not found' }, { status: 404 })));
  91. window.history.pushState({}, '', '/archives?highlight=99');
  92. render(<ArchivesPage />);
  93. expect(await screen.findByText('Linked archive not visible - try clearing filters')).toBeInTheDocument();
  94. });
  95. it('marks the archive a merged one went into (archive redesign P2)', async () => {
  96. server.use(http.get('/api/v1/archives/31', () => HttpResponse.json(archive(2, 'Bracket'))));
  97. window.history.pushState({}, '', '/archives?highlight=31');
  98. render(<ArchivesPage />);
  99. await waitFor(() => expect(card('Bracket').style.outline).toContain('solid'));
  100. expect(screen.queryByText(/not visible - try clearing filters/)).not.toBeInTheDocument();
  101. });
  102. it('looks for the archive in a fresh list, not one cached before it existed', async () => {
  103. beforeTheJobStarted = true;
  104. window.history.pushState({}, '', '/archives');
  105. render(<><ArchivesPage /><GoTo to="/archives?highlight=3" /></>);
  106. await screen.findByText('Bracket');
  107. beforeTheJobStarted = false;
  108. fireEvent.click(screen.getByText('go'));
  109. await waitFor(() => expect(card('Just started').style.outline).toContain('solid'));
  110. expect(screen.queryByText(/not visible - try clearing filters/)).not.toBeInTheDocument();
  111. });
  112. it('marks nothing without the parameter', async () => {
  113. window.history.pushState({}, '', '/archives');
  114. render(<ArchivesPage />);
  115. await screen.findByText('Bracket');
  116. expect(card('Bracket').style.outline).toBe('');
  117. expect(screen.queryByText(/not visible - try clearing filters/)).not.toBeInTheDocument();
  118. });
  119. });