refactor(web): use vitest for frontend testing and coverage (#4946)
This PR drops Jest as a requirement and utilises Vitest for frontend testing and coverage collection during the dev workflow and unit testing. Closes #4967 Signed-off-by: Amir Zarrinkafsh <nightah@me.com>pull/5204/head
parent
ecdae9e5d2
commit
0312defcd7
|
@ -19,6 +19,7 @@
|
||||||
.env.test.local
|
.env.test.local
|
||||||
.env.production.local
|
.env.production.local
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
.vitest-preview
|
||||||
|
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
|
|
|
@ -42,83 +42,14 @@
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"coverage": "VITE_COVERAGE=true vite build",
|
"coverage": "VITE_COVERAGE=true vite build",
|
||||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
|
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
|
||||||
"test": "jest --coverage --no-cache",
|
"test": "vitest run --coverage",
|
||||||
|
"test:watch": "vitest --coverage",
|
||||||
|
"test:preview": "vitest-preview",
|
||||||
"report": "nyc report -r clover -r json -r lcov -r text"
|
"report": "nyc report -r clover -r json -r lcov -r text"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
},
|
},
|
||||||
"jest": {
|
|
||||||
"roots": [
|
|
||||||
"<rootDir>/src"
|
|
||||||
],
|
|
||||||
"collectCoverageFrom": [
|
|
||||||
"src/**/*.{js,jsx,ts,tsx}",
|
|
||||||
"!src/**/*.d.ts"
|
|
||||||
],
|
|
||||||
"setupFilesAfterEnv": [
|
|
||||||
"<rootDir>/src/setupTests.js"
|
|
||||||
],
|
|
||||||
"testMatch": [
|
|
||||||
"<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
|
|
||||||
],
|
|
||||||
"testEnvironment": "jsdom",
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.(js|jsx|mjs|cjs|ts|tsx)$": [
|
|
||||||
"esbuild-jest",
|
|
||||||
{
|
|
||||||
"sourcemap": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"^.+\\.(css|png|svg)$": "jest-transform-stub"
|
|
||||||
},
|
|
||||||
"transformIgnorePatterns": [
|
|
||||||
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$"
|
|
||||||
],
|
|
||||||
"moduleNameMapper": {
|
|
||||||
"^@root/(.*)$": [
|
|
||||||
"<rootDir>/src/$1"
|
|
||||||
],
|
|
||||||
"^@assets/(.*)$": [
|
|
||||||
"<rootDir>/src/assets/$1"
|
|
||||||
],
|
|
||||||
"^@components/(.*)$": [
|
|
||||||
"<rootDir>/src/components/$1"
|
|
||||||
],
|
|
||||||
"^@constants/(.*)$": [
|
|
||||||
"<rootDir>/src/constants/$1"
|
|
||||||
],
|
|
||||||
"^@hooks/(.*)$": [
|
|
||||||
"<rootDir>/src/hooks/$1"
|
|
||||||
],
|
|
||||||
"^@i18n/(.*)$": [
|
|
||||||
"<rootDir>/src/i18n/$1"
|
|
||||||
],
|
|
||||||
"^@layouts/(.*)$": [
|
|
||||||
"<rootDir>/src/layouts/$1"
|
|
||||||
],
|
|
||||||
"^@models/(.*)$": [
|
|
||||||
"<rootDir>/src/models/$1"
|
|
||||||
],
|
|
||||||
"^@services/(.*)$": [
|
|
||||||
"<rootDir>/src/services/$1"
|
|
||||||
],
|
|
||||||
"^@themes/(.*)$": [
|
|
||||||
"<rootDir>/src/themes/$1"
|
|
||||||
],
|
|
||||||
"^@utils/(.*)$": [
|
|
||||||
"<rootDir>/src/utils/$1"
|
|
||||||
],
|
|
||||||
"^@views/(.*)$": [
|
|
||||||
"<rootDir>/src/views/$1"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"watchPlugins": [
|
|
||||||
"jest-watch-typeahead/filename",
|
|
||||||
"jest-watch-typeahead/testname"
|
|
||||||
],
|
|
||||||
"resetMocks": true
|
|
||||||
},
|
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
">0.2%",
|
">0.2%",
|
||||||
|
@ -140,17 +71,17 @@
|
||||||
"@limegrass/eslint-plugin-import-alias": "1.0.6",
|
"@limegrass/eslint-plugin-import-alias": "1.0.6",
|
||||||
"@testing-library/jest-dom": "5.16.5",
|
"@testing-library/jest-dom": "5.16.5",
|
||||||
"@testing-library/react": "14.0.0",
|
"@testing-library/react": "14.0.0",
|
||||||
"@types/jest": "29.5.0",
|
|
||||||
"@types/node": "18.15.11",
|
"@types/node": "18.15.11",
|
||||||
"@types/qrcode.react": "1.0.2",
|
"@types/qrcode.react": "1.0.2",
|
||||||
"@types/react": "18.0.34",
|
"@types/react": "18.0.34",
|
||||||
"@types/react-dom": "18.0.11",
|
"@types/react-dom": "18.0.11",
|
||||||
|
"@types/testing-library__jest-dom": "5.14.5",
|
||||||
"@types/zxcvbn": "4.4.1",
|
"@types/zxcvbn": "4.4.1",
|
||||||
"@typescript-eslint/eslint-plugin": "5.58.0",
|
"@typescript-eslint/eslint-plugin": "5.58.0",
|
||||||
"@typescript-eslint/parser": "5.58.0",
|
"@typescript-eslint/parser": "5.58.0",
|
||||||
"@vitejs/plugin-react": "3.1.0",
|
"@vitejs/plugin-react": "3.1.0",
|
||||||
|
"@vitest/coverage-istanbul": "0.30.0",
|
||||||
"esbuild": "0.17.16",
|
"esbuild": "0.17.16",
|
||||||
"esbuild-jest": "0.5.0",
|
|
||||||
"eslint": "8.38.0",
|
"eslint": "8.38.0",
|
||||||
"eslint-config-prettier": "8.8.0",
|
"eslint-config-prettier": "8.8.0",
|
||||||
"eslint-config-react-app": "7.0.1",
|
"eslint-config-react-app": "7.0.1",
|
||||||
|
@ -161,11 +92,8 @@
|
||||||
"eslint-plugin-prettier": "4.2.1",
|
"eslint-plugin-prettier": "4.2.1",
|
||||||
"eslint-plugin-react": "7.32.2",
|
"eslint-plugin-react": "7.32.2",
|
||||||
"eslint-plugin-react-hooks": "4.6.0",
|
"eslint-plugin-react-hooks": "4.6.0",
|
||||||
|
"happy-dom": "9.1.9",
|
||||||
"husky": "8.0.3",
|
"husky": "8.0.3",
|
||||||
"jest": "29.5.0",
|
|
||||||
"jest-environment-jsdom": "29.5.0",
|
|
||||||
"jest-transform-stub": "2.0.0",
|
|
||||||
"jest-watch-typeahead": "2.2.2",
|
|
||||||
"prettier": "2.8.7",
|
"prettier": "2.8.7",
|
||||||
"react-test-renderer": "18.2.0",
|
"react-test-renderer": "18.2.0",
|
||||||
"typescript": "5.0.4",
|
"typescript": "5.0.4",
|
||||||
|
@ -173,6 +101,8 @@
|
||||||
"vite-plugin-eslint": "1.8.1",
|
"vite-plugin-eslint": "1.8.1",
|
||||||
"vite-plugin-istanbul": "4.0.1",
|
"vite-plugin-istanbul": "4.0.1",
|
||||||
"vite-plugin-svgr": "2.4.0",
|
"vite-plugin-svgr": "2.4.0",
|
||||||
"vite-tsconfig-paths": "4.1.0"
|
"vite-tsconfig-paths": "4.1.0",
|
||||||
|
"vitest": "0.30.0",
|
||||||
|
"vitest-preview": "0.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
5406
web/pnpm-lock.yaml
5406
web/pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -1,9 +1,34 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { render } from "@testing-library/react";
|
import { render, screen } from "@testing-library/react";
|
||||||
|
|
||||||
import NotificationBar from "@components/NotificationBar";
|
import NotificationBar from "@components/NotificationBar";
|
||||||
|
import NotificationsContext from "@hooks/NotificationsContext";
|
||||||
|
import { Notification } from "@models/Notifications";
|
||||||
|
|
||||||
|
const testNotification: Notification = {
|
||||||
|
message: "Test notification",
|
||||||
|
level: "success",
|
||||||
|
timeout: 3,
|
||||||
|
};
|
||||||
|
|
||||||
it("renders without crashing", () => {
|
it("renders without crashing", () => {
|
||||||
render(<NotificationBar onClose={() => {}} />);
|
render(<NotificationBar onClose={() => {}} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("displays notification message and level correctly", async () => {
|
||||||
|
render(
|
||||||
|
<NotificationsContext.Provider value={{ notification: testNotification, setNotification: () => {} }}>
|
||||||
|
<NotificationBar onClose={() => {}} />
|
||||||
|
</NotificationsContext.Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const alert = await screen.getByRole("alert");
|
||||||
|
const message = await screen.findByText(testNotification.message);
|
||||||
|
|
||||||
|
expect(alert).toHaveClass(
|
||||||
|
`MuiAlert-filled${testNotification.level.charAt(0).toUpperCase() + testNotification.level.substring(1)}`,
|
||||||
|
{ exact: false },
|
||||||
|
);
|
||||||
|
expect(message).toHaveTextContent(testNotification.message);
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { fireEvent, render, screen } from "@testing-library/react";
|
||||||
|
import { beforeEach } from "vitest";
|
||||||
|
|
||||||
|
import PrivacyPolicyDrawer from "@components/PrivacyPolicyDrawer";
|
||||||
|
|
||||||
|
vi.mock("react-i18next", () => ({
|
||||||
|
withTranslation: () => (Component: any) => {
|
||||||
|
Component.defaultProps = { ...Component.defaultProps, t: (children: any) => children };
|
||||||
|
return Component;
|
||||||
|
},
|
||||||
|
Trans: ({ children }: any) => children,
|
||||||
|
useTranslation: () => {
|
||||||
|
return {
|
||||||
|
t: (str) => str,
|
||||||
|
i18n: {
|
||||||
|
changeLanguage: () => new Promise(() => {}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
document.body.setAttribute("data-privacypolicyurl", "");
|
||||||
|
document.body.setAttribute("data-privacypolicyaccept", "false");
|
||||||
|
|
||||||
|
global.localStorage.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders privacy policy and accepts when Accept button is clicked", () => {
|
||||||
|
document.body.setAttribute("data-privacypolicyurl", "http://example.com/privacy-policy");
|
||||||
|
document.body.setAttribute("data-privacypolicyaccept", "true");
|
||||||
|
|
||||||
|
const { container } = render(<PrivacyPolicyDrawer />);
|
||||||
|
fireEvent.click(screen.getByText("Accept"));
|
||||||
|
expect(container).toBeEmptyDOMElement();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not render when privacy policy is disabled", () => {
|
||||||
|
render(<PrivacyPolicyDrawer />);
|
||||||
|
expect(screen.queryByText("Privacy Policy")).toBeNull();
|
||||||
|
expect(screen.queryByText("You must view and accept the Privacy Policy before using")).toBeNull();
|
||||||
|
expect(screen.queryByText("Accept")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not render when acceptance is not required", () => {
|
||||||
|
document.body.setAttribute("data-privacypolicyurl", "http://example.com/privacy-policy");
|
||||||
|
|
||||||
|
render(<PrivacyPolicyDrawer />);
|
||||||
|
expect(screen.queryByText("Privacy Policy")).toBeNull();
|
||||||
|
expect(screen.queryByText("You must view and accept the Privacy Policy before using")).toBeNull();
|
||||||
|
expect(screen.queryByText("Accept")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not render when already accepted", () => {
|
||||||
|
global.localStorage.setItem("privacy-policy-accepted", "true");
|
||||||
|
|
||||||
|
const { container } = render(<PrivacyPolicyDrawer />);
|
||||||
|
expect(container).toBeEmptyDOMElement();
|
||||||
|
});
|
|
@ -0,0 +1,30 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { render } from "@testing-library/react";
|
||||||
|
|
||||||
|
import PrivacyPolicyLink from "@components/PrivacyPolicyLink";
|
||||||
|
|
||||||
|
vi.mock("react-i18next", () => ({
|
||||||
|
withTranslation: () => (Component: any) => {
|
||||||
|
Component.defaultProps = { ...Component.defaultProps, t: (children: any) => children };
|
||||||
|
return Component;
|
||||||
|
},
|
||||||
|
Trans: ({ children }: any) => children,
|
||||||
|
useTranslation: () => {
|
||||||
|
return {
|
||||||
|
t: (str) => str,
|
||||||
|
i18n: {
|
||||||
|
changeLanguage: () => new Promise(() => {}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("renders a link to the privacy policy with the correct text", () => {
|
||||||
|
document.body.setAttribute("data-privacypolicyurl", "http://example.com/privacy-policy");
|
||||||
|
|
||||||
|
const { getByRole } = render(<PrivacyPolicyLink />);
|
||||||
|
const link = getByRole("link");
|
||||||
|
expect(link).toHaveAttribute("href", "http://example.com/privacy-policy");
|
||||||
|
expect(link).toHaveTextContent("Privacy Policy");
|
||||||
|
});
|
|
@ -1,9 +1,37 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { render } from "@testing-library/react";
|
import { act, render } from "@testing-library/react";
|
||||||
|
|
||||||
import TimerIcon from "@components/TimerIcon";
|
import TimerIcon from "@components/TimerIcon";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.useFakeTimers().setSystemTime(new Date(2023, 1, 1, 8));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
it("renders without crashing", () => {
|
it("renders without crashing", () => {
|
||||||
render(<TimerIcon width={32} height={32} period={30} />);
|
render(<TimerIcon width={32} height={32} period={30} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders a timer icon with updating progress for a given period", async () => {
|
||||||
|
const { container } = render(<TimerIcon width={32} height={32} period={30} />);
|
||||||
|
const initialProgress =
|
||||||
|
container.firstElementChild!.firstElementChild!.nextElementSibling!.nextElementSibling!.getAttribute(
|
||||||
|
"stroke-dasharray",
|
||||||
|
);
|
||||||
|
expect(initialProgress).toBe("0 31.6");
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
vi.advanceTimersByTime(3000);
|
||||||
|
});
|
||||||
|
|
||||||
|
const updatedProgress =
|
||||||
|
container.firstElementChild!.firstElementChild!.nextElementSibling!.nextElementSibling!.getAttribute(
|
||||||
|
"stroke-dasharray",
|
||||||
|
);
|
||||||
|
expect(updatedProgress).toBe("3.16 31.6");
|
||||||
|
expect(Number(updatedProgress!.split(/\s(.+)/)[0])).toBeGreaterThan(Number(initialProgress!.split(/\s(.+)/)[0]));
|
||||||
|
});
|
||||||
|
|
|
@ -2,12 +2,41 @@ import React from "react";
|
||||||
|
|
||||||
import { render } from "@testing-library/react";
|
import { render } from "@testing-library/react";
|
||||||
|
|
||||||
import TypographyWithTooltip from "@components/TypographyWithTootip";
|
import TypographyWithTooltip, { Props } from "@components/TypographyWithTooltip";
|
||||||
|
|
||||||
|
const defaultProps: Props = {
|
||||||
|
variant: "h5",
|
||||||
|
value: "Example",
|
||||||
|
};
|
||||||
|
|
||||||
it("renders without crashing", () => {
|
it("renders without crashing", () => {
|
||||||
render(<TypographyWithTooltip value={"Example"} variant={"h5"} />);
|
render(<TypographyWithTooltip {...defaultProps} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders with tooltip without crashing", () => {
|
it("renders with tooltip without crashing", () => {
|
||||||
render(<TypographyWithTooltip value={"Example"} tooltip={"A tooltip"} variant={"h5"} />);
|
const props: Props = {
|
||||||
|
...defaultProps,
|
||||||
|
tooltip: "A tooltip",
|
||||||
|
};
|
||||||
|
render(<TypographyWithTooltip {...props} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders the text correctly", () => {
|
||||||
|
const props: Props = {
|
||||||
|
...defaultProps,
|
||||||
|
value: "Test text",
|
||||||
|
};
|
||||||
|
const { getByText } = render(<TypographyWithTooltip {...props} />);
|
||||||
|
const element = getByText(props.value!);
|
||||||
|
expect(element).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders the tooltip correctly", () => {
|
||||||
|
const props: Props = {
|
||||||
|
...defaultProps,
|
||||||
|
tooltip: "Test tooltip",
|
||||||
|
};
|
||||||
|
const { getByText } = render(<TypographyWithTooltip {...props} />);
|
||||||
|
const element = getByText(props.value!);
|
||||||
|
expect(element).toHaveAttribute("aria-label", props.tooltip);
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next";
|
||||||
import { ReactComponent as UserSvg } from "@assets/images/user.svg";
|
import { ReactComponent as UserSvg } from "@assets/images/user.svg";
|
||||||
import PrivacyPolicyDrawer from "@components/PrivacyPolicyDrawer";
|
import PrivacyPolicyDrawer from "@components/PrivacyPolicyDrawer";
|
||||||
import PrivacyPolicyLink from "@components/PrivacyPolicyLink";
|
import PrivacyPolicyLink from "@components/PrivacyPolicyLink";
|
||||||
import TypographyWithTooltip from "@components/TypographyWithTootip";
|
import TypographyWithTooltip from "@components/TypographyWithTooltip";
|
||||||
import { getLogoOverride, getPrivacyPolicyEnabled } from "@utils/Configuration";
|
import { getLogoOverride, getPrivacyPolicyEnabled } from "@utils/Configuration";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
import "@testing-library/jest-dom";
|
|
||||||
|
|
||||||
document.body.setAttribute("data-basepath", "");
|
|
||||||
document.body.setAttribute("data-duoselfenrollment", "true");
|
|
||||||
document.body.setAttribute("data-rememberme", "true");
|
|
||||||
document.body.setAttribute("data-resetpassword", "true");
|
|
||||||
document.body.setAttribute("data-resetpasswordcustomurl", "");
|
|
||||||
document.body.setAttribute("data-privacypolicyurl", "");
|
|
||||||
document.body.setAttribute("data-privacypolicyaccept", "false");
|
|
||||||
document.body.setAttribute("data-theme", "light");
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import matchers, { TestingLibraryMatchers } from "@testing-library/jest-dom/matchers";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
namespace Vi {
|
||||||
|
interface JestAssertion<T = any> extends jest.Matchers<void, T>, TestingLibraryMatchers<T, void> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect.extend(matchers);
|
||||||
|
|
||||||
|
const localStorageMock = (function () {
|
||||||
|
let store = {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getItem(key) {
|
||||||
|
return store[key];
|
||||||
|
},
|
||||||
|
|
||||||
|
setItem(key, value) {
|
||||||
|
store[key] = value;
|
||||||
|
},
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
store = {};
|
||||||
|
},
|
||||||
|
|
||||||
|
removeItem(key) {
|
||||||
|
delete store[key];
|
||||||
|
},
|
||||||
|
|
||||||
|
getAll() {
|
||||||
|
return store;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
Object.defineProperty(window, "localStorage", { value: localStorageMock });
|
||||||
|
|
||||||
|
document.body.setAttribute("data-basepath", "");
|
||||||
|
document.body.setAttribute("data-duoselfenrollment", "true");
|
||||||
|
document.body.setAttribute("data-rememberme", "true");
|
||||||
|
document.body.setAttribute("data-resetpassword", "true");
|
||||||
|
document.body.setAttribute("data-resetpasswordcustomurl", "");
|
||||||
|
document.body.setAttribute("data-privacypolicyurl", "");
|
||||||
|
document.body.setAttribute("data-privacypolicyaccept", "false");
|
||||||
|
document.body.setAttribute("data-theme", "light");
|
|
@ -21,7 +21,7 @@
|
||||||
"dom.iterable",
|
"dom.iterable",
|
||||||
"esnext"
|
"esnext"
|
||||||
],
|
],
|
||||||
"types": ["@types/jest", "vite/client", "vite-plugin-svgr/client"],
|
"types": ["vite/client", "vite-plugin-svgr/client", "vitest/globals"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
|
|
|
@ -5,18 +5,17 @@ import istanbul from "vite-plugin-istanbul";
|
||||||
import svgr from "vite-plugin-svgr";
|
import svgr from "vite-plugin-svgr";
|
||||||
import tsconfigPaths from "vite-tsconfig-paths";
|
import tsconfigPaths from "vite-tsconfig-paths";
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
export default defineConfig(({ mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
const isCoverage = process.env.VITE_COVERAGE === "true";
|
const isCoverage = process.env.VITE_COVERAGE === "true";
|
||||||
const sourcemap = isCoverage ? "inline" : undefined;
|
const sourcemap = isCoverage ? "inline" : undefined;
|
||||||
|
|
||||||
const istanbulPlugin = isCoverage
|
const istanbulPlugin = isCoverage
|
||||||
? istanbul({
|
? istanbul({
|
||||||
include: "src/*",
|
checkProd: false,
|
||||||
exclude: ["node_modules"],
|
exclude: ["node_modules"],
|
||||||
extension: [".js", ".jsx", ".ts", ".tsx"],
|
extension: [".js", ".jsx", ".ts", ".tsx"],
|
||||||
checkProd: false,
|
|
||||||
forceBuildInstrument: true,
|
forceBuildInstrument: true,
|
||||||
|
include: "src/*",
|
||||||
requireEnv: true,
|
requireEnv: true,
|
||||||
})
|
})
|
||||||
: undefined;
|
: undefined;
|
||||||
|
@ -24,14 +23,11 @@ export default defineConfig(({ mode }) => {
|
||||||
return {
|
return {
|
||||||
base: "./",
|
base: "./",
|
||||||
build: {
|
build: {
|
||||||
sourcemap,
|
|
||||||
outDir: "../internal/server/public_html",
|
|
||||||
emptyOutDir: true,
|
|
||||||
assetsDir: "static",
|
assetsDir: "static",
|
||||||
|
emptyOutDir: true,
|
||||||
|
outDir: "../internal/server/public_html",
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
entryFileNames: `static/js/[name].[hash].js`,
|
|
||||||
chunkFileNames: `static/js/[name].[hash].js`,
|
|
||||||
assetFileNames: ({ name }) => {
|
assetFileNames: ({ name }) => {
|
||||||
if (name && name.endsWith(".css")) {
|
if (name && name.endsWith(".css")) {
|
||||||
return "static/css/[name].[hash].[ext]";
|
return "static/css/[name].[hash].[ext]";
|
||||||
|
@ -39,12 +35,26 @@ export default defineConfig(({ mode }) => {
|
||||||
|
|
||||||
return "static/media/[name].[hash].[ext]";
|
return "static/media/[name].[hash].[ext]";
|
||||||
},
|
},
|
||||||
|
chunkFileNames: `static/js/[name].[hash].js`,
|
||||||
|
entryFileNames: `static/js/[name].[hash].js`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
sourcemap,
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 3000,
|
|
||||||
open: false,
|
open: false,
|
||||||
|
port: 3000,
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
coverage: {
|
||||||
|
provider: "istanbul",
|
||||||
|
},
|
||||||
|
environment: "happy-dom",
|
||||||
|
globals: true,
|
||||||
|
onConsoleLog(log) {
|
||||||
|
if (log.includes('No routes matched location "blank"')) return false;
|
||||||
|
},
|
||||||
|
setupFiles: ["src/setupTests.ts"],
|
||||||
},
|
},
|
||||||
plugins: [eslintPlugin({ cache: false }), istanbulPlugin, react(), svgr(), tsconfigPaths()],
|
plugins: [eslintPlugin({ cache: false }), istanbulPlugin, react(), svgr(), tsconfigPaths()],
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue