tests
This commit is contained in:
parent
1106e95ad6
commit
3d55e78614
105
orchestrator/src/client/components/JobHeader.test.tsx
Normal file
105
orchestrator/src/client/components/JobHeader.test.tsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||||
|
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
|
||||||
|
import { JobHeader } from "./JobHeader";
|
||||||
|
import { useSettings } from "../hooks/useSettings";
|
||||||
|
import * as api from "../api";
|
||||||
|
import type { Job } from "../../shared/types";
|
||||||
|
|
||||||
|
// Mock useSettings
|
||||||
|
vi.mock("../hooks/useSettings", () => ({
|
||||||
|
useSettings: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock api
|
||||||
|
vi.mock("../api", () => ({
|
||||||
|
checkSponsor: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock Tooltip components to simplify testing
|
||||||
|
vi.mock("@/components/ui/tooltip", () => ({
|
||||||
|
TooltipProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
||||||
|
Tooltip: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
||||||
|
TooltipTrigger: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
||||||
|
TooltipContent: ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<div data-testid="tooltip-content">{children}</div>
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mockJob: Job = {
|
||||||
|
id: "job-1",
|
||||||
|
title: "Software Engineer",
|
||||||
|
employer: "Tech Corp",
|
||||||
|
location: "London",
|
||||||
|
salary: "£60,000",
|
||||||
|
deadline: "2025-12-31",
|
||||||
|
status: "discovered",
|
||||||
|
source: "linkedin",
|
||||||
|
suitabilityScore: 85,
|
||||||
|
suitabilityReason: "Strong match",
|
||||||
|
sponsorMatchScore: null,
|
||||||
|
sponsorMatchNames: null,
|
||||||
|
// Other fields...
|
||||||
|
} as Job;
|
||||||
|
|
||||||
|
describe("JobHeader", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
(useSettings as any).mockReturnValue({
|
||||||
|
showSponsorInfo: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders basic job information", () => {
|
||||||
|
render(<JobHeader job={mockJob} />);
|
||||||
|
expect(screen.getByText("Software Engineer")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("Tech Corp")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("London")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("£60,000")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows 'Check Sponsorship Status' button when sponsorMatchScore is null", async () => {
|
||||||
|
const onCheckSponsor = vi.fn().mockResolvedValue(undefined);
|
||||||
|
render(<JobHeader job={mockJob} onCheckSponsor={onCheckSponsor} />);
|
||||||
|
|
||||||
|
const button = screen.getByText("Check Sponsorship Status");
|
||||||
|
expect(button).toBeInTheDocument();
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
|
||||||
|
expect(onCheckSponsor).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows 'Confirmed Sponsor' when score >= 95", () => {
|
||||||
|
const jobWithSponsor = { ...mockJob, sponsorMatchScore: 98, sponsorMatchNames: '["Tech Corp Ltd"]' };
|
||||||
|
render(<JobHeader job={jobWithSponsor} />);
|
||||||
|
|
||||||
|
expect(screen.getByText("Confirmed Sponsor")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows 'Potential Sponsor' when score is between 80 and 94", () => {
|
||||||
|
const jobWithPotential = { ...mockJob, sponsorMatchScore: 85, sponsorMatchNames: '["Techy Corp"]' };
|
||||||
|
render(<JobHeader job={jobWithPotential} />);
|
||||||
|
|
||||||
|
expect(screen.getByText("Potential Sponsor")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows 'Sponsor Not Found' when score < 80", () => {
|
||||||
|
const jobNoSponsor = { ...mockJob, sponsorMatchScore: 40, sponsorMatchNames: '["Other Corp"]' };
|
||||||
|
render(<JobHeader job={jobNoSponsor} />);
|
||||||
|
|
||||||
|
expect(screen.getByText("Sponsor Not Found")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hides sponsor info when showSponsorInfo is false", () => {
|
||||||
|
(useSettings as any).mockReturnValue({
|
||||||
|
showSponsorInfo: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const jobWithSponsor = { ...mockJob, sponsorMatchScore: 98 };
|
||||||
|
render(<JobHeader job={jobWithSponsor} />);
|
||||||
|
|
||||||
|
expect(screen.queryByText("Confirmed Sponsor")).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("Check Sponsorship Status")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
66
orchestrator/src/client/hooks/useSettings.test.ts
Normal file
66
orchestrator/src/client/hooks/useSettings.test.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { renderHook, waitFor } from '@testing-library/react';
|
||||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
|
import { useSettings, _resetSettingsCache } from './useSettings';
|
||||||
|
import * as api from '../api';
|
||||||
|
|
||||||
|
vi.mock('../api', () => ({
|
||||||
|
getSettings: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('useSettings', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
_resetSettingsCache();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fetches settings on mount if not already cached', async () => {
|
||||||
|
const mockSettings = { showSponsorInfo: false };
|
||||||
|
(api.getSettings as any).mockResolvedValue(mockSettings);
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useSettings());
|
||||||
|
|
||||||
|
// Should start in loading state
|
||||||
|
expect(result.current.settings).toBeNull();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.settings).toEqual(mockSettings);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.showSponsorInfo).toBe(false);
|
||||||
|
expect(api.getSettings).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses default values when settings are null', async () => {
|
||||||
|
(api.getSettings as any).mockResolvedValue(null);
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useSettings());
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
// settings is null, so showSponsorInfo should default to true
|
||||||
|
expect(result.current.showSponsorInfo).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('provides a refresh function that updates settings', async () => {
|
||||||
|
const initialSettings = { showSponsorInfo: true };
|
||||||
|
const updatedSettings = { showSponsorInfo: false };
|
||||||
|
|
||||||
|
(api.getSettings as any).mockResolvedValueOnce(initialSettings);
|
||||||
|
(api.getSettings as any).mockResolvedValueOnce(updatedSettings);
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useSettings());
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.settings).toEqual(initialSettings);
|
||||||
|
});
|
||||||
|
|
||||||
|
let refreshed;
|
||||||
|
await waitFor(async () => {
|
||||||
|
refreshed = await result.current.refreshSettings();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(refreshed).toEqual(updatedSettings);
|
||||||
|
expect(result.current.settings).toEqual(updatedSettings);
|
||||||
|
expect(result.current.showSponsorInfo).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -56,3 +56,10 @@ export function useSettings() {
|
|||||||
refreshSettings,
|
refreshSettings,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @internal For testing only */
|
||||||
|
export function _resetSettingsCache() {
|
||||||
|
settingsCache = null;
|
||||||
|
isFetching = false;
|
||||||
|
subscribers.clear();
|
||||||
|
}
|
||||||
|
|||||||
@ -95,4 +95,26 @@ describe.sequential('Jobs API routes', () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('checks visa sponsor status for a job', async () => {
|
||||||
|
const { searchSponsors } = await import('../../services/visa-sponsors/index.js');
|
||||||
|
vi.mocked(searchSponsors).mockReturnValue([
|
||||||
|
{ sponsor: { organisationName: 'ACME CORP SPONSOR' } as any, score: 100, matchedName: 'acme corp sponsor' }
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { createJob } = await import('../../repositories/jobs.js');
|
||||||
|
const job = await createJob({
|
||||||
|
source: 'manual',
|
||||||
|
title: 'Sponsored Dev',
|
||||||
|
employer: 'Acme',
|
||||||
|
jobUrl: 'https://example.com/job/4',
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await fetch(`${baseUrl}/api/jobs/${job.id}/check-sponsor`, { method: 'POST' });
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
expect(body.success).toBe(true);
|
||||||
|
expect(body.data.sponsorMatchScore).toBe(100);
|
||||||
|
expect(body.data.sponsorMatchNames).toContain('ACME CORP SPONSOR');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user