fix: Merge dev into feat/assignment-page
This commit is contained in:
commit
bb3a242bf9
20 changed files with 207 additions and 37 deletions
|
@ -10,11 +10,10 @@
|
|||
"preview": "vite preview",
|
||||
"type-check": "vue-tsc --build",
|
||||
"format": "prettier --write src/",
|
||||
"test:e2e": "playwright test",
|
||||
"format-check": "prettier --check src/",
|
||||
"lint": "eslint . --fix",
|
||||
"pretest:unit": "tsx ../docs/api/generate.ts && npm run build",
|
||||
"test:unit": "vitest --run"
|
||||
"test:unit": "vitest --run",
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.69.0",
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
import MenuBar from "@/components/MenuBar.vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { computed } from "vue";
|
||||
import authService from "@/services/auth/auth-service.ts";
|
||||
|
||||
void authService.loadUser();
|
||||
|
||||
const route = useRoute();
|
||||
interface RouteMeta {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
import auth from "@/services/auth/auth-service.ts";
|
||||
|
||||
|
@ -10,9 +11,10 @@
|
|||
const { t, locale } = useI18n();
|
||||
|
||||
const role = auth.authState.activeRole;
|
||||
const _router = useRouter(); // Zonder '_' gaf dit een linter error voor unused variable
|
||||
|
||||
const name = ref(auth.authState.user!.profile.name!);
|
||||
const initials: string = name.value
|
||||
const name: string = auth.authState.user!.profile.name!;
|
||||
const initials: string = name
|
||||
.split(" ")
|
||||
.map((n) => n[0])
|
||||
.join("");
|
||||
|
|
|
@ -1,8 +1,24 @@
|
|||
export const apiConfig = {
|
||||
baseUrl:
|
||||
window.location.hostname === "localhost" && !(window.location.port === "80" || window.location.port === "")
|
||||
? "http://localhost:3000/api"
|
||||
: window.location.origin + "/api",
|
||||
baseUrl: ((): string => {
|
||||
if (import.meta.env.MODE === "test") {
|
||||
// TODO Remove hardcoding
|
||||
return "http://localhost:9876/api";
|
||||
}
|
||||
|
||||
if (import.meta.env.VITE_API_BASE_URL) {
|
||||
return import.meta.env.VITE_API_BASE_URL;
|
||||
}
|
||||
|
||||
if (
|
||||
window.location.hostname === "localhost" &&
|
||||
!(window.location.port === "80" || window.location.port === "")
|
||||
) {
|
||||
return "http://localhost:3000/api";
|
||||
}
|
||||
|
||||
// Fallback to the current origin with "/api" suffix
|
||||
return `${window.location.origin}/api`;
|
||||
})(),
|
||||
};
|
||||
|
||||
export const loginRoute = "/login";
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import apiClient from "@/services/api-client/api-client.ts";
|
||||
import type { AxiosResponse, ResponseType } from "axios";
|
||||
import { HttpErrorResponseException } from "@/exception/http-error-response-exception.ts";
|
||||
import { apiConfig } from "@/config.ts";
|
||||
|
||||
export abstract class BaseController {
|
||||
protected basePath: string;
|
||||
|
@ -16,9 +17,18 @@ export abstract class BaseController {
|
|||
}
|
||||
|
||||
protected async get<T>(path: string, queryParams?: QueryParams, responseType?: ResponseType): Promise<T> {
|
||||
const response = await apiClient.get<T>(this.absolutePathFor(path), { params: queryParams, responseType });
|
||||
BaseController.assertSuccessResponse(response);
|
||||
return response.data;
|
||||
try {
|
||||
const response = await apiClient.get<T>(this.absolutePathFor(path), { params: queryParams, responseType });
|
||||
BaseController.assertSuccessResponse(response);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
if (error instanceof HttpErrorResponseException) {
|
||||
throw error;
|
||||
}
|
||||
throw new Error(
|
||||
`An unexpected error occurred while fetching data from ${apiConfig.baseUrl}${this.absolutePathFor(path)}: ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected async post<T>(path: string, body: unknown, queryParams?: QueryParams): Promise<T> {
|
||||
|
|
|
@ -12,11 +12,9 @@ import App from "./App.vue";
|
|||
import router from "./router";
|
||||
import { aliases, mdi } from "vuetify/iconsets/mdi";
|
||||
import { VueQueryPlugin, QueryClient } from "@tanstack/vue-query";
|
||||
import authService from "./services/auth/auth-service.ts";
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
await authService.loadUser();
|
||||
app.use(router);
|
||||
|
||||
const link = document.createElement("link");
|
||||
|
|
|
@ -142,7 +142,7 @@ const router = createRouter({
|
|||
router.beforeEach(async (to, _from, next) => {
|
||||
// Verify if user is logged in before accessing certain routes
|
||||
if (to.meta.requiresAuth) {
|
||||
if (!authService.isLoggedIn.value) {
|
||||
if (!authService.isLoggedIn.value && !(await authService.loadUser())) {
|
||||
next("/login");
|
||||
} else {
|
||||
next();
|
||||
|
|
|
@ -37,7 +37,20 @@ async function loadUser(): Promise<User | null> {
|
|||
if (!activeRole) {
|
||||
return null;
|
||||
}
|
||||
const user = await (await getUserManagers())[activeRole].getUser();
|
||||
|
||||
const userManager = (await getUserManagers())[activeRole];
|
||||
let user = await userManager.getUser(); // Load the user from the local storage.
|
||||
if (!user) {
|
||||
// If the user is not in the local storage, he could still be authenticated in Keycloak.
|
||||
try {
|
||||
user = await userManager.signinSilent();
|
||||
} catch (_: unknown) {
|
||||
// When the user was previously logged in and then logged out, signinSilent throws an error.
|
||||
// In that case, the user is not authenticated anymore, so we set him to null.
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
|
||||
setUserAuthInfo(user);
|
||||
authState.activeRole = activeRole ?? null;
|
||||
return user;
|
||||
|
|
19
frontend/tests/controllers/student-controller.test.ts
Normal file
19
frontend/tests/controllers/student-controller.test.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { StudentController } from "../../src/controllers/students";
|
||||
import { expect, it, describe, afterAll, beforeAll } from "vitest";
|
||||
import { setup, teardown } from "../setup-backend.js";
|
||||
|
||||
describe("Test controller students", () => {
|
||||
beforeAll(async () => {
|
||||
await setup();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await teardown();
|
||||
});
|
||||
|
||||
it("Get students", async () => {
|
||||
const controller = new StudentController();
|
||||
const data = await controller.getAll(true);
|
||||
expect(data.students).to.have.length.greaterThan(0);
|
||||
});
|
||||
});
|
40
frontend/tests/setup-backend.ts
Normal file
40
frontend/tests/setup-backend.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { spawn } from "child_process";
|
||||
import { ChildProcess, spawnSync } from "node:child_process";
|
||||
|
||||
let backendProcess: ChildProcess;
|
||||
|
||||
async function waitForEndpoint(url: string, delay = 1000, retries = 60): Promise<void> {
|
||||
try {
|
||||
await fetch(url);
|
||||
} catch {
|
||||
// Endpoint is not ready yet
|
||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||
// Retry
|
||||
await waitForEndpoint(url, delay, retries - 1);
|
||||
}
|
||||
}
|
||||
|
||||
export async function setup(): Promise<void> {
|
||||
// Precompile needed packages
|
||||
spawnSync("npx", ["tsc", "--build", "tsconfig.json"], {
|
||||
cwd: `../common`,
|
||||
});
|
||||
|
||||
// Spin up the backend
|
||||
backendProcess = spawn("npx", ["tsx", "--env-file=.env.test", "tool/startTestApp.ts"], {
|
||||
cwd: `../backend`,
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_ENV: "test",
|
||||
},
|
||||
});
|
||||
|
||||
// Wait until you can curl the backend
|
||||
await waitForEndpoint("http://localhost:9876/api");
|
||||
}
|
||||
|
||||
export async function teardown(): Promise<void> {
|
||||
if (backendProcess) {
|
||||
backendProcess.kill();
|
||||
}
|
||||
}
|
|
@ -9,6 +9,13 @@ export default mergeConfig(
|
|||
environment: "jsdom",
|
||||
exclude: [...configDefaults.exclude, "e2e/**"],
|
||||
root: fileURLToPath(new URL("./", import.meta.url)),
|
||||
|
||||
/*
|
||||
* The test-backend server can be started for each test-file individually using `beforeAll(() => setup())`,
|
||||
* or for all tests once using:
|
||||
globalSetup: ["./tests/setup-backend.ts"],
|
||||
* In this project, the backend server is started for each test-file individually.
|
||||
*/
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue