mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-25 14:09:03 -04:00

the #shared alias used by Nuxt cannot be easily disabled and to prevent breackage with jiti, we make use of it
105 lines
3.7 KiB
TypeScript
105 lines
3.7 KiB
TypeScript
import fs from 'node:fs/promises';
|
|
|
|
import type { AsyncExpectationResult } from '@vitest/expect';
|
|
import { HtmlValidate } from 'html-validate/node';
|
|
import type { Result } from 'html-validate/node';
|
|
import marked from 'marked';
|
|
import { describe, expect, test } from 'vitest';
|
|
|
|
import { extractMetadata } from '#shared/blog/metadata.ts';
|
|
import parseMarkdown from '#shared/parseMarkdown.ts';
|
|
import { Translator } from '#shared/translator.ts';
|
|
import type { Config } from '~~/locale/config.ts';
|
|
import allLocales from '~~/locale/locales.ts';
|
|
import type { Translations } from '~~/locale/translations.ts';
|
|
import { loadSuml } from '~~/server/loader.ts';
|
|
|
|
const validator = new HtmlValidate({
|
|
extends: [
|
|
'html-validate:recommended',
|
|
],
|
|
rules: {
|
|
'attr-case': 'off',
|
|
'element-required-attributes': 'off',
|
|
'no-deprecated-attr': 'off',
|
|
'no-inline-style': 'off',
|
|
'prefer-tbody': 'off',
|
|
'wcag/h30': 'off',
|
|
'wcag/h63': 'off',
|
|
// these originate from the current markdown parser
|
|
'valid-id': 'off',
|
|
'no-trailing-whitespace': 'off',
|
|
},
|
|
});
|
|
|
|
async function toBeValidHTML(actual: string): AsyncExpectationResult {
|
|
const validatorReport = await validator.validateString(actual);
|
|
const results: Result[] = validatorReport.results;
|
|
const messages = results
|
|
.flatMap((result) => result.messages)
|
|
.map((message) => `${message.ruleId} at ${message.line}:${message.column} (${message.selector})\n\t${message.message}`);
|
|
|
|
if (messages.length > 0) {
|
|
return {
|
|
message: () => `expected to be valid HTML\n\n${messages.join('\n')}`,
|
|
pass: false,
|
|
};
|
|
} else {
|
|
return {
|
|
message: () => 'expected to be invalid HTML',
|
|
pass: true,
|
|
};
|
|
}
|
|
}
|
|
|
|
interface CustomMatchers<R> {
|
|
toBeValidHTML(): Promise<R>;
|
|
}
|
|
|
|
declare module 'vitest' {
|
|
interface Assertion<T> extends CustomMatchers<T> {}
|
|
}
|
|
|
|
expect.extend({ toBeValidHTML });
|
|
|
|
const baseTranslations = await loadSuml<Translations>('locale/_base/translations.suml');
|
|
|
|
describe.each(allLocales)('blog in $code', async ({ code }) => {
|
|
const config = await loadSuml<Config>(`locale/${code}/config.suml`);
|
|
const translations = await loadSuml<Translations>(`locale/${code}/translations.suml`);
|
|
|
|
const translator = new Translator(translations, baseTranslations, config);
|
|
|
|
const blogDirectory = `locale/${code}/blog`;
|
|
const outputDirectory = `test/output/locale/${code}/blog`;
|
|
const slugs = (await fs.readdir(blogDirectory))
|
|
.filter((file) => file.endsWith('.md'))
|
|
.map((file) => file.replace(/\.md$/, ''));
|
|
|
|
test('contains posts when enabled', () => {
|
|
if (config.links.blog) {
|
|
expect(slugs).not.toHaveLength(0);
|
|
}
|
|
});
|
|
|
|
describe.each(slugs)('post %s', (slug) => {
|
|
test('has valid metadata', async () => {
|
|
const content = await fs.readFile(`${blogDirectory}/${slug}.md`, 'utf-8');
|
|
const metadata = extractMetadata(config, content);
|
|
expect(metadata?.title).toBeTruthy();
|
|
expect(metadata?.authors.length).toBeGreaterThan(0);
|
|
expect(metadata?.date).toMatch(/\d\d\d\d-\d\d-\d\d/);
|
|
});
|
|
test('is valid HTML', async () => {
|
|
const content = await fs.readFile(`${blogDirectory}/${slug}.md`, 'utf-8');
|
|
const parsed = marked(content);
|
|
const blogEntry = await parseMarkdown(parsed, translator);
|
|
|
|
await fs.mkdir(outputDirectory, { recursive: true });
|
|
await fs.writeFile(`${outputDirectory}/${slug}.html`, blogEntry.content ?? '');
|
|
|
|
await expect(blogEntry.content).toBeValidHTML();
|
|
});
|
|
});
|
|
});
|