mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-12 07:24:51 +00:00
test: add table driven unit tests
This commit is contained in:
@@ -1,110 +0,0 @@
|
||||
import RootLayout from '#/app/layout';
|
||||
import Page from './page'
|
||||
import { RenderResult, render, screen, waitFor, renderHook } from '@testing-library/react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
|
||||
describe('/login/verify', () => {
|
||||
|
||||
const noUserIDError = "No userId provided!"
|
||||
const noCodeError = "No code provided!"
|
||||
const codeLabelText = "Code"
|
||||
|
||||
describe.each`
|
||||
userId | code | directSubmit | expectRenderedUserIDError | expectRenderedCodePrefilled | expectRenderedCodeError
|
||||
| ${"123"} | ${"xyz"} | ${true} | ${false} | ${"" /*TODO: We should expect "xyz"*/} | ${false}
|
||||
| ${"123"} | ${""} | ${true} | ${false} | ${""} | ${true}
|
||||
| ${"123"} | ${undefined} | ${true} | ${false} | ${""} | ${true}
|
||||
| ${"123"} | ${"xyz"} | ${false} | ${false} | ${"xyz"} | ${false}
|
||||
| ${"123"} | ${""} | ${false} | ${false} | ${""} | ${false}
|
||||
| ${"123"} | ${undefined} | ${false} | ${false} | ${""} | ${false}
|
||||
| ${""} | ${"xyz"} | ${true} | ${true} | ${false} | ${false}
|
||||
| ${""} | ${""} | ${true} | ${true} | ${false} | ${false}
|
||||
| ${""} | ${undefined} | ${true} | ${true} | ${false} | ${false}
|
||||
| ${""} | ${"xyz"} | ${false} | ${true} | ${false} | ${false}
|
||||
| ${""} | ${""} | ${false} | ${true} | ${false} | ${false}
|
||||
| ${""} | ${undefined} | ${false} | ${true} | ${false} | ${false}
|
||||
| ${undefined} | ${"xyz"} | ${true} | ${true} | ${false} | ${false}
|
||||
| ${undefined} | ${""} | ${true} | ${true} | ${false} | ${false}
|
||||
| ${undefined} | ${undefined} | ${true} | ${true} | ${false} | ${false}
|
||||
| ${undefined} | ${"xyz"} | ${false} | ${true} | ${false} | ${false}
|
||||
| ${undefined} | ${""} | ${false} | ${true} | ${false} | ${false}
|
||||
| ${undefined} | ${undefined} | ${false} | ${true} | ${false} | ${false}
|
||||
`(`With code=$code, submit=$submit and userId=$userId`, ({ userId, code, directSubmit, expectRenderedUserIDError, expectRenderedCodePrefilled, expectRenderedCodeError }) => {
|
||||
|
||||
let renderResult: RenderResult;
|
||||
beforeEach(async () => {
|
||||
await act(async () => {
|
||||
renderResult = render(await RootLayout({
|
||||
children: await Page({
|
||||
searchParams: {
|
||||
code: code,
|
||||
submit: directSubmit,
|
||||
userID: userId,
|
||||
}
|
||||
})
|
||||
}))
|
||||
// TODO: Replace the above syntax for awaiting the JSX.Element once https://github.com/DefinitelyTyped/DefinitelyTyped/pull/65135 is released
|
||||
// renderResult = render(<Page searchParams={{
|
||||
// code: code,
|
||||
// submit: submit,
|
||||
// userID: userId,
|
||||
// }} />)
|
||||
})
|
||||
})
|
||||
|
||||
it(`should have rendered`, () => {
|
||||
expect(renderResult.container.firstChild).toBeDefined();
|
||||
})
|
||||
describe(`With expectRenderedUserIDError=${expectRenderedUserIDError}`, () => {
|
||||
if (expectRenderedUserIDError) {
|
||||
it(`should show the error "${noUserIDError}"`, () => {
|
||||
const error = screen.getByText(noUserIDError)
|
||||
expect(error).toBeInTheDocument()
|
||||
expect(error).toBeVisible()
|
||||
})
|
||||
} else {
|
||||
it(`should not show the error "${noUserIDError}`, () => {
|
||||
const error = screen.queryByText(noUserIDError)
|
||||
expect(error).not.toBeInTheDocument()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe(`With expectRenderedCodePrefilled=${expectRenderedCodePrefilled}`, () => {
|
||||
if (typeof expectRenderedCodePrefilled == 'string') {
|
||||
it(`should show the ${codeLabelText} input with the value "${expectRenderedCodePrefilled}" prefilled`, async () => {
|
||||
await waitFor(() => {
|
||||
const codeInput = screen.getByLabelText(codeLabelText)
|
||||
expect(codeInput).toHaveTextContent(expectRenderedCodePrefilled)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
it(`should not show the ${codeLabelText} input`, () => {
|
||||
const codeInput = screen.queryByText(codeLabelText)
|
||||
expect(codeInput).not.toBeInTheDocument()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe(`With expectRenderedCodeError=${expectRenderedCodeError}`, () => {
|
||||
if (expectRenderedCodeError) {
|
||||
it(`should show the error "${noCodeError}"`, () => {
|
||||
const error = screen.getByText(noCodeError)
|
||||
expect(error).toBeInTheDocument()
|
||||
expect(error).toBeVisible()
|
||||
})
|
||||
} else {
|
||||
it(`should not show the error "${expectRenderedCodeError}`, () => {
|
||||
const error = screen.queryByText(noCodeError)
|
||||
expect(error).not.toBeInTheDocument()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (!directSubmit) {
|
||||
describe(`With click on submit`, () => {
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
});
|
||||
@@ -1,30 +0,0 @@
|
||||
// Polyfill "window.fetch" used in the React component.
|
||||
import 'whatwg-fetch'
|
||||
|
||||
import * as mockRouter from 'next-router-mock';
|
||||
import { mockLoginBackend } from './mock-login-backend'
|
||||
|
||||
// Inspired by https://github.com/scottrippey/next-router-mock/issues/67#issuecomment-1564906960
|
||||
const useRouter = mockRouter.useRouter;
|
||||
|
||||
const MockNextNavigation = {
|
||||
...mockRouter,
|
||||
usePathname: () => {
|
||||
const router = useRouter();
|
||||
return router.pathname;
|
||||
},
|
||||
useSearchParams: () => {
|
||||
const router = useRouter();
|
||||
const path = router.asPath.split('?')?.[1] ?? '';
|
||||
return new URLSearchParams(path);
|
||||
},
|
||||
};
|
||||
|
||||
// jest.mock('next/navigation', () => MockNextNavigation);
|
||||
// jest.mock('next/router', () => mockRouter)
|
||||
|
||||
beforeAll(() => {
|
||||
mockLoginBackend.listen()
|
||||
})
|
||||
beforeEach(() => mockLoginBackend.resetHandlers())
|
||||
afterAll(() => mockLoginBackend.close())
|
||||
@@ -8,12 +8,10 @@ export default async (): Promise<Config.InitialOptions> => {
|
||||
},
|
||||
setupFilesAfterEnv: [
|
||||
'@testing-library/jest-dom/extend-expect',
|
||||
'<rootDir>/jest-setup.ts',
|
||||
],
|
||||
moduleNameMapper: {
|
||||
'^#/(.*)$': '<rootDir>/$1',
|
||||
},
|
||||
testEnvironment: 'jsdom',
|
||||
clearMocks: true,
|
||||
};
|
||||
};
|
||||
@@ -60,7 +60,7 @@
|
||||
"@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",
|
||||
|
||||
43
apps/login/ui/PasswordComplexity.test.tsx
Normal file
43
apps/login/ui/PasswordComplexity.test.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { render, screen, waitFor, within } from '@testing-library/react';
|
||||
import PasswordComplexity from './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)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
});
|
||||
@@ -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,14 @@ 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>
|
||||
|
||||
@@ -31,6 +31,7 @@ export {
|
||||
|
||||
export { type LegalAndSupportSettings } from "./proto/server/zitadel/settings/v2alpha/legal_settings";
|
||||
export { type PasswordComplexitySettings } from "./proto/server/zitadel/settings/v2alpha/password_settings";
|
||||
export { type ResourceOwnerType } from "./proto/server/zitadel/settings/v2alpha/settings";
|
||||
|
||||
import {
|
||||
getServers,
|
||||
|
||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -139,8 +139,8 @@ importers:
|
||||
specifier: 18.11.9
|
||||
version: 18.11.9
|
||||
'@types/react':
|
||||
specifier: 18.0.25
|
||||
version: 18.0.25
|
||||
specifier: 18.2.8
|
||||
version: 18.2.8
|
||||
'@types/react-dom':
|
||||
specifier: 18.0.9
|
||||
version: 18.0.9
|
||||
@@ -5886,7 +5886,7 @@ packages:
|
||||
/@types/react-dom@18.0.9:
|
||||
resolution: {integrity: sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==}
|
||||
dependencies:
|
||||
'@types/react': 18.0.25
|
||||
'@types/react': 17.0.52
|
||||
dev: true
|
||||
|
||||
/@types/react@17.0.52:
|
||||
@@ -5897,8 +5897,8 @@ packages:
|
||||
csstype: 3.1.1
|
||||
dev: true
|
||||
|
||||
/@types/react@18.0.25:
|
||||
resolution: {integrity: sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==}
|
||||
/@types/react@18.2.8:
|
||||
resolution: {integrity: sha512-lTyWUNrd8ntVkqycEEplasWy2OxNlShj3zqS0LuB1ENUGis5HodmhM7DtCoUGbxj3VW/WsGA0DUhpG6XrM7gPA==}
|
||||
dependencies:
|
||||
'@types/prop-types': 15.7.5
|
||||
'@types/scheduler': 0.16.2
|
||||
|
||||
Reference in New Issue
Block a user