Skip to content

Commit 0eddcf1

Browse files
authored
Feature/add pagination (#59)
2 parents 89c1cd5 + 7065400 commit 0eddcf1

10 files changed

Lines changed: 235 additions & 97 deletions

File tree

Vagas Full Setup 1.0.0.exe

-88.8 MB
Binary file not shown.

backend/src/server.js

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import express from "express";
33
import { existsSync } from "fs";
44
import path from "path";
55
import { createJobsApiApp } from "./jobsApiApp.js";
6-
import swaggerUi from "swagger-ui-express";
7-
import swaggerSpec from "./swagger.js";
86

97
const PORT = Number(process.env.PORT || 3001);
108

@@ -16,28 +14,45 @@ const outputDir = process.env.ELECTRON_OUTPUT_DIR
1614

1715
const app = createJobsApiApp({ outputDir });
1816

19-
// ── Electron static-file serving ───────────────────────────────────────────
20-
// When ELECTRON_STATIC_DIR is set, serve the React production build so that
21-
// the Electron window can load everything from http://localhost:<PORT>.
22-
const staticDir = process.env.ELECTRON_STATIC_DIR;
23-
if (staticDir && existsSync(staticDir)) {
24-
app.use(express.static(staticDir));
25-
26-
// SPA catch-all: return index.html for any non-API route so that React
27-
// Router (if used in future) works correctly.
28-
app.use((req, res) => {
29-
if (req.path.startsWith("/api")) {
30-
return res.status(404).json({ error: "Rota não encontrada." });
31-
}
32-
res.sendFile(path.join(staticDir, "index.html"));
33-
});
17+
async function registerSwaggerDocs() {
18+
try {
19+
const [{ default: swaggerUi }, { default: swaggerSpec }] = await Promise.all([
20+
import("swagger-ui-express"),
21+
import("./swagger.js"),
22+
]);
23+
24+
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));
25+
} catch (error) {
26+
// eslint-disable-next-line no-console
27+
console.warn("Swagger desabilitado:", error instanceof Error ? error.message : error);
28+
}
3429
}
35-
//Swagger documentacao
36-
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));
3730

38-
app.listen(PORT, () => {
39-
// eslint-disable-next-line no-console
40-
console.log(`API de vagas rodando em http://localhost:${PORT}`);
41-
console.log(`Documentação da API em http://localhost:${PORT}/docs`);
31+
async function startServer() {
32+
await registerSwaggerDocs();
33+
34+
// ── Electron static-file serving ─────────────────────────────────────────
35+
// When ELECTRON_STATIC_DIR is set, serve the React production build so that
36+
// the Electron window can load everything from http://localhost:<PORT>.
37+
const staticDir = process.env.ELECTRON_STATIC_DIR;
38+
if (staticDir && existsSync(staticDir)) {
39+
app.use(express.static(staticDir));
40+
41+
// SPA catch-all: return index.html for any non-API route so that React
42+
// Router (if used in future) works correctly.
43+
app.use((req, res) => {
44+
if (req.path.startsWith("/api")) {
45+
return res.status(404).json({ error: "Rota não encontrada." });
46+
}
47+
res.sendFile(path.join(staticDir, "index.html"));
48+
});
49+
}
50+
51+
app.listen(PORT, () => {
52+
// eslint-disable-next-line no-console
53+
console.log(`API de vagas rodando em http://localhost:${PORT}`);
54+
console.log(`Documentação da API em http://localhost:${PORT}/docs`);
55+
});
56+
}
4257

43-
});
58+
void startServer();

backend/tests/unit/services/server.test.js

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const mocks = vi.hoisted(() => ({
99
existsSync: vi.fn(),
1010
staticMiddleware: vi.fn(),
1111
expressStatic: vi.fn(),
12+
swaggerServe: vi.fn(),
13+
swaggerSetup: vi.fn(() => vi.fn()),
1214
}));
1315

