Move from Create React to Vite
Some checks failed
CI / Go tests & lint (push) Successful in 9s
CI / Frontend tests & type-check (push) Failing after 14s

This commit is contained in:
2026-04-09 10:44:50 -03:00
parent a8e170f7e1
commit 8344bf016f
27 changed files with 1746 additions and 16621 deletions

View File

@@ -1,17 +1,18 @@
import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { vi, type Mock } from 'vitest';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import Activate from './Activate';
import { api } from '../api';
import { AuthProvider } from '../auth';
jest.mock('../api', () => ({
vi.mock('../api', () => ({
api: {
activate: jest.fn(),
activate: vi.fn(),
},
}));
const mockActivate = api.activate as jest.Mock;
const mockActivate = api.activate as Mock;
function renderActivate(token = 'valid-token') {
return render(

View File

@@ -1,14 +1,15 @@
import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { vi, type Mock } from 'vitest';
import { MemoryRouter } from 'react-router-dom';
import Profile from './Profile';
import { api, Volunteer } from '../api';
import { AuthProvider } from '../auth';
jest.mock('../api', () => ({
vi.mock('../api', () => ({
api: {
getVolunteer: jest.fn(),
updateVolunteer: jest.fn(),
getVolunteer: vi.fn(),
updateVolunteer: vi.fn(),
},
}));
@@ -21,8 +22,8 @@ function buildFakeJWT(payload: object): string {
return `${header}.${body}.fakesig`;
}
const mockGetVolunteer = api.getVolunteer as jest.Mock;
const mockUpdateVolunteer = api.updateVolunteer as jest.Mock;
const mockGetVolunteer = api.getVolunteer as Mock;
const mockUpdateVolunteer = api.updateVolunteer as Mock;
const baseVolunteer: Volunteer = {
id: 5,

View File

@@ -1,32 +1,36 @@
import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { vi, type Mock } from 'vitest';
import { MemoryRouter } from 'react-router-dom';
import Schedules from './Schedules';
import { api, ShiftInstance, ShiftTemplate } from '../api';
jest.mock('../api', () => ({
...jest.requireActual('../api'),
api: {
listShifts: jest.fn(),
listShiftTemplates: jest.fn(),
listVolunteers: jest.fn(),
listTimeOff: jest.fn(),
generateShifts: jest.fn(),
publishShifts: jest.fn(),
unpublishShifts: jest.fn(),
updateShift: jest.fn(),
confirmShift: jest.fn(),
createShiftTemplate: jest.fn(),
updateShiftTemplate: jest.fn(),
deleteShiftTemplate: jest.fn(),
},
vi.mock('../api', async () => {
const actual = await vi.importActual<typeof import('../api')>('../api');
return {
...actual,
api: {
listShifts: vi.fn(),
listShiftTemplates: vi.fn(),
listVolunteers: vi.fn(),
listTimeOff: vi.fn(),
generateShifts: vi.fn(),
publishShifts: vi.fn(),
unpublishShifts: vi.fn(),
updateShift: vi.fn(),
confirmShift: vi.fn(),
createShiftTemplate: vi.fn(),
updateShiftTemplate: vi.fn(),
deleteShiftTemplate: vi.fn(),
},
};
});
vi.mock('../auth', () => ({
useAuth: vi.fn(),
}));
jest.mock('../auth', () => ({
useAuth: jest.fn(),
}));
const { useAuth } = require('../auth');
const { useAuth } = await import('../auth');
const mockDraftInstance: ShiftInstance = {
id: 1,
@@ -75,8 +79,8 @@ function renderAt(path: string) {
describe('Schedules (volunteer view)', () => {
beforeEach(() => {
useAuth.mockReturnValue({ role: 'volunteer', volunteerID: 10 });
(api.listShifts as jest.Mock).mockResolvedValue([mockPublishedInstance]);
(useAuth as Mock).mockReturnValue({ role: 'volunteer', volunteerID: 10 });
(api.listShifts as Mock).mockResolvedValue([mockPublishedInstance]);
});
it('renders published shifts for a volunteer', async () => {
@@ -98,9 +102,9 @@ describe('Schedules (volunteer view)', () => {
describe('Schedules (admin shifts view)', () => {
beforeEach(() => {
useAuth.mockReturnValue({ role: 'admin', volunteerID: 1 });
(api.listShifts as jest.Mock).mockResolvedValue([mockDraftInstance]);
(api.listShiftTemplates as jest.Mock).mockResolvedValue([mockTemplate]);
(useAuth as Mock).mockReturnValue({ role: 'admin', volunteerID: 1 });
(api.listShifts as Mock).mockResolvedValue([mockDraftInstance]);
(api.listShiftTemplates as Mock).mockResolvedValue([mockTemplate]);
});
it('shows Generate and Publish buttons when drafts exist', async () => {
@@ -111,7 +115,7 @@ describe('Schedules (admin shifts view)', () => {
});
it('calls generateShifts on Generate click', async () => {
(api.generateShifts as jest.Mock).mockResolvedValue([mockDraftInstance]);
(api.generateShifts as Mock).mockResolvedValue([mockDraftInstance]);
renderAt('/schedules');
await waitFor(() => expect(screen.getByText('Generate')).toBeInTheDocument());
fireEvent.click(screen.getByText('Generate'));
@@ -119,8 +123,8 @@ describe('Schedules (admin shifts view)', () => {
});
it('calls publishShifts on Publish click', async () => {
(api.publishShifts as jest.Mock).mockResolvedValue({ year: 2026, month: 4 });
(api.listShifts as jest.Mock)
(api.publishShifts as Mock).mockResolvedValue({ year: 2026, month: 4 });
(api.listShifts as Mock)
.mockResolvedValueOnce([mockDraftInstance])
.mockResolvedValue([{ ...mockDraftInstance, status: 'published' }]);
@@ -131,7 +135,7 @@ describe('Schedules (admin shifts view)', () => {
});
it('shows Unpublish button when all shifts are published', async () => {
(api.listShifts as jest.Mock).mockResolvedValue([mockPublishedInstance]);
(api.listShifts as Mock).mockResolvedValue([mockPublishedInstance]);
renderAt('/schedules');
await waitFor(() => expect(screen.getByText('Unpublish')).toBeInTheDocument());
});
@@ -143,11 +147,11 @@ describe('Schedules (admin shifts view)', () => {
});
it('opens edit form with volunteer checkboxes when Edit is clicked', async () => {
(api.listVolunteers as jest.Mock).mockResolvedValue([
(api.listVolunteers as Mock).mockResolvedValue([
{ id: 5, name: 'Alice', active: true, operational_roles: 'Dog Shelter Volunteer', is_trainee: false },
{ id: 6, name: 'Bob', active: true, operational_roles: 'Behaviour Team', is_trainee: false },
]);
(api.listTimeOff as jest.Mock).mockResolvedValue([]);
(api.listTimeOff as Mock).mockResolvedValue([]);
renderAt('/schedules');
await waitFor(() => expect(screen.getByText('Edit')).toBeInTheDocument());
fireEvent.click(screen.getByText('Edit'));
@@ -157,11 +161,11 @@ describe('Schedules (admin shifts view)', () => {
});
it('hides volunteers with approved time off on the shift date', async () => {
(api.listVolunteers as jest.Mock).mockResolvedValue([
(api.listVolunteers as Mock).mockResolvedValue([
{ id: 5, name: 'Alice', active: true, operational_roles: 'Dog Shelter Volunteer', is_trainee: false },
{ id: 6, name: 'Bob', active: true, operational_roles: 'Behaviour Team', is_trainee: false },
]);
(api.listTimeOff as jest.Mock).mockResolvedValue([
(api.listTimeOff as Mock).mockResolvedValue([
{ id: 1, volunteer_id: 6, starts_at: '2026-04-06T00:00:00Z', ends_at: '2026-04-07T00:00:00Z', status: 'approved' },
]);
renderAt('/schedules');
@@ -183,8 +187,8 @@ describe('Schedules (admin shifts view)', () => {
describe('Schedules (admin templates view)', () => {
beforeEach(() => {
useAuth.mockReturnValue({ role: 'admin', volunteerID: 1 });
(api.listShiftTemplates as jest.Mock).mockResolvedValue([mockTemplate]);
(useAuth as Mock).mockReturnValue({ role: 'admin', volunteerID: 1 });
(api.listShiftTemplates as Mock).mockResolvedValue([mockTemplate]);
});
it('renders templates at /schedules/templates', async () => {
@@ -201,8 +205,8 @@ describe('Schedules (admin templates view)', () => {
});
it('deletes a template', async () => {
(api.deleteShiftTemplate as jest.Mock).mockResolvedValue(undefined);
window.confirm = jest.fn().mockReturnValue(true);
(api.deleteShiftTemplate as Mock).mockResolvedValue(undefined);
window.confirm = vi.fn().mockReturnValue(true);
renderAt('/schedules/templates');
await waitFor(() => expect(screen.getAllByText('Delete')[0]).toBeInTheDocument());
fireEvent.click(screen.getAllByText('Delete')[0]);

View File

@@ -1,24 +1,25 @@
import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { vi, type Mock } from 'vitest';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import Setup from './Setup';
import { api } from '../api';
import { AuthProvider } from '../auth';
jest.mock('../api', () => ({
vi.mock('../api', () => ({
api: {
getSetupStatus: jest.fn(),
createSetupAdmin: jest.fn(),
getSetupStatus: vi.fn(),
createSetupAdmin: vi.fn(),
},
}));
// Mock useSetup from App to provide setNeedsSetup
const mockSetNeedsSetup = jest.fn();
jest.mock('../App', () => ({
const mockSetNeedsSetup = vi.fn();
vi.mock('../App', () => ({
useSetup: () => ({ setNeedsSetup: mockSetNeedsSetup }),
}));
const mockCreateSetupAdmin = api.createSetupAdmin as jest.Mock;
const mockCreateSetupAdmin = api.createSetupAdmin as Mock;
function renderSetup() {
return render(

View File

@@ -1,24 +1,25 @@
import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { vi, type Mock } from 'vitest';
import { MemoryRouter } from 'react-router-dom';
import Volunteers from './Volunteers';
import { api, AdminVolunteer } from '../api';
import { AuthProvider } from '../auth';
jest.mock('../api', () => ({
vi.mock('../api', () => ({
api: {
listVolunteers: jest.fn(),
createVolunteer: jest.fn(),
updateVolunteer: jest.fn(),
resendInvite: jest.fn(),
listVolunteers: vi.fn(),
createVolunteer: vi.fn(),
updateVolunteer: vi.fn(),
resendInvite: vi.fn(),
},
OPERATIONAL_ROLES: ['Behaviour Team', 'Dog Log Monitor', 'Dog Shelter Volunteer', 'Trainee', 'Floater'],
}));
const mockListVolunteers = api.listVolunteers as jest.Mock;
const mockCreateVolunteer = api.createVolunteer as jest.Mock;
const mockUpdateVolunteer = api.updateVolunteer as jest.Mock;
const mockResendInvite = api.resendInvite as jest.Mock;
const mockListVolunteers = api.listVolunteers as Mock;
const mockCreateVolunteer = api.createVolunteer as Mock;
const mockUpdateVolunteer = api.updateVolunteer as Mock;
const mockResendInvite = api.resendInvite as Mock;
function buildFakeJWT(payload: object): string {
const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));