Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/tests/verify-snapshot.ts
6446 views
1
/*
2
* verify-snapshot.ts
3
*
4
* Copyright (C) 2024 Posit Software, PBC
5
*/
6
7
import { extname } from "../src/deno_ral/path.ts";
8
import { normalizeNewlines } from "../src/core/text.ts";
9
import { withDocxContent } from "./verify.ts";
10
import { createPatch, diffWordsWithSpace, diffChars } from "../src/core/lib/external/diff.js";
11
12
import * as slimdom from "slimdom";
13
import xpath from "fontoxpath";
14
15
type Canonicalizer = (file: string) => Promise<string>;
16
17
const ipynbCanonicalizer = (file: string) => {
18
const json = JSON.parse(Deno.readTextFileSync(file));
19
for (const cell of json.cells) {
20
if (cell.id.match(/^[0-9a-f-]+$/)) {
21
cell.id = "<uuid>";
22
}
23
}
24
return Promise.resolve(JSON.stringify(json, null, 2));
25
}
26
27
const docxCanonicalizer = async (fileName: string) => {
28
return withDocxContent(fileName, async (content) => {
29
const xmlDoc = slimdom.parseXmlDocument(content);
30
for await (const element of xpath.evaluateXPathToAsyncIterator("//pic:cNvPr", xmlDoc)) {
31
element.setAttribute("descr", "<uuid>");
32
}
33
return slimdom.serializeToWellFormedString(xmlDoc);
34
});
35
}
36
37
const canonicalizers: Record<string, Canonicalizer> = {
38
"ipynb": ipynbCanonicalizer,
39
"docx": docxCanonicalizer,
40
};
41
42
const readAndNormalizeNewlines = (file: string) => {
43
return normalizeNewlines(Deno.readTextFileSync(file));
44
}
45
46
export const canonicalizeSnapshot = async (file: string) => {
47
const origFile = file;
48
if (file.endsWith(".snapshot")) {
49
file = file.slice(0, -9);
50
}
51
let ext = extname(file).slice(1);
52
const canonicalizer = canonicalizers[ext] || readAndNormalizeNewlines;
53
return canonicalizer(origFile);
54
}
55
56
export const checkSnapshot = async (file: string) => {
57
const outputCanonical = await canonicalizeSnapshot(file);
58
const snapshotCanonical = await canonicalizeSnapshot(file + ".snapshot");
59
return outputCanonical === snapshotCanonical;
60
}
61
62
export const generateSnapshotDiff = async (file: string): Promise<string> => {
63
const outputCanonical = await canonicalizeSnapshot(file);
64
const snapshotCanonical = await canonicalizeSnapshot(file + ".snapshot");
65
return createPatch(
66
file + ".snapshot",
67
snapshotCanonical,
68
outputCanonical,
69
"expected",
70
"actual"
71
);
72
}
73
74
export type WordDiffPart = { value: string; added?: boolean; removed?: boolean };
75
76
export const generateInlineDiff = async (file: string): Promise<WordDiffPart[]> => {
77
const outputCanonical = await canonicalizeSnapshot(file);
78
const snapshotCanonical = await canonicalizeSnapshot(file + ".snapshot");
79
80
const stripWhitespace = (s: string) => s.replace(/\s+/g, "");
81
const isWhitespaceOnlyChange = stripWhitespace(outputCanonical) === stripWhitespace(snapshotCanonical);
82
83
if (isWhitespaceOnlyChange) {
84
return diffChars(snapshotCanonical, outputCanonical);
85
}
86
87
return diffWordsWithSpace(snapshotCanonical, outputCanonical);
88
}
89
90