Files
KanbanGit/tests/Fixtures/form-feedback.mjs
T
Twyco 802294d6a2
CI/CD / test_and_check_app (push) Canceled after 0s
CI/CD / Publish immutable image (push) Canceled after 0s
CI/CD / Publish immutable image (pull_request) Canceled after 0s
CI/CD / test_and_check_app (pull_request) Canceled after 6s
feat(KAN-21): rebuild login verification and two-factor flows
2026-09-11 14:18:23 +02:00

172 lines
5.9 KiB
JavaScript

import assert from 'node:assert/strict';
import { fileURLToPath } from 'node:url';
import vue from '@vitejs/plugin-vue';
import { createServer } from 'vite';
import { createSSRApp, h } from 'vue';
import { renderToString } from 'vue/server-renderer';
const server = await createServer({
configFile: false,
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('../../resources/js', import.meta.url)),
},
},
server: { middlewareMode: true, hmr: false, watch: null },
appType: 'custom',
});
try {
const { isFormNetworkFailure, matchesFormRequest, formRequestHeader } =
await server.ssrLoadModule('/resources/js/lib/form-feedback.ts');
const config = { headers: { [formRequestHeader]: 'submission-a' } };
for (const code of ['ERR_NETWORK', 'ECONNABORTED', 'ETIMEDOUT']) {
assert.equal(
isFormNetworkFailure(
{ isAxiosError: true, code, config },
'submission-a',
),
true,
);
}
for (const error of [
null,
new Error('Render failed'),
{ code: 'ERR_NETWORK', config },
{ isAxiosError: true, code: 'ERR_CANCELED', config },
{
isAxiosError: true,
code: 'ERR_NETWORK',
config,
response: { status: 500 },
},
{ isAxiosError: true, code: 'ERR_NETWORK', config: {} },
]) {
assert.equal(isFormNetworkFailure(error, 'submission-a'), false);
}
assert.equal(
isFormNetworkFailure(
{ isAxiosError: true, code: 'ERR_NETWORK', config },
'submission-b',
),
false,
);
assert.equal(matchesFormRequest(config, null), false);
assert.equal(matchesFormRequest(config, 'submission-a'), true);
assert.equal(matchesFormRequest(undefined, 'submission-a'), false);
const { createLocalization } = await server.ssrLoadModule(
'/resources/js/lib/localization.ts',
);
const { en } = await server.ssrLoadModule('/resources/js/locales/en.ts');
const { default: FeedbackState } = await server.ssrLoadModule(
'/resources/js/components/FeedbackState.vue',
);
const { default: FormFeedback } = await server.ssrLoadModule(
'/resources/js/components/FormFeedback.vue',
);
const { default: InputError } = await server.ssrLoadModule(
'/resources/js/components/InputError.vue',
);
const { default: AuthFormFeedback } = await server.ssrLoadModule(
'/resources/js/components/AuthFormFeedback.vue',
);
const { default: PasswordInput } = await server.ssrLoadModule(
'/resources/js/components/PasswordInput.vue',
);
const localization = createLocalization(
{ locale: 'de' },
{
de: {
...en,
'common.retry': 'Erneut versuchen',
'feedback.empty': 'Noch keine Einträge',
'auth.feedback.expiredTitle': 'Sitzung abgelaufen',
'auth.password.show': 'Passwort anzeigen',
},
},
);
const render = (component, props) =>
renderToString(
createSSRApp({ render: () => h(component, props) }).use(
localization,
),
);
assert.match(
await render(FeedbackState, { state: 'empty' }),
/Noch keine Einträge/,
);
const loading = await render(FormFeedback, { state: 'loading' });
assert.match(loading, /role="status"/);
assert.match(loading, /aria-busy="true"/);
assert.match(loading, /motion-reduce:animate-none/);
assert.doesNotMatch(loading, /<button/);
const validation = await render(FormFeedback, { state: 'validation' });
assert.match(validation, /role="alert"/);
assert.match(validation, /Check your entries/);
assert.doesNotMatch(validation, /<button/);
for (const state of ['network', 'server', 'cancelled']) {
const html = await render(FormFeedback, { state });
assert.match(html, /Erneut versuchen/);
assert.match(html, /type="button"/);
assert.match(html, /role="alert"/);
}
assert.match(
await render(FormFeedback, { state: 'network' }),
/Connection interrupted/,
);
assert.match(
await render(FormFeedback, { state: 'server' }),
/unexpected response/,
);
assert.match(
await render(FormFeedback, { state: 'success' }),
/Changes saved/,
);
assert.doesNotMatch(await render(FormFeedback, { state: 'idle' }), /role=/);
const fieldError = await render(InputError, {
message: '<script>alert(1)</script>',
});
assert.match(fieldError, /role="alert"/);
assert.match(fieldError, /text-destructive/);
assert.doesNotMatch(fieldError, /<script>/);
const expired = await render(AuthFormFeedback, {
state: 'server',
responseStatus: 419,
});
assert.match(expired, /Sitzung abgelaufen/);
assert.match(expired, /href="\/login"/);
assert.doesNotMatch(expired, /Erneut versuchen/);
assert.match(
await render(AuthFormFeedback, {
state: 'server',
responseStatus: 429,
}),
/Too many attempts/,
);
assert.match(
await render(AuthFormFeedback, {
state: 'loading',
loadingTitle: 'Signing in',
}),
/Signing in/,
);
assert.match(
await render(AuthFormFeedback, { state: 'validation' }),
/Check your details/,
);
const password = await render(PasswordInput, {
disabled: true,
autocomplete: 'current-password',
});
assert.match(password, /Passwort anzeigen/);
assert.match(password, /autocomplete="current-password"/);
assert.match(password, /aria-pressed="false"/);
assert.doesNotMatch(password, /tabindex="-1"/);
assert.match(password, /<button[^>]*disabled/);
console.log('Form feedback behavior checks passed.');
} finally {
await server.close();
}