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,
|
||||
};
|
||||
}
|
||||
|
||||
/** @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