generated from nhcarrigan/template
feat: multiple improvements to library functionality #50
+4
-1
@@ -3,7 +3,10 @@ module.exports = {
|
|||||||
preset: '../jest.preset.js',
|
preset: '../jest.preset.js',
|
||||||
testEnvironment: 'node',
|
testEnvironment: 'node',
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
|
'^.+\\.[tj]s$': ['ts-jest', {
|
||||||
|
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||||
|
isolatedModules: true,
|
||||||
|
}],
|
||||||
},
|
},
|
||||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||||
coverageDirectory: '../coverage/api',
|
coverageDirectory: '../coverage/api',
|
||||||
|
|||||||
@@ -57,5 +57,13 @@ export async function app(fastify: FastifyInstance, opts: AppOptions) {
|
|||||||
fastify.register(AutoLoad, {
|
fastify.register(AutoLoad, {
|
||||||
dir: path.join(__dirname, 'routes'),
|
dir: path.join(__dirname, 'routes'),
|
||||||
options: { ...opts, prefix: '/api' },
|
options: { ...opts, prefix: '/api' },
|
||||||
|
ignorePattern: /root\.ts$/,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register root route without prefix
|
||||||
|
fastify.register(AutoLoad, {
|
||||||
|
dir: path.join(__dirname, 'routes'),
|
||||||
|
options: { ...opts },
|
||||||
|
matchFilter: /root\.ts$/,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,30 @@
|
|||||||
import { FastifyInstance } from 'fastify';
|
import { FastifyInstance } from 'fastify';
|
||||||
|
import { readFileSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
interface PackageJson {
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cachedVersion: string | null = null;
|
||||||
|
|
||||||
|
function getVersion(): string {
|
||||||
|
if (cachedVersion) {
|
||||||
|
return cachedVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const packageJsonPath = join(process.cwd(), 'package.json');
|
||||||
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as PackageJson;
|
||||||
|
cachedVersion = packageJson.version;
|
||||||
|
return cachedVersion;
|
||||||
|
} catch {
|
||||||
|
return 'unknown';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default async function (fastify: FastifyInstance) {
|
export default async function (fastify: FastifyInstance) {
|
||||||
fastify.get('/', async function () {
|
fastify.get('/', async function () {
|
||||||
return { message: 'Hello API' };
|
return { version: getVersion() };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,3 +13,35 @@ process.env.API_URL = 'http://localhost:3000/api';
|
|||||||
process.env.DATABASE_URL = 'postgresql://test:test@localhost:5432/test';
|
process.env.DATABASE_URL = 'postgresql://test:test@localhost:5432/test';
|
||||||
process.env.BASE_URL = 'http://localhost:4200';
|
process.env.BASE_URL = 'http://localhost:4200';
|
||||||
process.env.NODE_ENV = 'test';
|
process.env.NODE_ENV = 'test';
|
||||||
|
|
||||||
|
// Mock ESM packages to avoid import issues in Jest
|
||||||
|
jest.mock('jsdom', () => ({
|
||||||
|
JSDOM: class {
|
||||||
|
window = {
|
||||||
|
document: {
|
||||||
|
createElement: jest.fn(() => ({})),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('marked', () => ({
|
||||||
|
marked: jest.fn((input: string) => `<p>${input}</p>`),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('dompurify', () => {
|
||||||
|
const mockDOMPurify = {
|
||||||
|
sanitize: jest.fn((input: string) => input),
|
||||||
|
addHook: jest.fn(),
|
||||||
|
};
|
||||||
|
const createDOMPurify = jest.fn(() => mockDOMPurify);
|
||||||
|
return createDOMPurify;
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock('@nhcarrigan/logger', () => ({
|
||||||
|
Logger: class {
|
||||||
|
log = jest.fn().mockResolvedValue(undefined);
|
||||||
|
error = jest.fn().mockResolvedValue(undefined);
|
||||||
|
metric = jest.fn().mockResolvedValue(undefined);
|
||||||
|
},
|
||||||
|
}));
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
"jest.config.ts",
|
"jest.config.ts",
|
||||||
"jest.config.cts",
|
"jest.config.cts",
|
||||||
"src/**/*.spec.ts",
|
"src/**/*.spec.ts",
|
||||||
"src/**/*.test.ts"
|
"src/**/*.test.ts",
|
||||||
|
"src/test-setup.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ import { ApiService } from '../../services/api.service';
|
|||||||
[alt]="user.username"
|
[alt]="user.username"
|
||||||
class="user-avatar"
|
class="user-avatar"
|
||||||
(click)="toggleDropdown()"
|
(click)="toggleDropdown()"
|
||||||
|
(keyup.enter)="toggleDropdown()"
|
||||||
|
(keyup.space)="toggleDropdown()"
|
||||||
|
tabindex="0"
|
||||||
|
role="button"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@if (showDropdown()) {
|
@if (showDropdown()) {
|
||||||
|
|||||||
@@ -6,7 +6,14 @@
|
|||||||
|
|
||||||
<div class="toast-container">
|
<div class="toast-container">
|
||||||
@for (toast of toastService.toastList(); track toast.id) {
|
@for (toast of toastService.toastList(); track toast.id) {
|
||||||
<div class="toast toast-{{ toast.type }}" (click)="toastService.remove(toast.id)">
|
<div
|
||||||
|
class="toast toast-{{ toast.type }}"
|
||||||
|
(click)="toastService.remove(toast.id)"
|
||||||
|
(keyup.enter)="toastService.remove(toast.id)"
|
||||||
|
(keyup.space)="toastService.remove(toast.id)"
|
||||||
|
tabindex="0"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
<div class="toast-icon">
|
<div class="toast-icon">
|
||||||
@switch (toast.type) {
|
@switch (toast.type) {
|
||||||
@case ('error') { ❌ }
|
@case ('error') { ❌ }
|
||||||
|
|||||||
@@ -17,10 +17,11 @@ describe("book Types", () => {
|
|||||||
|
|
||||||
it("should have all expected enum values", () => {
|
it("should have all expected enum values", () => {
|
||||||
const values = Object.values(BookStatus);
|
const values = Object.values(BookStatus);
|
||||||
expect(values).toHaveLength(3);
|
expect(values).toHaveLength(4);
|
||||||
expect(values).toContain("READING");
|
expect(values).toContain("READING");
|
||||||
expect(values).toContain("FINISHED");
|
expect(values).toContain("FINISHED");
|
||||||
expect(values).toContain("TO_READ");
|
expect(values).toContain("TO_READ");
|
||||||
|
expect(values).toContain("RETIRED");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,11 @@ describe("game Types", () => {
|
|||||||
|
|
||||||
it("should have all expected enum values", () => {
|
it("should have all expected enum values", () => {
|
||||||
const values = Object.values(GameStatus);
|
const values = Object.values(GameStatus);
|
||||||
expect(values).toHaveLength(3);
|
expect(values).toHaveLength(4);
|
||||||
expect(values).toContain("PLAYING");
|
expect(values).toContain("PLAYING");
|
||||||
expect(values).toContain("COMPLETED");
|
expect(values).toContain("COMPLETED");
|
||||||
expect(values).toContain("BACKLOG");
|
expect(values).toContain("BACKLOG");
|
||||||
|
expect(values).toContain("RETIRED");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,11 @@ describe("manga Types", () => {
|
|||||||
|
|
||||||
it("should have all expected enum values", () => {
|
it("should have all expected enum values", () => {
|
||||||
const values = Object.values(MangaStatus);
|
const values = Object.values(MangaStatus);
|
||||||
expect(values).toHaveLength(3);
|
expect(values).toHaveLength(4);
|
||||||
expect(values).toContain("READING");
|
expect(values).toContain("READING");
|
||||||
expect(values).toContain("COMPLETED");
|
expect(values).toContain("COMPLETED");
|
||||||
expect(values).toContain("WANT_TO_READ");
|
expect(values).toContain("WANT_TO_READ");
|
||||||
|
expect(values).toContain("RETIRED");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -33,10 +33,11 @@ describe("music Types", () => {
|
|||||||
|
|
||||||
it("should have all expected enum values", () => {
|
it("should have all expected enum values", () => {
|
||||||
const values = Object.values(MusicStatus);
|
const values = Object.values(MusicStatus);
|
||||||
expect(values).toHaveLength(3);
|
expect(values).toHaveLength(4);
|
||||||
expect(values).toContain("LISTENING");
|
expect(values).toContain("LISTENING");
|
||||||
expect(values).toContain("COMPLETED");
|
expect(values).toContain("COMPLETED");
|
||||||
expect(values).toContain("WANT_TO_LISTEN");
|
expect(values).toContain("WANT_TO_LISTEN");
|
||||||
|
expect(values).toContain("RETIRED");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -35,10 +35,11 @@ describe("show Types", () => {
|
|||||||
|
|
||||||
it("should have all expected enum values", () => {
|
it("should have all expected enum values", () => {
|
||||||
const values = Object.values(ShowStatus);
|
const values = Object.values(ShowStatus);
|
||||||
expect(values).toHaveLength(3);
|
expect(values).toHaveLength(4);
|
||||||
expect(values).toContain("WATCHING");
|
expect(values).toContain("WATCHING");
|
||||||
expect(values).toContain("COMPLETED");
|
expect(values).toContain("COMPLETED");
|
||||||
expect(values).toContain("WANT_TO_WATCH");
|
expect(values).toContain("WANT_TO_WATCH");
|
||||||
|
expect(values).toContain("RETIRED");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user