utils.test.ts 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import { describe, expect, test } from 'bun:test';
  2. import {
  3. extractFromHtml,
  4. extractHeadingsFromMarkdown,
  5. joinRenderedContent,
  6. withCssTreeWarningsSuppressed,
  7. } from './utils';
  8. // 200 段逗号分隔的 box-shadow 链 —— csstree/csstree#294 的复现案例,
  9. // 稳定触发 css-tree lexer 15000 迭代上限警告(jsdom 29 + css-tree 3.2.1 已验证)。
  10. // 若上游修复后不再触发,本测试退化为弱断言(无泄漏仍成立),可移除 helper。
  11. const CSS_TREE_WARNING_HTML = (() => {
  12. const shadows: string[] = [];
  13. for (let i = 1; i <= 200; i++) {
  14. shadows.push(`${i}px 0 0 -${Math.min(i + 3, 200)}px #cfcfcf`);
  15. }
  16. return `<!DOCTYPE html><html><head><style>
  17. .range-block__range::-webkit-slider-thumb { box-shadow: ${shadows.join(', ')}; }
  18. </style></head><body><article><h1>Hello</h1><p>World</p></article></body></html>`;
  19. })();
  20. describe('smartfetch/utils', () => {
  21. test('extracts cleaned headings from markdown', () => {
  22. const headings = extractHeadingsFromMarkdown(
  23. ['# Intro', '## Details ###', '### C#', 'plain text'].join('\n'),
  24. );
  25. expect(headings).toEqual(['Intro', 'Details', 'C#']);
  26. });
  27. test('injects metadata comments after an XML declaration in html output', () => {
  28. const result = joinRenderedContent(
  29. '---\nsource: "smartfetch"\n---\n\n',
  30. '<?xml version="1.0"?><root>ok</root>',
  31. 'html',
  32. );
  33. expect(result).toStartWith('<?xml version="1.0"?>');
  34. expect(result).toContain('<!--\n---\nsource: "smartfetch"\n---\n-->');
  35. expect(result).toContain('<root>ok</root>');
  36. });
  37. test('suppresses css-tree warnings during html extraction', async () => {
  38. const originalWarn = console.warn;
  39. const warnCalls: unknown[][] = [];
  40. console.warn = (...args: unknown[]) => warnCalls.push(args);
  41. try {
  42. const result = await extractFromHtml(
  43. CSS_TREE_WARNING_HTML,
  44. 'https://example.com/',
  45. false,
  46. );
  47. const cssTreeWarnings = warnCalls.filter((args) =>
  48. String(args[0]).startsWith('[csstree-match]'),
  49. );
  50. expect(cssTreeWarnings).toEqual([]);
  51. expect(result.text).toContain('Hello');
  52. expect(result.text).toContain('World');
  53. } finally {
  54. console.warn = originalWarn;
  55. }
  56. });
  57. test('filters only css-tree warnings inside the guard', () => {
  58. const originalWarn = console.warn;
  59. const warnCalls: unknown[][] = [];
  60. console.warn = (...args: unknown[]) => warnCalls.push(args);
  61. try {
  62. withCssTreeWarningsSuppressed(() => {
  63. console.warn('[csstree-match] BREAK after 15000 iterations');
  64. console.warn('[smartfetch] unrelated warning');
  65. });
  66. expect(warnCalls).toEqual([['[smartfetch] unrelated warning']]);
  67. } finally {
  68. console.warn = originalWarn;
  69. }
  70. });
  71. test('restores the original console.warn after extraction', async () => {
  72. const originalWarn = console.warn;
  73. try {
  74. await extractFromHtml(
  75. CSS_TREE_WARNING_HTML,
  76. 'https://example.com/',
  77. true,
  78. );
  79. expect(console.warn).toBe(originalWarn);
  80. } finally {
  81. console.warn = originalWarn;
  82. }
  83. });
  84. });