Merge pull request #25 from zitadel/setup-unit-integration-tests

test: setup unit tests
This commit is contained in:
Max Peintner
2023-06-08 09:12:51 +02:00
committed by GitHub
33 changed files with 3922 additions and 1244 deletions

View File

@@ -1,4 +1,4 @@
module.exports = {
extends: "next/core-web-vitals",
extends: ["next/core-web-vitals"],
ignorePatterns: ["external/**/*.ts"],
};

View File

@@ -0,0 +1,57 @@
import { render, screen, waitFor, within } from "@testing-library/react";
import PasswordComplexity from "../ui/PasswordComplexity";
// TODO: Why does this not compile?
// import { ResourceOwnerType } from '@zitadel/server';
const matchesTitle = `Matches`;
const doesntMatchTitle = `Doesn't match`;
describe("<PasswordComplexity/>", () => {
describe.each`
settingsMinLength | password | expectSVGTitle
${5} | ${"Password1!"} | ${matchesTitle}
${30} | ${"Password1!"} | ${doesntMatchTitle}
${0} | ${"Password1!"} | ${matchesTitle}
${undefined} | ${"Password1!"} | ${false}
`(
`With settingsMinLength=$settingsMinLength, password=$password, expectSVGTitle=$expectSVGTitle`,
({ settingsMinLength, password, expectSVGTitle }) => {
const feedbackElementLabel = /password length/i;
beforeEach(() => {
render(
<PasswordComplexity
password={password}
equals
passwordComplexitySettings={{
minLength: settingsMinLength,
requiresLowercase: false,
requiresUppercase: false,
requiresNumber: false,
requiresSymbol: false,
resourceOwnerType: 0, // ResourceOwnerType.RESOURCE_OWNER_TYPE_UNSPECIFIED,
}}
/>
);
});
if (expectSVGTitle === false) {
it(`should not render the feedback element`, async () => {
await waitFor(() => {
expect(
screen.queryByText(feedbackElementLabel)
).not.toBeInTheDocument();
});
});
} else {
it(`Should show one SVG with title ${expectSVGTitle}`, async () => {
await waitFor(async () => {
const svg = within(
screen.getByText(feedbackElementLabel)
.parentElement as HTMLElement
).findByRole("img");
expect(await svg).toHaveTextContent(expectSVGTitle);
});
});
}
}
);
});

View File

@@ -0,0 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"jsx": "react-jsxdev",
"types": ["node", "jest", "@testing-library/jest-dom"]
}
}

19
apps/login/jest.config.ts Normal file
View File

@@ -0,0 +1,19 @@
import type { Config } from "@jest/types";
import { pathsToModuleNameMapper } from "ts-jest";
import { compilerOptions } from "./tsconfig.json";
export default async (): Promise<Config.InitialOptions> => {
return {
preset: "ts-jest",
transform: {
"^.+\\.tsx?$": ["ts-jest", { tsconfig: "./__test__/tsconfig.json" }],
},
setupFilesAfterEnv: ["@testing-library/jest-dom/extend-expect"],
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
prefix: "<rootDir>/",
}),
testEnvironment: "jsdom",
testRegex: "/__test__/.*\\.test\\.tsx?$",
modulePathIgnorePatterns: ["cypress"],
};
};

View File

@@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
// Custom hook to read auth record and user profile doc
// Custom hook to read auth record and user profile doc
export function useUserData() {
const [clientData, setClientData] = useState(null);

View File

@@ -2,13 +2,15 @@
"name": "@zitadel/login",
"private": true,
"scripts": {
"build": "next build",
"dev": "next dev",
"test": "jest",
"test:watch": "jest --watch",
"lint": "next lint && prettier --check .",
"lint:fix": "prettier --write .",
"lint-staged": "lint-staged",
"build": "next build",
"prestart": "build",
"start": "next start",
"test": "yarn prettier:check &nexarn lint",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next"
},
"git": {
@@ -39,24 +41,34 @@
},
"devDependencies": {
"@bufbuild/buf": "^1.14.0",
"@jest/types": "^29.5.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@types/jest": "^29.5.1",
"@types/ms": "0.7.31",
"@types/node": "18.11.9",
"@types/react": "18.0.25",
"@types/react": "18.2.8",
"@types/react-dom": "18.0.9",
"@types/testing-library__jest-dom": "^5.14.6",
"@types/tinycolor2": "1.4.3",
"@types/uuid": "^9.0.1",
"@vercel/git-hooks": "1.0.0",
"@zitadel/tsconfig": "workspace:*",
"autoprefixer": "10.4.13",
"del-cli": "5.0.0",
"eslint-config-zitadel": "workspace:*",
"grpc-tools": "1.11.3",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"lint-staged": "13.0.3",
"make-dir-cli": "3.0.0",
"postcss": "8.4.21",
"prettier-plugin-tailwindcss": "0.1.13",
"tailwindcss": "3.2.4",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"ts-proto": "^1.139.0",
"typescript": "4.8.4",
"typescript": "5.0.4",
"zitadel-tailwind-config": "workspace:*"
}
}

View File

@@ -1,11 +1,17 @@
{
"extends": "@zitadel/tsconfig/nextjs.json",
"compilerOptions": {
"jsx": "preserve",
"rootDir": ".",
"baseUrl": ".",
"paths": {
"#/*": ["./*"]
},
"plugins": [{ "name": "next" }]
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]

View File

@@ -20,7 +20,9 @@ const check = (
strokeWidth={1.5}
stroke="currentColor"
className="w-6 h-6 las la-check text-green-500 dark:text-green-500 mr-2 text-lg"
role="img"
>
<title>Matches</title>
<path
strokeLinecap="round"
strokeLinejoin="round"
@@ -36,7 +38,9 @@ const cross = (
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
role="img"
>
<title>Doesn't match</title>
<path
strokeLinecap="round"
strokeLinejoin="round"
@@ -60,12 +64,16 @@ export default function PasswordComplexity({
return (
<div className="mb-4 grid grid-cols-2 gap-x-8 gap-y-2">
<div className="flex flex-row items-center">
{hasMinLength ? check : cross}
<span className={desc}>
Password length {passwordComplexitySettings.minLength}
</span>
</div>
{passwordComplexitySettings.minLength != undefined ? (
<div className="flex flex-row items-center">
{hasMinLength ? check : cross}
<span className={desc}>
Password length {passwordComplexitySettings.minLength}
</span>
</div>
) : (
<span />
)}
<div className="flex flex-row items-center">
{hasSymbol ? check : cross}
<span className={desc}>has Symbol</span>

View File

@@ -28,7 +28,9 @@ export default function VerifyEmailForm({ userId, code, submit }: Props) {
useEffect(() => {
if (submit && code && userId) {
submitCode({ code });
// When we navigate to this page, we always want to be redirected if submit is true and the parameters are valid.
// For programmatic verification, the /verifyemail API should be used.
submitCodeAndContinue({ code });
}
}, []);
@@ -53,12 +55,11 @@ export default function VerifyEmailForm({ userId, code, submit }: Props) {
const response = await res.json();
setLoading(false);
if (!res.ok) {
setLoading(false);
setError(response.details);
return Promise.reject(response);
} else {
setLoading(false);
return response;
}
}