chore: lint build test
Node.js CI / CI (pull_request) Failing after 8s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 55s

This commit is contained in:
2026-02-19 16:24:21 -08:00
parent f28e2e37c0
commit f378a74199
12 changed files with 93 additions and 10 deletions
+4 -1
View File
@@ -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',
+8
View File
@@ -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$/,
}); });
} }
+24 -1
View File
@@ -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() };
}); });
} }
+32
View File
@@ -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);
},
}));
+2 -1
View File
@@ -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') { ❌ }
+2 -1
View File
@@ -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");
}); });
}); });
+2 -1
View File
@@ -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");
}); });
}); });
+2 -1
View File
@@ -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");
}); });
}); });
+2 -1
View File
@@ -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");
}); });
}); });
+2 -1
View File
@@ -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");
}); });
}); });