@diplodoc/cli-tests 0.0.0-rc-fix-translate-openapi-includer-202507171205 → 0.0.0-rc-resolve-codeblock-202509151421

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/e2e/__snapshots__/bundles.spec.ts.snap +168 -0
  2. package/e2e/__snapshots__/generate-map.spec.ts.snap +345 -0
  3. package/e2e/__snapshots__/include-toc.test.ts.snap +42 -0
  4. package/e2e/__snapshots__/load-custom-resources.spec.ts.snap +31 -117
  5. package/e2e/__snapshots__/metadata.spec.ts.snap +18 -51
  6. package/e2e/__snapshots__/plugin-corner-cases.spec.ts.snap +0 -0
  7. package/e2e/__snapshots__/preprocess.test.ts.snap +581 -0
  8. package/e2e/__snapshots__/regression.test.ts.snap +151 -268
  9. package/e2e/__snapshots__/restricted-access.test.ts.snap +20 -0
  10. package/e2e/__snapshots__/rtl.spec.ts.snap +6 -102
  11. package/e2e/__snapshots__/search.test.ts.snap +21 -36
  12. package/e2e/__snapshots__/single-page.spec.ts.snap +157 -0
  13. package/e2e/__snapshots__/skip-html-extension.spec.ts.snap +152 -0
  14. package/e2e/__snapshots__/translation.spec.ts.snap +469 -1186
  15. package/e2e/bundles.spec.ts +15 -0
  16. package/e2e/errors.spec.ts +33 -2
  17. package/e2e/generate-map.spec.ts +9 -3
  18. package/e2e/preprocess.test.ts +33 -0
  19. package/e2e/redirects-validation.spec.ts +61 -0
  20. package/e2e/search.test.ts +1 -1
  21. package/e2e/single-page.spec.ts +22 -0
  22. package/e2e/skip-html-extension.spec.ts +15 -0
  23. package/e2e/translation.spec.ts +21 -2
  24. package/fixtures/cliAdapter.ts +23 -0
  25. package/fixtures/globals.d.ts +4 -0
  26. package/fixtures/runners/binary.ts +4 -3
  27. package/fixtures/runners/types.ts +1 -2
  28. package/fixtures/utils/file.ts +53 -10
  29. package/fixtures/utils/test.ts +17 -8
  30. package/mocks/bundles/input/.yfm +13 -0
  31. package/mocks/bundles/input/index.md +12 -0
  32. package/mocks/bundles/input/page1.md +3 -0
  33. package/mocks/bundles/input/page2.md +5 -0
  34. package/mocks/bundles/input/toc.yaml +9 -0
  35. package/mocks/docs-viewer-interface/input/.yfm +6 -0
  36. package/mocks/docs-viewer-interface/input/index.md +3 -0
  37. package/mocks/docs-viewer-interface/input/toc.yaml +5 -0
  38. package/mocks/errors/extract-filtered-link/input/filtered.md +1 -0
  39. package/mocks/errors/extract-filtered-link/input/filtered2.md +0 -0
  40. package/mocks/errors/extract-filtered-link/input/index.md +7 -0
  41. package/mocks/errors/extract-filtered-link/input/toc.yaml +1 -0
  42. package/mocks/load-custom-resources/md2html-with-resources/input/page.md +9 -2
  43. package/mocks/load-custom-resources/md2md-with-resources/input/page.md +9 -2
  44. package/mocks/load-custom-resources/single-page-with-resources/input/page.md +9 -2
  45. package/mocks/metadata/md2html-with-metadata/input/page.md +8 -1
  46. package/mocks/metadata/md2md-with-metadata/input/page.md +7 -0
  47. package/mocks/preprocess/input/.yfm +1 -0
  48. package/mocks/preprocess/input/1.md +29 -0
  49. package/mocks/preprocess/input/_assets/1.png +0 -0
  50. package/mocks/preprocess/input/_assets/1.svg +0 -0
  51. package/mocks/preprocess/input/autotitle.md +23 -0
  52. package/mocks/preprocess/input/commented-include.md +3 -0
  53. package/mocks/preprocess/input/images.md +5 -0
  54. package/mocks/preprocess/input/included-item.md +12 -0
  55. package/mocks/preprocess/input/includes/deep.md +1 -0
  56. package/mocks/preprocess/input/includes/deepWithIndent.md +6 -0
  57. package/mocks/preprocess/input/includes/presets.yaml +2 -0
  58. package/mocks/preprocess/input/includes/sub/user.md +1 -0
  59. package/mocks/preprocess/input/includes/sub/userWithIndent.md +5 -0
  60. package/mocks/preprocess/input/includes/test.md +1 -0
  61. package/mocks/preprocess/input/includes/user.md +1 -0
  62. package/mocks/preprocess/input/includes.md +18 -0
  63. package/mocks/preprocess/input/latex.md +3 -0
  64. package/mocks/preprocess/input/mermaid.md +13 -0
  65. package/mocks/preprocess/input/presets.yaml +8 -0
  66. package/mocks/preprocess/input/sub/folder/item-1.md +7 -0
  67. package/mocks/preprocess/input/sub/toc.yaml +5 -0
  68. package/mocks/preprocess/input/toc-i.yaml +3 -0
  69. package/mocks/preprocess/input/toc.yaml +16 -0
  70. package/mocks/redirects-validation/extensions-deprecation/input/blah.md +0 -0
  71. package/mocks/redirects-validation/extensions-deprecation/input/redirects.yaml +5 -0
  72. package/mocks/redirects-validation/extensions-deprecation/input/toc.yaml +4 -0
  73. package/mocks/redirects-validation/invalid-regex/input/blah.md +0 -0
  74. package/mocks/redirects-validation/invalid-regex/input/redirects.yaml +3 -0
  75. package/mocks/redirects-validation/invalid-regex/input/toc.yaml +4 -0
  76. package/mocks/redirects-validation/malformed-redirect/input/blah.md +0 -0
  77. package/mocks/redirects-validation/malformed-redirect/input/redirects.yaml +6 -0
  78. package/mocks/redirects-validation/malformed-redirect/input/toc.yaml +4 -0
  79. package/mocks/redirects-validation/same-path/input/blah.md +0 -0
  80. package/mocks/redirects-validation/same-path/input/redirects.yaml +3 -0
  81. package/mocks/redirects-validation/same-path/input/toc.yaml +4 -0
  82. package/mocks/redirects-validation/unparseable/input/blah.md +0 -0
  83. package/mocks/redirects-validation/unparseable/input/redirects.yaml +11 -0
  84. package/mocks/redirects-validation/unparseable/input/toc.yaml +4 -0
  85. package/mocks/regression/input/.yfm +2 -0
  86. package/mocks/regression/input/autotitle.md +3 -0
  87. package/mocks/regression/input/images.md +2 -0
  88. package/mocks/regression/input/includes/deep.md +2 -0
  89. package/mocks/regression/input/includes/presets.yaml +1 -0
  90. package/mocks/regression/input/includes/tools.md +1 -0
  91. package/mocks/regression/input/includes.md +5 -0
  92. package/mocks/regression/input/merge/merge.md +2 -0
  93. package/mocks/regression/input/merge/presets.yaml +1 -0
  94. package/mocks/regression/input/merge/toc.yaml +2 -0
  95. package/mocks/regression/input/presets.yaml +1 -0
  96. package/mocks/regression/input/toc.yaml +2 -0
  97. package/mocks/search/input/.yfm +12 -0
  98. package/mocks/single-page/input/ru/index.yaml +9 -0
  99. package/mocks/single-page/input/ru/page.md +14 -0
  100. package/mocks/single-page/input/ru/project/config.md +1 -0
  101. package/mocks/single-page/input/ru/toc.yaml +7 -0
  102. package/mocks/skip-html-extension/input/.yfm +1 -0
  103. package/mocks/skip-html-extension/input/folder/index.md +1 -0
  104. package/mocks/skip-html-extension/input/index-test-html/index.md +4 -0
  105. package/mocks/skip-html-extension/input/index.md +6 -0
  106. package/mocks/skip-html-extension/input/page1.md +3 -0
  107. package/mocks/skip-html-extension/input/page2.md +6 -0
  108. package/mocks/skip-html-extension/input/toc.yaml +13 -0
  109. package/mocks/translation/dir-files/input/.yfm +6 -0
  110. package/mocks/translation/dir-files/input/ru/aboba.md +2 -4
  111. package/mocks/translation/dir-files/input/ru/nested/a1.md +3 -0
  112. package/mocks/translation/dir-files/input/ru/nested/folder1/a1.md +3 -0
  113. package/mocks/translation/dir-files/input/ru/nested/folder1/toc-i.yaml +3 -0
  114. package/mocks/translation/dir-files/input/ru/nested/syntax/base.md +2 -0
  115. package/mocks/translation/dir-files/input/ru/nested/toc.yaml +1 -0
  116. package/mocks/translation/dir-files/input/ru/no-var-page.md +3 -0
  117. package/mocks/translation/dir-files/input/ru/to-be-excluded.md +1 -0
  118. package/mocks/translation/dir-files/input/ru/toc.yaml +7 -1
  119. package/mocks/translation/openapi/input/toc.yaml +1 -1
  120. package/mocks/warning/unreachable-autotitle/input/index.md +1 -0
  121. package/mocks/warning/unreachable-autotitle/input/link.md +0 -0
  122. package/mocks/warning/unreachable-autotitle/input/toc.yaml +4 -0
  123. package/package.json +5 -5
