mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 07:18:47 +00:00
update
This commit is contained in:
34
docs/__tests__/buildNav.test.js
Normal file
34
docs/__tests__/buildNav.test.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const { buildNav } = require('../src/generator');
|
||||
|
||||
test('generates navigation tree', () => {
|
||||
const pages = [
|
||||
{ file: 'guide/install.md', data: { title: 'Install', order: 1 } },
|
||||
{ file: 'guide/usage.md', data: { title: 'Usage', order: 2 } },
|
||||
{ file: 'guide/nested/info.md', data: { title: 'Info', order: 1 } }
|
||||
];
|
||||
const tree = buildNav(pages);
|
||||
const guide = tree.find(n => n.name === 'guide');
|
||||
expect(guide).toBeDefined();
|
||||
expect(guide.children.length).toBe(3);
|
||||
const install = guide.children.find(c => c.name === 'install.md');
|
||||
expect(install.path).toBe('/guide/install.html');
|
||||
});
|
||||
|
||||
test('adds display names and section flags', () => {
|
||||
const pages = [
|
||||
{ file: '02-api.md', data: { title: 'API', order: 2 } },
|
||||
{ file: '01-guide/index.md', data: { title: 'Guide', order: 1 } },
|
||||
{ file: '01-guide/setup.md', data: { title: 'Setup', order: 2 } },
|
||||
{ file: 'index.md', data: { title: 'Home', order: 10 } }
|
||||
];
|
||||
const nav = buildNav(pages);
|
||||
expect(nav[0].name).toBe('index.md');
|
||||
const guide = nav.find(n => n.name === '01-guide');
|
||||
expect(guide.displayName).toBe('Guide');
|
||||
expect(guide.isSection).toBe(true);
|
||||
const api = nav.find(n => n.name === '02-api.md');
|
||||
expect(api.displayName).toBe('API');
|
||||
// alphabetical within same order
|
||||
expect(nav[1].name).toBe('01-guide');
|
||||
expect(nav[2].name).toBe('02-api.md');
|
||||
});
|
13
docs/__tests__/loadConfig.test.js
Normal file
13
docs/__tests__/loadConfig.test.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const loadConfig = require('../src/config/loadConfig');
|
||||
|
||||
test('loads configuration and merges defaults', () => {
|
||||
const dir = fs.mkdtempSync(path.join(__dirname, 'cfg-'));
|
||||
const file = path.join(dir, 'config.yaml');
|
||||
fs.writeFileSync(file, 'site:\n title: Test Site\n');
|
||||
const cfg = loadConfig(file);
|
||||
expect(cfg.site.title).toBe('Test Site');
|
||||
expect(cfg.navigation.search).toBe(true);
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
});
|
23
docs/__tests__/pluginHooks.test.js
Normal file
23
docs/__tests__/pluginHooks.test.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const loadPlugins = require('../src/config/loadPlugins');
|
||||
|
||||
test('plugin hook modifies data', async () => {
|
||||
const dir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'plugins-'));
|
||||
const pluginFile = path.join(dir, 'test.plugin.js');
|
||||
fs.writeFileSync(
|
||||
pluginFile,
|
||||
"module.exports = { onParseMarkdown: ({ content }) => ({ content: content + '!!' }) };\n"
|
||||
);
|
||||
|
||||
const plugins = loadPlugins({ pluginsDir: dir, plugins: ['test.plugin'] });
|
||||
let data = { content: 'hello' };
|
||||
for (const plugin of plugins) {
|
||||
if (typeof plugin.onParseMarkdown === 'function') {
|
||||
const res = await plugin.onParseMarkdown(data);
|
||||
if (res !== undefined) data = res;
|
||||
}
|
||||
}
|
||||
expect(data.content).toBe('hello!!');
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
});
|
77
docs/__tests__/renderMarkdown.test.js
Normal file
77
docs/__tests__/renderMarkdown.test.js
Normal file
@@ -0,0 +1,77 @@
|
||||
jest.mock('@11ty/eleventy', () => {
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
return class Eleventy {
|
||||
constructor(input, output) {
|
||||
this.input = input;
|
||||
this.output = output;
|
||||
}
|
||||
setConfig() {}
|
||||
async write() {
|
||||
const walk = d => {
|
||||
const entries = fs.readdirSync(d, { withFileTypes: true });
|
||||
let files = [];
|
||||
for (const e of entries) {
|
||||
const p = path.join(d, e.name);
|
||||
if (e.isDirectory()) files = files.concat(walk(p));
|
||||
else if (p.endsWith('.md')) files.push(p);
|
||||
}
|
||||
return files;
|
||||
};
|
||||
for (const file of walk(this.input)) {
|
||||
const rel = path.relative(this.input, file).replace(/\.md$/, '.html');
|
||||
const dest = path.join(this.output, rel);
|
||||
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
||||
fs.writeFileSync(dest, '<header></header><aside class="sidebar"></aside>');
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const { generate } = require('../src/generator');
|
||||
|
||||
function getPaths(tree) {
|
||||
const paths = [];
|
||||
for (const node of tree) {
|
||||
if (node.path) paths.push(node.path);
|
||||
if (node.children) paths.push(...getPaths(node.children));
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
test('markdown files render with layout and appear in nav/search', async () => {
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'df-test-'));
|
||||
const contentDir = path.join(tmp, 'content');
|
||||
const outputDir = path.join(tmp, '_site');
|
||||
fs.mkdirSync(path.join(contentDir, 'guide'), { recursive: true });
|
||||
fs.writeFileSync(path.join(contentDir, 'index.md'), '# Home\nWelcome');
|
||||
fs.writeFileSync(path.join(contentDir, 'guide', 'install.md'), '# Install\nSteps');
|
||||
const configPath = path.join(tmp, 'config.yaml');
|
||||
fs.writeFileSync(configPath, 'site:\n title: Test\n');
|
||||
|
||||
await generate({ contentDir, outputDir, configPath });
|
||||
|
||||
const indexHtml = fs.readFileSync(path.join(outputDir, 'index.html'), 'utf8');
|
||||
const installHtml = fs.readFileSync(path.join(outputDir, 'guide', 'install.html'), 'utf8');
|
||||
expect(indexHtml).toContain('<header');
|
||||
expect(indexHtml).toContain('<aside class="sidebar"');
|
||||
expect(installHtml).toContain('<header');
|
||||
expect(installHtml).toContain('<aside class="sidebar"');
|
||||
|
||||
const nav = JSON.parse(fs.readFileSync(path.join(outputDir, 'navigation.json'), 'utf8'));
|
||||
const navPaths = getPaths(nav);
|
||||
expect(navPaths).toContain('/index.html');
|
||||
expect(navPaths).toContain('/guide/install.html');
|
||||
|
||||
const search = JSON.parse(fs.readFileSync(path.join(outputDir, 'search-index.json'), 'utf8'));
|
||||
const docs = search.docs.map(d => d.id);
|
||||
expect(docs).toContain('index.html');
|
||||
expect(docs).toContain('guide/install.html');
|
||||
const installDoc = search.docs.find(d => d.id === 'guide/install.html');
|
||||
expect(installDoc.body).toContain('Steps');
|
||||
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
});
|
128
docs/__tests__/responsive.test.js
Normal file
128
docs/__tests__/responsive.test.js
Normal file
@@ -0,0 +1,128 @@
|
||||
jest.mock('@11ty/eleventy', () => {
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
return class Eleventy {
|
||||
constructor(input, output) {
|
||||
this.input = input;
|
||||
this.output = output;
|
||||
}
|
||||
setConfig() {}
|
||||
async write() {
|
||||
const walk = d => {
|
||||
const entries = fs.readdirSync(d, { withFileTypes: true });
|
||||
let files = [];
|
||||
for (const e of entries) {
|
||||
const p = path.join(d, e.name);
|
||||
if (e.isDirectory()) files = files.concat(walk(p));
|
||||
else if (p.endsWith('.md')) files.push(p);
|
||||
}
|
||||
return files;
|
||||
};
|
||||
for (const file of walk(this.input)) {
|
||||
const rel = path.relative(this.input, file).replace(/\.md$/, '.html');
|
||||
const dest = path.join(this.output, rel);
|
||||
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
dest,
|
||||
`<!DOCTYPE html><html><head><link rel="stylesheet" href="/assets/theme.css"></head><body><header><button id="sidebar-toggle" class="sidebar-toggle">☰</button></header><div class="container"><aside class="sidebar"></aside><main></main></div><script src="/assets/theme.js"></script></body></html>`
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const http = require('http');
|
||||
const os = require('os');
|
||||
const puppeteer = require('puppeteer');
|
||||
const { generate } = require('../src/generator');
|
||||
|
||||
jest.setTimeout(30000);
|
||||
|
||||
let server;
|
||||
let browser;
|
||||
let port;
|
||||
let tmp;
|
||||
|
||||
beforeAll(async () => {
|
||||
tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'df-responsive-'));
|
||||
const contentDir = path.join(tmp, 'content');
|
||||
const outputDir = path.join(tmp, '_site');
|
||||
fs.mkdirSync(contentDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(contentDir, 'index.md'), '# Home\n');
|
||||
await generate({ contentDir, outputDir });
|
||||
fs.cpSync(path.join(__dirname, '../assets'), path.join(outputDir, 'assets'), { recursive: true });
|
||||
|
||||
server = http.createServer((req, res) => {
|
||||
let filePath = path.join(outputDir, req.url === '/' ? 'index.html' : req.url);
|
||||
if (req.url.startsWith('/assets')) {
|
||||
filePath = path.join(outputDir, req.url);
|
||||
}
|
||||
fs.readFile(filePath, (err, data) => {
|
||||
if (err) {
|
||||
res.writeHead(404);
|
||||
res.end('Not found');
|
||||
return;
|
||||
}
|
||||
const ext = path.extname(filePath).slice(1);
|
||||
const type = { html: 'text/html', js: 'text/javascript', css: 'text/css' }[ext] || 'application/octet-stream';
|
||||
res.writeHead(200, { 'Content-Type': type });
|
||||
res.end(data);
|
||||
});
|
||||
});
|
||||
await new Promise(resolve => {
|
||||
server.listen(0, () => {
|
||||
port = server.address().port;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
browser = await puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (browser) await browser.close();
|
||||
if (server) server.close();
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test('sidebar opens on small screens', async () => {
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 500, height: 800 });
|
||||
await page.goto(`http://localhost:${port}/`);
|
||||
await page.waitForSelector('#sidebar-toggle');
|
||||
await page.click('#sidebar-toggle');
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
const bodyClass = await page.evaluate(() => document.body.classList.contains('sidebar-open'));
|
||||
const sidebarLeft = await page.evaluate(() => getComputedStyle(document.querySelector('.sidebar')).left);
|
||||
expect(bodyClass).toBe(true);
|
||||
expect(sidebarLeft).toBe('0px');
|
||||
});
|
||||
|
||||
test('clicking outside closes sidebar on small screens', async () => {
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 500, height: 800 });
|
||||
await page.goto(`http://localhost:${port}/`);
|
||||
await page.waitForSelector('#sidebar-toggle');
|
||||
await page.click('#sidebar-toggle');
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
await page.click('main');
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
const bodyClass = await page.evaluate(() => document.body.classList.contains('sidebar-open'));
|
||||
expect(bodyClass).toBe(false);
|
||||
});
|
||||
|
||||
test('sidebar toggles on large screens', async () => {
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 1024, height: 800 });
|
||||
await page.goto(`http://localhost:${port}/`);
|
||||
await page.waitForSelector('#sidebar-toggle');
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
let sidebarWidth = await page.evaluate(() => getComputedStyle(document.querySelector('.sidebar')).width);
|
||||
expect(sidebarWidth).toBe('240px');
|
||||
await page.click('#sidebar-toggle');
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
sidebarWidth = await page.evaluate(() => getComputedStyle(document.querySelector('.sidebar')).width);
|
||||
expect(sidebarWidth).toBe('0px');
|
||||
});
|
Reference in New Issue
Block a user