Skip to content

Commit

Permalink
adjust sonar, add route guard tests
Browse files Browse the repository at this point in the history
  • Loading branch information
MarvinOehlerkingCap committed Aug 5, 2024
1 parent 25d5be6 commit d5c20c2
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 17 deletions.
6 changes: 3 additions & 3 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ const config = deepmerge(defaultPreset, {

collectCoverageFrom: [
// Include
"<rootDir>/src/layouts/**/*.{js,ts,vue}",
"<rootDir>/src/modules/**/*.{js,ts,vue}",
"<rootDir>/src/layouts/**/*.{js,ts}", // add vue files
"<rootDir>/src/modules/**/*.{js,ts}", // add vue files
"<rootDir>/src/plugins/**/*.(js|ts)",
// "<rootDir>/src/router/guards/**/*.(js|ts)",
"<rootDir>/src/router/guards/**/*.(js|ts)",
"<rootDir>/src/utils/**/*.(js|ts)",

// Exclude
Expand Down
6 changes: 3 additions & 3 deletions sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ sonar.organization=schulcloud-verbund
sonar.projectKey=hpi-schul-cloud_shd-client

sonar.sources=.
sonar.tests=.
sonar.tests=src/

# Exclude test files, locales and generated code from source scope
sonar.exclusions=tests/**/*,**/*.unit.ts,**/*.unit.js,src/serverApi/**/*,**/locales/**.ts
sonar.exclusions=src/serverApi/**/*,**/locales/**.ts

# Include test files in test scope
# Include test files and test utils in test scope
sonar.test.inclusions=tests/**/*,**/*.unit.ts,**/*.unit.js

# Coverage report directory of jest
Expand Down
13 changes: 10 additions & 3 deletions src/modules/page/AboutView.unit.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { createTestingVuetify } from "@@/tests/test-utils/setup";
import { mount } from "@vue/test-utils";
import { VBtn } from "vuetify/lib/components/index.mjs";
import AboutView from "./AboutView.vue";

describe("AboutView", () => {
const getWrapper = () => {
const wrapper = mount(AboutView);
const wrapper = mount(AboutView, {
global: { plugins: [createTestingVuetify()] },
});

return {
wrapper,
};
};

it("should improve the code coverage", () => {
it("should increase the counter button on click", async () => {
const { wrapper } = getWrapper();

expect(wrapper).toBeDefined();
const counter = wrapper.getComponent(VBtn);
await counter.trigger("click");

expect(counter.text()).toEqual("1");
});
});
7 changes: 6 additions & 1 deletion src/modules/page/AboutView.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
<template>
<main>
<h3>This is the new and improved Superhero-Dashboard!</h3>
<VBtn @click="counter++">{{ counter }}</VBtn>
</main>
</template>

<script setup lang="ts"></script>
<script setup lang="ts">
import { Ref, ref } from "vue";
const counter: Ref<number> = ref(0);
</script>
2 changes: 1 addition & 1 deletion src/router/guards/is-authenticated.guard.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useAuthStore } from "@data/auth";
import { NavigationGuardNext, RouteLocationNormalized } from "vue-router";
import { getLoginUrlWithRedirect } from "../login-redirect-url";
import { getLoginUrlWithRedirect } from "./login-redirect-url";

export const isAuthenticatedGuard = (
to: RouteLocationNormalized,
Expand Down
121 changes: 121 additions & 0 deletions src/router/guards/is-authenticated.unit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { meResponseFactory } from "@@/tests/test-utils/factory";
import { useAuthStore } from "@data/auth";
import { createTestingPinia } from "@pinia/testing";
import { setActivePinia } from "pinia";
import { RouteLocationNormalized } from "vue-router";
import { isAuthenticatedGuard } from "./is-authenticated.guard";

jest.mock("./login-redirect-url", () => ({
getLoginUrlWithRedirect: () => "login-url",
}));

describe("Authentication Guard", () => {
beforeEach(() => {
setActivePinia(createTestingPinia());
});

afterEach(() => {
jest.clearAllMocks();
});

describe("isAuthenticatedGuard", () => {
describe("when authenticated", () => {
const setup = () => {
const to: RouteLocationNormalized = {
fullPath: "/test",
} as RouteLocationNormalized;
const from: RouteLocationNormalized = {} as RouteLocationNormalized;
const next = jest.fn();
const authStore = useAuthStore();

authStore.me = meResponseFactory.build();

return {
to,
from,
next,
};
};

it("should pass", () => {
const { to, from, next } = setup();

isAuthenticatedGuard(to, from, next);

expect(next).toHaveBeenCalled();
});
});

describe("when the url is public", () => {
const setup = () => {
const to: RouteLocationNormalized = {
fullPath: "/test",
meta: {
isPublic: true,
},
} as unknown as RouteLocationNormalized;
const from: RouteLocationNormalized = {} as RouteLocationNormalized;
const next = jest.fn();
const authStore = useAuthStore();

authStore.me = null;

return {
to,
from,
next,
};
};

it("should pass", () => {
const { to, from, next } = setup();

isAuthenticatedGuard(to, from, next);

expect(next).toHaveBeenCalled();
});
});

describe("when not authenticated and the url is not public", () => {
const setup = () => {
const to: RouteLocationNormalized = {
fullPath: "/test",
} as RouteLocationNormalized;
const from: RouteLocationNormalized = {} as RouteLocationNormalized;
const next = jest.fn();
const authStore = useAuthStore();

authStore.me = null;

const assign = jest.fn();
Object.defineProperty(window, "location", {
configurable: true,
value: { assign },
});

return {
to,
from,
next,
assign,
};
};

it("should redirect to login", () => {
const { to, from, next, assign } = setup();

isAuthenticatedGuard(to, from, next);

expect(assign).toHaveBeenCalledWith("login-url");
});

it("should not pass", () => {
const { to, from, next } = setup();

isAuthenticatedGuard(to, from, next);

expect(next).not.toHaveBeenCalled();
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEnvConfigStore } from "@data/env-config";

export const getLoginUrlWithRedirect = (targetPath: string) => {
export const getLoginUrlWithRedirect = (targetPath: string): string => {
const currentOrigin = window.location.origin;

const currentUrl = new URL(targetPath, currentOrigin);
Expand Down
87 changes: 87 additions & 0 deletions src/router/guards/login-redirect-url.unit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { envsFactory } from "@@/tests/test-utils/factory";
import { useEnvConfigStore } from "@data/env-config";
import { createPinia, setActivePinia } from "pinia";
import { getLoginUrlWithRedirect } from "./login-redirect-url";

describe("Login Redirect Util", () => {
beforeEach(() => {
setActivePinia(createPinia());
});

afterEach(() => {
jest.clearAllMocks();
});

describe("getLoginUrlWithRedirect", () => {
describe("when redirecting to internal login before redirecting to the target url", () => {
const setup = () => {
const loginUrl = "https://test.com/login";
const targetPath = "/dashboard";
const origin = "https://test.com";
const envConfigStore = useEnvConfigStore();

envConfigStore.setEnvs(
envsFactory.build({
NOT_AUTHENTICATED_REDIRECT_URL: loginUrl,
})
);

jest.spyOn(window, "location", "get").mockReturnValue({
origin: origin,
} as Location);

return {
loginUrl,
targetPath,
origin,
};
};

it("should redirect to internal login with post-login-redirect to internal target url", () => {
const { loginUrl, targetPath, origin } = setup();

const result = getLoginUrlWithRedirect(targetPath);

expect(result).toEqual(
`${loginUrl}?redirect=${encodeURIComponent(`${origin}${targetPath}`)}`
);
});
});

describe("when redirecting to an external login before redirecting to the target url", () => {
const setup = () => {
const origin = "https://test.com";
const loginUrl = `https://external-login.thr/login?service=${encodeURIComponent(origin)}`;
const targetPath = "/dashboard";
const envConfigStore = useEnvConfigStore();

envConfigStore.setEnvs(
envsFactory.build({
NOT_AUTHENTICATED_REDIRECT_URL: loginUrl,
})
);

jest.spyOn(window, "location", "get").mockReturnValue({
origin: origin,
} as Location);

return {
loginUrl,
targetPath,
origin,
};
};

it("should redirect to external login with post-login-redirect to internal target url", () => {
const { loginUrl, targetPath, origin } = setup();

const result = getLoginUrlWithRedirect(targetPath);

const redirectUri = encodeURIComponent(`${origin}${targetPath}`);
expect(result).toEqual(
`${loginUrl}${encodeURIComponent(`/?redirect=${redirectUri}`)}`
);
});
});
});
});
6 changes: 3 additions & 3 deletions src/utils/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ export const mapAxiosErrorToResponseError = (
apiError = errorPayload;
} else if (typeof errorPayload === "string") {
apiError.message = errorPayload;
apiError.code = error.response?.status || apiError.code;
apiError.type = error.code || apiError.type;
apiError.title = error.response?.statusText || apiError.title;
apiError.code = error.response?.status ?? apiError.code;
apiError.type = error.code ?? apiError.type;
apiError.title = error.response?.statusText ?? apiError.title;
}
}
return apiError;
Expand Down
4 changes: 2 additions & 2 deletions src/utils/api/api.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ describe("AxiosInstance", () => {

const data = "NOT_FOUND";
const responseError = axiosErrorFactory.build({
response: { data },
response: { data, status: undefined, statusText: undefined },
});

return {
Expand All @@ -111,7 +111,7 @@ describe("AxiosInstance", () => {
expect(result).toStrictEqual({
message: "NOT_FOUND",
code: 1,
title: responseError.response?.statusText,
title: "",
type: "Unknown error",
});
});
Expand Down

0 comments on commit d5c20c2

Please sign in to comment.