Files
govoplan-core/webui/tests/password-generator.test.tsx
T

94 lines
4.2 KiB
TypeScript

function assert(condition: unknown, message = "assertion failed"): asserts condition {
if (!condition) throw new Error(message);
}
import { renderToStaticMarkup } from "react-dom/server";
import PasswordField from "../src/components/PasswordField";
import PasswordGeneratorDialog from "../src/components/PasswordGeneratorDialog";
import {
DEFAULT_PASSWORD_GENERATOR_OPTIONS,
generateSecurePassword,
type SecureRandomFill
} from "../src/components/passwordGenerator";
import { PlatformLanguageProvider } from "../src/i18n/LanguageContext";
function sequenceFill(sequence: number[]): SecureRandomFill {
let index = 0;
return (values) => {
values[0] = sequence[index % sequence.length] >>> 0;
index += 1;
return values;
};
}
const password = generateSecurePassword(
DEFAULT_PASSWORD_GENERATOR_OPTIONS,
sequenceFill(Array.from({ length: 64 }, (_, index) => index * 7919))
);
assert(password.length === 20, "the requested password length is preserved");
assert(/[a-z]/.test(password), "selected lowercase characters are represented");
assert(/[A-Z]/.test(password), "selected uppercase characters are represented");
assert(/[0-9]/.test(password), "selected digits are represented");
assert(/[!#$%&*+\-=?@^_]/.test(password), "selected symbols are represented");
let rejectedDraws = 0;
const rejectionAware = generateSecurePassword(
{ length: 12, lowercase: true, uppercase: false, digits: false, symbols: false },
(values) => {
values[0] = rejectedDraws++ % 2 === 0 ? 0xffff_ffff : 7;
return values;
}
);
assert(rejectionAware.length === 12, "out-of-range random draws are rejected without shortening output");
assert(rejectedDraws > 12, "modulo-bias rejection consumes a replacement secure draw");
for (const [options, expected] of [
[{ ...DEFAULT_PASSWORD_GENERATOR_OPTIONS, length: 11 }, "between 12 and 128"],
[{ ...DEFAULT_PASSWORD_GENERATOR_OPTIONS, lowercase: false, uppercase: false, digits: false, symbols: false }, "at least one character set"]
] as const) {
let message = "";
try {
generateSecurePassword(options, sequenceFill([1]));
} catch (error) {
message = error instanceof Error ? error.message : String(error);
}
assert(message.includes(expected), `invalid generator options should explain ${expected}`);
}
const markup = renderToStaticMarkup(
<PlatformLanguageProvider>
<PasswordField value="" onValueChange={() => undefined} generator helpContextId="access.authentication.password" helpModuleId="access" />
</PlatformLanguageProvider>
);
assert(markup.includes('aria-label="Generate password"'), "the opt-in generator action is accessible");
assert(markup.includes("lucide-dice-5"), "the familiar generator icon is used");
assert(!markup.includes("password-generator-dialog"), "the generator dialog stays closed until explicitly requested");
assert(markup.includes('data-help-context-id="access.authentication.password"'), "the owner context reaches the password field and its actions");
assert(markup.includes('data-help-module-id="access"'), "the password field retains its documentation owner");
for (const flags of [{}, { generator: true, disabled: true }, { generator: true, readOnly: true }]) {
const unavailableMarkup = renderToStaticMarkup(
<PlatformLanguageProvider>
<PasswordField value="fixture-password" onValueChange={() => undefined} {...flags} />
</PlatformLanguageProvider>
);
assert(!unavailableMarkup.includes('aria-label="Generate password"'), "unavailable generation cannot open the optional dialog");
assert(!unavailableMarkup.includes("module-load-progress"), "closed generators never begin loading while rendering a field");
}
const dialogMarkup = renderToStaticMarkup(
<PlatformLanguageProvider>
<PasswordGeneratorDialog
open
helpContextId="access.authentication.password"
helpModuleId="access"
onUse={() => undefined}
onClose={() => undefined}
/>
</PlatformLanguageProvider>
);
assert(dialogMarkup.includes('data-help-context-id="access.authentication.password"'), "the generator dialog inherits the calling credential context");
assert(dialogMarkup.includes('data-help-module-id="access"'), "generated-password controls retain the credential owner's module");
console.log("Password generator contract passed.");