@@ -0,0 +1,15 @@
1
+ import {describe, it} from 'vitest';
2
+ import {TestAdapter, compareDirectories, getTestPaths} from '../fixtures';
3
+
4
+ describe('Check bundles', () => {
5
+ it('bundles list is correct', async () => {
6
+ const {inputPath, outputPath} = getTestPaths('mocks/bundles');
7
+
8
+ await TestAdapter.testBuildPass(inputPath, outputPath, {
9
+ md2md: false,
10
+ md2html: true,
11
+ args: '-j2',
12
+ });
13
+ await compareDirectories(outputPath, false, true);
14
+ });
15
+ });
@@ -13,12 +13,12 @@ function test(path: string, expect: Function) {
13
13
  const {inputPath, outputPath} = getTestPaths(path);
14
14
 
15
15
  const md = await TestAdapter.build.run(inputPath, outputPath, ['-j2', '-f', 'md']);
16
- const html = await TestAdapter.build.run(outputPath, outputPath + '-html', ['-j2', '-f', 'html']);
17
-
16
+ const html = await TestAdapter.build.run(inputPath, outputPath + '-html', ['-j2', '-f', 'html']);
18
17
  return expect({md, html});
19
18
  });
20
19
  }
21
20
 
21
+
22
22
  describe('Errors', () => {
23
23
  test('mocks/errors/unreachable-link', ({html}: TestResult) => {
24
24
  expectErrors(html, [
@@ -26,6 +26,29 @@ describe('Errors', () => {
26
26
  'ERR index.md: 2: YFM003 / unreachable-link Link is unreachable [Context: "[Unreachable link: "missed.html"][missed file](./missed.md)"]'
27
27
  ]);
28
28
  });
29
+
30
+ it('translate extract with filtered links', async () => {
31
+ const {inputPath, outputPath} = getTestPaths('mocks/errors/extract-filtered-link');
32
+
33
+ const result = await TestAdapter.extract.run(inputPath, outputPath, ['--source', 'ru-RU', '--target', 'es-ES', '--filter']);
34
+
35
+ expect(result.code).toEqual(1);
36
+
37
+ expect(result.errors).toEqual([
38
+ "ERR File index.md contains link to filtered.md, which was filtered from toc.yaml or it's not been included initially",
39
+ "ERR File index.md contains link to filtered2.md, which was filtered from toc.yaml or it's not been included initially",
40
+ "ERR File index.md contains link to filtered2.md, which was filtered from toc.yaml or it's not been included initially",
41
+ "ERR File index.md contains link to filtered3.md, which was filtered from toc.yaml or it's not been included initially",
42
+ ])
43
+ });
44
+ });
45
+
46
+ describe('Warnings', () => {
47
+ test('mocks/warning/unreachable-autotitle', ({html}: TestResult) => {
48
+ expectWarnings(html, [
49
+ 'WARN index.md: 1: YFM010 / unreachable-autotitle-anchor Auto title anchor is unreachable [Context: "[Unreachable autotitle anchor: "link.html#unknown_yfm010"][{#T}](./link.md#unknown_yfm010)"]',
50
+ ]);
51
+ });
29
52
  });
30
53
 
31
54
  function expectErrors(report: Report, errors: string[]) {
@@ -35,3 +58,11 @@ function expectErrors(report: Report, errors: string[]) {
35
58
  expect(report.errors).toContain(error);
36
59
  }
37
60
  }
61
+
62
+ function expectWarnings(report: Report, warnings: string[]) {
63
+ expect(report.warns.length).toEqual(warnings.length);
64
+
65
+ for (const warn of warnings) {
66
+ expect(report.warns).toContain(warn);
67
+ }
68
+ }
@@ -1,6 +1,7 @@
1
1
  import {describe, expect, test} from 'vitest';
2
2
  import {TestAdapter, bundleless, getFileContent, getTestPaths} from '../fixtures';
3
- import {join} from 'path';
3
+ import {join} from 'node:path';
4
+ import {readFile} from 'node:fs/promises';
4
5
 
5
6
  const generateMapTestTemplate = (
6
7
  testTitle: string,
@@ -11,11 +12,17 @@ const generateMapTestTemplate = (
11
12
  test(testTitle, async () => {
12
13
  const {inputPath, outputPath} = getTestPaths(testRootPath);
13
14
 
14
- await TestAdapter.testBuildPass(inputPath, outputPath, {md2md, md2html, args: '--add-map-file'});
15
+ await TestAdapter.testBuildPass(inputPath, outputPath, {
16
+ md2md,
17
+ md2html,
18
+ args: '--add-map-file --build-manifest',
19
+ });
15
20
 
16
21
  const content = getFileContent(join(outputPath, 'files.json'));
22
+ const manifestContent = await readFile(join(outputPath, 'yfm-build-manifest.json'), 'utf-8');
17
23
 
18
24
  expect(bundleless(content)).toMatchSnapshot();
25
+ expect(JSON.parse(manifestContent)).toMatchSnapshot();
19
26
  });
20
27
  };
21
28
 
@@ -28,7 +35,6 @@ describe('Generate map for', () => {
28
35
  generateMapTestTemplate(
29
36
  'project with single language and toc include - only md2html',
30
37
  'mocks/generate-map/test1',
31
- false,
32
38
  );
33
39
 
34
40
  generateMapTestTemplate('project with multiple language', 'mocks/generate-map/test2');
@@ -0,0 +1,33 @@
1
+ import {describe, test} from 'vitest';
2
+ import {TestAdapter, compareDirectories, getTestPaths} from '../fixtures';
3
+
4
+ const generateFilesYamlTestTemplate = (
5
+ testTitle: string,
6
+ testRootPath: string,
7
+ args: string[] = [],
8
+ ) => {
9
+ test(testTitle, async () => {
10
+ const {inputPath, outputPath} = getTestPaths(testRootPath);
11
+ await TestAdapter.testBuildPass(inputPath, outputPath, {
12
+ md2md:true,
13
+ md2html: false,
14
+ args: args.join(' '),
15
+ });
16
+ await TestAdapter.testBuildPass(outputPath, outputPath + '-html', {
17
+ md2md: false,
18
+ md2html: true,
19
+ args: args.join(' '),
20
+ });
21
+ await compareDirectories(outputPath);
22
+ });
23
+ };
24
+
25
+ describe('Preprocess', () => {
26
+ generateFilesYamlTestTemplate('HashIncludes=true,Autotitles=false', 'mocks/preprocess', [
27
+ '--no-merge-autotitles'
28
+ ]);
29
+
30
+ generateFilesYamlTestTemplate('HashIncludes=true,Autotitles=true', 'mocks/preprocess');
31
+
32
+ // generateFilesYamlTestTemplate('Nested toc restricted access', 'mocks/preprocess/test3');
33
+ });
@@ -0,0 +1,61 @@
1
+ import {describe, expect, it} from 'vitest';
2
+ import {TestAdapter, getTestPaths} from '../fixtures';
3
+
4
+ describe('Redirects validation', () => {
5
+ it('should emit an error on an unparseable redirects.yaml', async () => {
6
+ const {inputPath, outputPath} = getTestPaths('mocks/redirects-validation/unparseable');
7
+
8
+ const report = await TestAdapter.build.run(inputPath, outputPath, ['-f', 'md']);
9
+
10
+ expect(report.code).toBe(1);
11
+ expect(report.errors).toContainEqual(expect.stringMatching(/redirects.yaml parsing error/));
12
+ });
13
+
14
+ it.skip('should emit a warning when supplied with a `redirects.yaml` that mentions file extensions', async () => {
15
+ const {inputPath, outputPath} = getTestPaths(
16
+ 'mocks/redirects-validation/extensions-deprecation',
17
+ );
18
+
19
+ const report = await TestAdapter.build.run(inputPath, outputPath, ['-f', 'md']);
20
+
21
+ expect(report.code).toBe(0);
22
+ expect(report.warns).toContainEqual(
23
+ expect.stringMatching(/Redirects with explicit extensions are deprecated./),
24
+ );
25
+ });
26
+
27
+ it('should emit an error when an invalid regular expression pattern is encountered', async () => {
28
+ const {inputPath, outputPath} = getTestPaths('mocks/redirects-validation/invalid-regex');
29
+
30
+ const report = await TestAdapter.build.run(inputPath, outputPath, ['-f', 'md']);
31
+
32
+ expect(report.code).toBe(1);
33
+ expect(report.errors).toContainEqual(
34
+ expect.stringMatching(
35
+ /Redirects configuration results in a non-valid regular expression/,
36
+ ),
37
+ );
38
+ });
39
+
40
+ it('should emit an error when a redirect is malformed', async () => {
41
+ const {inputPath, outputPath} = getTestPaths('mocks/redirects-validation/malformed-redirect');
42
+
43
+ const report = await TestAdapter.build.run(inputPath, outputPath, ['-f', 'md']);
44
+
45
+ expect(report.code).toBe(1);
46
+ expect(report.errors).toContainEqual(
47
+ expect.stringMatching(/One of the two parameters is missing/),
48
+ );
49
+ });
50
+
51
+ it('should emit an error when a redirect leads to the same path', async () => {
52
+ const {inputPath, outputPath} = getTestPaths('mocks/redirects-validation/same-path');
53
+
54
+ const report = await TestAdapter.build.run(inputPath, outputPath, ['-f', 'md']);
55
+
56
+ expect(report.code).toBe(1);
57
+ expect(report.errors).toContainEqual(
58
+ expect.stringMatching(/Parameters must be different/),
59
+ );
60
+ });
61
+ });
@@ -8,7 +8,7 @@ describe('Local search', () => {
8
8
  await TestAdapter.testBuildPass(inputPath, outputPath, {
9
9
  md2md: false,
10
10
  md2html: true,
11
- args: '-j2 --search',
11
+ args: '-j2 --search --interface-toc',
12
12
  });
13
13
  await compareDirectories(outputPath);
14
14
  });
@@ -0,0 +1,22 @@
1
+ import {describe, test} from 'vitest';
2
+ import {TestAdapter, compareDirectories, getTestPaths} from '../fixtures';
3
+
4
+ const generateMapTestSinglePageTemplate = (
5
+ testTitle: string,
6
+ testRootPath: string,
7
+ {md2md = true, md2html = true, args = '--single-page'},
8
+ ) => {
9
+ test(testTitle, async () => {
10
+ const {inputPath, outputPath} = getTestPaths(testRootPath);
11
+ await TestAdapter.testBuildPass(inputPath, outputPath, {md2md, md2html, args});
12
+ await compareDirectories(outputPath);
13
+ });
14
+ };
15
+
16
+ describe('Single page mode', () => {
17
+ generateMapTestSinglePageTemplate(
18
+ 'simple md2html single page with lang dirs',
19
+ 'mocks/single-page',
20
+ {md2md: false},
21
+ );
22
+ });
@@ -0,0 +1,15 @@
1
+ import {describe, it} from 'vitest';
2
+ import {TestAdapter, compareDirectories, getTestPaths} from '../fixtures';
3
+
4
+ describe('Skip html extension', () => {
5
+ it('transforms links correctly', async () => {
6
+ const {inputPath, outputPath} = getTestPaths('mocks/skip-html-extension');
7
+
8
+ await TestAdapter.testBuildPass(inputPath, outputPath, {
9
+ md2md: false,
10
+ md2html: true,
11
+ args: '-j2 --skip-html-extension',
12
+ });
13
+ await compareDirectories(outputPath);
14
+ });
15
+ });
@@ -7,7 +7,7 @@ const generateMapTestTemplate = (
7
7
  args: TranslateRunArgs,
8
8
  ignoreFileContent = true,
9
9
  ) => {
10
- test.skip(testTitle, async () => {
10
+ test(testTitle, async () => {
11
11
  const {inputPath, outputPath} = getTestPaths(testRootPath);
12
12
 
13
13
  await TestAdapter.testTranslatePass(inputPath, outputPath, args);
@@ -56,10 +56,17 @@ describe('Translate command', () => {
56
56
  target: 'es-ES',
57
57
  });
58
58
 
59
+ generateMapTestTemplate('do not filter files on extract', 'mocks/translation/dir-files', {
60
+ subcommand: 'extract',
61
+ source: 'ru-RU',
62
+ target: 'es-ES',
63
+ });
64
+
59
65
  generateMapTestTemplate('filter files on extract', 'mocks/translation/dir-files', {
60
66
  subcommand: 'extract',
61
67
  source: 'ru-RU',
62
68
  target: 'es-ES',
69
+ additionalArgs: '--filter'
63
70
  });
64
71
 
65
72
  generateMapTestTemplate(
@@ -69,7 +76,19 @@ describe('Translate command', () => {
69
76
  subcommand: 'extract',
70
77
  source: 'ru-RU',
71
78
  target: 'es-ES',
72
- additionalArgs: '--exclude ru/_no-translate/*.md',
79
+ additionalArgs: '--exclude ru/to-be-excluded.md --filter',
80
+ },
81
+ );
82
+
83
+ const vars = {skip: 'prod'}
84
+ generateMapTestTemplate(
85
+ 'filter files on extract with extra vars option',
86
+ 'mocks/translation/dir-files',
87
+ {
88
+ subcommand: 'extract',
89
+ source: 'ru-RU',
90
+ target: 'es-ES',
91
+ additionalArgs: `--vars ${JSON.stringify(vars)} --filter`,
73
92
  },
74
93
  );
75
94
 
@@ -34,11 +34,34 @@ class Build {
34
34
  }
35
35
  }
36
36
 
37
+ class Extract {
38
+ private readonly runner: Runner;
39
+
40
+ constructor(runner: Runner) {
41
+ this.runner = runner;
42
+ }
43
+
44
+ run(input: string, output: string, args: string[]) {
45
+ return this.runner.runYfmDocs([
46
+ 'translate',
47
+ 'extract',
48
+ '--input',
49
+ input,
50
+ '--output',
51
+ output,
52
+ '--quiet',
53
+ ...args,
54
+ ]);
55
+ }
56
+ }
57
+
37
58
  export class CliTestAdapter {
38
59
  readonly runner: Runner = createRunner();
39
60
 
40
61
  readonly build = new Build(this.runner);
41
62
 
63
+ readonly extract = new Extract(this.runner);
64
+
42
65
  async testBuildPass(
43
66
  inputPath: string,
44
67
  outputPath: string,
@@ -0,0 +1,4 @@
1
+ declare module "@diplodoc/cli/manifest" {
2
+ const manifest: Record<string, Record<string, string[]>>;
3
+ export = manifest;
4
+ }
@@ -1,5 +1,6 @@
1
1
  import {Runner} from './types';
2
2
  import {execa} from 'execa';
3
+ import strip from 'strip-ansi';
3
4
 
4
5
  export class BinaryRunner implements Runner {
5
6
  private readonly binaryPath: string;
@@ -29,7 +30,7 @@ export class BinaryRunner implements Runner {
29
30
 
30
31
  function fillLog(filter: RegExp, source: string) {
31
32
  return source.split('\n')
32
- .filter((line) => line.match(filter))
33
- .map((line) => line.trim())
34
- .filter(Boolean);
33
+ .map((line) => strip(line).trim())
34
+ .filter(Boolean)
35
+ .filter((line) => line.match(filter));
35
36
  }
@@ -2,9 +2,8 @@ export type Report = {
2
2
  code: number;
3
3
  warns: string[];
4
4
  errors: string[];
5
- }
5
+ };
6
6
 
7
7
  export interface Runner {
8
8
  runYfmDocs(argv: string[]): Promise<Report>;
9
9
  }
10
-
@@ -1,18 +1,42 @@
1
1
  import {readFileSync} from 'node:fs';
2
+ import {rm} from 'node:fs/promises';
2
3
  import {join, resolve} from 'node:path';
3
4
  import {glob} from 'glob';
4
5
  import {bundleless, hashless, platformless} from './test';
5
6
  import {expect} from 'vitest';
6
- import {$} from 'execa';
7
+
8
+ const SYSTEM_DIRS = ['_bundle/', '_search/'];
7
9
 
8
10
  export function getFileContent(filePath: string) {
9
- return bundleless(platformless(readFileSync(filePath, 'utf8')));
11
+ return platformless(bundleless(readFileSync(filePath, 'utf8')));
10
12
  }
11
13
 
12
- const uselessFile = (file: string) =>
13
- !['_bundle/', '_assets/', '_search/'].some((part) => file.includes(part));
14
+ const uselessFile = (file: string, dirs: string[]) =>
15
+ !dirs.some((part) => file.includes(part));
16
+
17
+ export function stripSystemLinks(content: string) {
18
+ const dirPattern = SYSTEM_DIRS.map(d => d.replace('/', '\\/')).join('|');
19
+
20
+ content = content.replace(
21
+ new RegExp(`<script[^>]+src="(?:${dirPattern})[^"]*"[^>]*></script>`, 'g'),
22
+ ''
23
+ );
24
+
25
+ content = content.replace(
26
+ new RegExp(`<link[^>]+href="(?:${dirPattern})[^"]*"[^>]*\\/?>`, 'g'),
27
+ ''
28
+ );
29
+
30
+ content = content.replace(/^[ \t]*\r?\n/gm, '');
31
+
32
+ return content;
33
+ }
14
34
 
15
- export async function compareDirectories(outputPath: string, ignoreFileContent = false) {
35
+ export async function compareDirectories(
36
+ outputPath: string,
37
+ ignoreFileContent = false,
38
+ checkBundle = false,
39
+ ) {
16
40
  const filesFromOutput = (
17
41
  await glob(`**/*`, {
18
42
  cwd: outputPath,
@@ -21,13 +45,32 @@ export async function compareDirectories(outputPath: string, ignoreFileContent =
21
45
  nodir: true,
22
46
  posix: true,
23
47
  })
24
- ).sort();
48
+ ).map(bundleless).sort();
25
49
 
26
- expect(hashless(bundleless(JSON.stringify(filesFromOutput, null, 2)))).toMatchSnapshot('filelist');
50
+ let filesForSnapshot;
51
+
52
+ if (checkBundle) {
53
+ filesForSnapshot = filesFromOutput;
54
+ } else {
55
+ filesForSnapshot = filesFromOutput.filter(file => uselessFile(file, SYSTEM_DIRS));
56
+ }
57
+
58
+ // Here we sort the order of the included files after all processing
59
+ // This is necessary for better test stability
60
+ // We do not care in what order these files were received and processed
61
+ // We sort only the final list and put it in the snapshot
62
+ filesForSnapshot = filesForSnapshot.map(hashless).sort();
63
+
64
+ expect(JSON.stringify(filesForSnapshot, null, 2)).toMatchSnapshot('filelist');
27
65
 
28
66
  if (!ignoreFileContent) {
29
- filesFromOutput.filter(uselessFile).forEach((filePath) => {
30
- const content = getFileContent(resolve(outputPath, filePath));
67
+ filesFromOutput.filter(file => uselessFile(file, ['_assets/', ...SYSTEM_DIRS])).forEach((filePath) => {
68
+ let content = getFileContent(resolve(outputPath, filePath));
69
+
70
+ if (!checkBundle && filePath.endsWith('.html')) {
71
+ content = stripSystemLinks(content);
72
+ }
73
+
31
74
  expect(content).toMatchSnapshot();
32
75
  });
33
76
  }
@@ -46,5 +89,5 @@ export function getTestPaths(testRootPath: string): TestPaths {
46
89
  }
47
90
 
48
91
  export function cleanupDirectory(path: string) {
49
- return $`rm -rf ${path}`;
92
+ return rm(path, {recursive: true, force: true});
50
93
  }
@@ -1,11 +1,25 @@
1
- import {readFileSync} from 'node:fs';
1
+ import assets from '@diplodoc/cli/manifest';
2
2
 
3
3
  export function platformless(text: string): string {
4
+ let index = 1;
5
+
4
6
  return hashless(text)
5
7
  .replace(/\r\n/g, '\n')
8
+ // Fix for XML equiv-text attributes in Windows - handle various patterns
9
+ .replace(/equiv-text="[\r\n]+&#10;"/g, 'equiv-text="&#10;"')
10
+ .replace(/equiv-text="[\r\n]+&amp;#10;"/g, 'equiv-text="&amp;#10;"')
11
+ // Also normalize any other attributes that might have line ending issues
12
+ .replace(/(ctype|id)="[\r\n]+(.*?)"/g, '$1="$2"')
6
13
  .replace(/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}/g, 'UUID')
7
- .replace(/(content"?[:=]{1}[" ]{1}Diplodoc.*? )v\d+\.\d+\.\d+(?:-[\w-]+)?/g, `$1vDIPLODOC-VERSION`)
8
- .replace(/(\\(?![/"'])){1,2}/g, '/');
14
+ .replace(
15
+ /(content"?[:=]{1}[" ]{1}Diplodoc.*? )v\d+\.\d+\.\d+(?:-[\w-]+)?/g,
16
+ `$1vDIPLODOC-VERSION`,
17
+ )
18
+ .replace(/(\\(?![/"'])){1,2}/g, '/')
19
+ .replace(
20
+ /id=\\"inline-code-id-[a-zA-Z0-9]{8}\\"/g,
21
+ () => `id="inline-code-id-${index++}"`
22
+ )
9
23
  }
10
24
 
11
25
  export function hashless(text: string): string {
@@ -15,8 +29,6 @@ export function hashless(text: string): string {
15
29
  }
16
30
 
17
31
  export function bundleless(text: string): string {
18
- const assets = require('@diplodoc/client/manifest') as Record<string, Record<string, string[]>>;
19
-
20
32
  for (const [entryKey, entry] of Object.entries(assets)) {
21
33
  for (const [typeKey, type] of Object.entries(entry)) {
22
34
  for (let index = 0; index < type.length; index++) {
@@ -32,6 +44,3 @@ export function bundleless(text: string): string {
32
44
  return text;
33
45
  }
34
46
 
35
- export function getNormalizedContent(filePath: string): string {
36
- return bundleless(platformless(readFileSync(filePath, 'utf8')));
37
- }
@@ -0,0 +1,13 @@
1
+ allowHTML: true
2
+
3
+ meta:
4
+ rootPath: http://127.0.0.1:5000/
5
+
6
+ interface:
7
+ toc-header: false
8
+ favicon-src: https://storage.yandexcloud.net/diplodoc-www-assets/favicon/favicon.ico
9
+
10
+ search:
11
+ provider: local
12
+ tolerance: 2
13
+ confidense: phrased
@@ -0,0 +1,12 @@
1
+ ---
2
+ availableLangs:
3
+ - en
4
+ - ru
5
+ ---
6
+
7
+ # Header
8
+
9
+ Content
10
+
11
+ [Link 1](page1.md)
12
+ [Link 2](page2.md)
@@ -0,0 +1,3 @@
1
+ # Page 1
2
+
3
+ [Link](page2.md#hash)
@@ -0,0 +1,5 @@
1
+ # Page 2
2
+
3
+ ## hash
4
+
5
+ [External link](https://example.com)
@@ -0,0 +1,9 @@
1
+ title: Skip html extension
2
+ href: index.md
3
+
4
+ items:
5
+ - name: Title 1
6
+ href: page1.md
7
+ - name: Title 2
8
+ href: page2.md
9
+
@@ -0,0 +1,6 @@
1
+ langs: ['en','ru']
2
+
3
+ interface:
4
+ toc: false
5
+ feedback: false
6
+ search: false
@@ -0,0 +1,3 @@
1
+ # Hello World!
2
+
3
+ some text inside
@@ -0,0 +1,5 @@
1
+ title: Test123
2
+ href: index.md
3
+ items:
4
+ - name: Главная
5
+ href: index.md
@@ -0,0 +1 @@
1
+ File exists but not attached to toc
@@ -0,0 +1,7 @@
1
+ [filtered file](./filtered.md)
2
+
3
+ {% if test == "dev" %}[will be deleted on translate-extract](./index.md){% endif %}
4
+
5
+ [second missed link](./filtered2.md)
6
+ [second missed link](./filtered2.md)
7
+ [second missed link](./filtered3.md)
@@ -0,0 +1 @@
1
+ href: index.md
@@ -1,7 +1,14 @@
1
1
  ---
2
+ title: Page Title
3
+ description: Some test description
4
+
5
+ interface:
6
+ toc: false
7
+ favicon-src: /favicon.ico
8
+
2
9
  metadata:
3
10
  - name: yfm
4
- content: builder
11
+ content: builder in page
5
12
  ---
6
13
 
7
- Lorem
14
+ Lorem
@@ -1,7 +1,14 @@
1
1
  ---
2
+ title: Page Title
3
+ description: Some test description
4
+
5
+ interface:
6
+ toc: false
7
+ favicon-src: /favicon.ico
8
+
2
9
  metadata:
3
10
  - name: yfm
4
- content: builder
11
+ content: builder in page
5
12
  ---
6
13
 
7
- Lorem
14
+ Lorem
@@ -1,7 +1,14 @@
1
1
  ---
2
+ title: Page Title
3
+ description: Some test description
4
+
5
+ interface:
6
+ toc: false
7
+ favicon-src: /favicon.ico
8
+
2
9
  metadata:
3
10
  - name: yfm
4
- content: builder
11
+ content: builder in page
5
12
  ---
6
13
 
7
- Lorem
14
+ Lorem
@@ -1,7 +1,14 @@
1
1
  ---
2
+ title: Page Title
3
+ description: Some test description
4
+
5
+ interface:
6
+ toc: false
7
+ favicon-src: /favicon.ico
8
+
2
9
  metadata:
3
10
  - name: yfm
4
- value: builder in page
11
+ content: builder in page
5
12
  ---
6
13
 
7
14
  Lorem