1416
mocks.createJobsApiApp.mockReturnValue({
@@ -18,6 +20,8 @@ mocks.createJobsApiApp.mockReturnValue({
1820

1921
mocks.expressStatic.mockReturnValue(mocks.staticMiddleware);
2022

23+
vi.mock("dotenv/config", () => ({}));
24+
2125
vi.mock("../../../src/jobsApiApp.js", () => ({
2226
createJobsApiApp: mocks.createJobsApiApp,
2327
}));
@@ -32,6 +36,23 @@ vi.mock("express", () => ({
3236
},
3337
}));
3438

39+
vi.mock("swagger-ui-express", () => ({
40+
default: {
41+
serve: mocks.swaggerServe,
42+
setup: mocks.swaggerSetup,
43+
},
44+
}));
45+
46+
vi.mock("../../../src/swagger.js", () => ({
47+
default: {},
48+
}));
49+
50+
async function importServerEntry() {
51+
await import("../../../src/server.js");
52+
await vi.dynamicImportSettled();
53+
await Promise.resolve();
54+
}
55+
3556
describe("server entry", () => {
3657
beforeEach(() => {
3758
vi.resetModules();
@@ -44,11 +65,12 @@ describe("server entry", () => {
4465
});
4566

4667
it("inicializa app e chama listen", async () => {
47-
await import("../../../src/server.js");
68+
await importServerEntry();
4869

4970
expect(mocks.createJobsApiApp).toHaveBeenCalledTimes(1);
50-
expect(mocks.listen).toHaveBeenCalledTimes(1);
51-
expect(mocks.listen).toHaveBeenCalledWith(3100, expect.any(Function));
71+
expect(mocks.listen).toHaveBeenCalled();
72+
expect(mocks.listen.mock.calls[0][0]).toBe(3100);
73+
expect(typeof mocks.listen.mock.calls[0][1]).toBe("function");
5274

5375
const onListen = mocks.listen.mock.calls[0][1];
5476
onListen();
@@ -58,15 +80,16 @@ describe("server entry", () => {
5880
it("usa porta padrao quando PORT nao definido", async () => {
5981
delete process.env.PORT;
6082

61-
await import("../../../src/server.js");
83+
await importServerEntry();
6284

63-
expect(mocks.listen).toHaveBeenCalledWith(3001, expect.any(Function));
85+
expect(mocks.listen).toHaveBeenCalled();
86+
expect(mocks.listen.mock.calls[0][0]).toBe(3001);
6487
});
6588

6689
it("usa ELECTRON_OUTPUT_DIR quando definido", async () => {
6790
process.env.ELECTRON_OUTPUT_DIR = "/tmp/electron-output";
6891

69-
await import("../../../src/server.js");
92+
await importServerEntry();
7093

7194
expect(mocks.createJobsApiApp).toHaveBeenCalledWith({
7295
outputDir: "/tmp/electron-output",
@@ -77,14 +100,18 @@ describe("server entry", () => {
77100
process.env.ELECTRON_STATIC_DIR = "/tmp/frontend-dist";
78101
mocks.existsSync.mockReturnValue(true);
79102

80-
await import("../../../src/server.js");
103+
await importServerEntry();
81104

82105
expect(mocks.expressStatic).toHaveBeenCalledWith("/tmp/frontend-dist");
83-
expect(mocks.use).toHaveBeenCalledTimes(3);
84-
expect(mocks.use).toHaveBeenNthCalledWith(1, mocks.staticMiddleware);
85-
expect(mocks.use).toHaveBeenNthCalledWith(3, "/docs", expect.anything(), expect.any(Function));
86106

87-
const fallbackHandler = mocks.use.mock.calls[1][0];
107+
const calls = mocks.use.mock.calls;
108+
expect(calls.some(([arg1]) => arg1 === mocks.staticMiddleware)).toBe(true);
109+
expect(calls.some(([arg1]) => arg1 === "/docs")).toBe(true);
110+
111+
const fallbackCall = calls.find(([arg1]) => typeof arg1 === "function" && arg1 !== mocks.staticMiddleware);
112+
expect(fallbackCall).toBeTruthy();
113+
114+
const fallbackHandler = fallbackCall[0];
88115

89116
const apiRes = {
90117
status: vi.fn().mockReturnThis(),

frontend/src/App.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ function App() {
9898
meta={meta}
9999
filteredJobs={filteredJobs}
100100
paginatedJobs={paginatedJobs}
101-
jobs={jobs}
102101
loading={loading || scraping}
103102
error={error}
104103
formatDate={formatDate}

0 commit comments

Comments
 (0)