diff --git a/apps/login/app/(login)/verify/page.test.tsx b/apps/login/app/(login)/verify/page.test.tsx deleted file mode 100644 index ef87916b24e..00000000000 --- a/apps/login/app/(login)/verify/page.test.tsx +++ /dev/null @@ -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() - }) - }) - - 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`, () => { - - }) - } - }) -}); \ No newline at end of file diff --git a/apps/login/jest-setup.ts b/apps/login/jest-setup.ts deleted file mode 100644 index 6f6d89c2852..00000000000 --- a/apps/login/jest-setup.ts +++ /dev/null @@ -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()) \ No newline at end of file diff --git a/apps/login/jest.config.ts b/apps/login/jest.config.ts index 6dfa0dab385..1be286e7dd7 100644 --- a/apps/login/jest.config.ts +++ b/apps/login/jest.config.ts @@ -8,12 +8,10 @@ export default async (): Promise => { }, setupFilesAfterEnv: [ '@testing-library/jest-dom/extend-expect', - '/jest-setup.ts', ], moduleNameMapper: { '^#/(.*)$': '/$1', }, testEnvironment: 'jsdom', - clearMocks: true, }; }; \ No newline at end of file diff --git a/apps/login/package.json b/apps/login/package.json index 40065973ca4..db8d32c4a2f 100644 --- a/apps/login/package.json +++ b/apps/login/package.json @@ -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", diff --git a/apps/login/ui/PasswordComplexity.test.tsx b/apps/login/ui/PasswordComplexity.test.tsx new file mode 100644 index 00000000000..13d0d37f4d9 --- /dev/null +++ b/apps/login/ui/PasswordComplexity.test.tsx @@ -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('', () => { + 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() + }) + 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) + }) + }) + } + }) +}); \ No newline at end of file diff --git a/apps/login/ui/PasswordComplexity.tsx b/apps/login/ui/PasswordComplexity.tsx index efe32641c2c..10cba0edd1b 100644 --- a/apps/login/ui/PasswordComplexity.tsx +++ b/apps/login/ui/PasswordComplexity.tsx @@ -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" > + Matches + Doesn't match -
- {hasMinLength ? check : cross} - - Password length {passwordComplexitySettings.minLength} - -
+ {passwordComplexitySettings.minLength != undefined ? ( +
+ {hasMinLength ? check : cross} + + Password length {passwordComplexitySettings.minLength} + +
+ ) : }
{hasSymbol ? check : cross} has Symbol diff --git a/packages/zitadel-server/src/index.ts b/packages/zitadel-server/src/index.ts index bc8b26f2835..c9823843546 100644 --- a/packages/zitadel-server/src/index.ts +++ b/packages/zitadel-server/src/index.ts @@ -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, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6c65f4c3246..57fc4a18980 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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