Use C8 for coverage

This commit is contained in:
Dolan Miu
2023-06-12 18:43:29 +01:00
parent 09ab91eeef
commit eeee01a0e0
11 changed files with 655 additions and 2090 deletions

View File

@ -17,7 +17,6 @@
"dolan",
"execa",
"falsey",
"fflate",
"iife",
"Initializable",
"iroha",

3
.gitignore vendored
View File

@ -59,6 +59,3 @@ My Document.docx
# Temporary folder
tmp
# nyc
.nyc_output

25
.nycrc
View File

@ -1,25 +0,0 @@
{
"check-coverage": true,
"statements": 99.87,
"branches": 98.21,
"functions": 100,
"lines": 99.86,
"include": [
"src/**/*.ts"
],
"exclude": [
"src/**/*.spec.ts",
"src/import-dotx/import-dotx.ts"
],
"reporter": [
"lcov",
"text",
"json"
],
"extension": [
".ts"
],
"cache": true,
"all": true,
"sourceMap": true
}

2509
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,9 +22,8 @@
],
"scripts": {
"build": "tsc && vite build",
"test": "vitest run",
"test.coverage": "nyc npm test",
"test.watch": "vitest",
"test": "vitest",
"test.coverage": "vitest run --coverage",
"prepublishOnly": "npm run build --omit=dev",
"lint": "eslint --ext .ts src",
"predemo": "npm run build",
@ -72,16 +71,13 @@
},
"homepage": "https://docx.js.org",
"devDependencies": {
"@types/chai": "^4.2.15",
"@types/chai-as-promised": "^7.1.5",
"@types/inquirer": "^9.0.3",
"@types/prompt": "^1.1.1",
"@types/unzipper": "^0.10.4",
"@types/xml": "^1.0.8",
"@typescript-eslint/eslint-plugin": "^5.36.1",
"@typescript-eslint/parser": "^5.36.1",
"chai": "^4.3.6",
"chai-as-promised": "^7.1.1",
"@vitest/coverage-c8": "^0.32.0",
"cspell": "^6.2.2",
"docsify-cli": "^4.3.0",
"eslint": "^8.23.0",
@ -95,7 +91,6 @@
"glob": "^9.3.0",
"inquirer": "^9.2.7",
"jsdom": "^22.1.0",
"nyc": "^15.1.0",
"pre-commit": "^1.2.2",
"prettier": "^2.3.1",
"process": "^0.11.10",

View File

@ -1,6 +1,4 @@
/* tslint:disable:typedef space-before-function-paren */
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import * as fflate from "fflate";
import { File } from "@file/file";
import { Footer, Header } from "@file/header";
@ -9,21 +7,6 @@ import * as convenienceFunctions from "@util/convenience-functions";
import { Compiler } from "./next-compiler";
const unzip = (zipFile: Uint8Array): Promise<ReadonlySet<string>> => {
const set = new Set<string>();
const unzipper = new fflate.Unzip((file) => {
set.add(file.name);
});
return new Promise<ReadonlySet<string>>((resolve) => {
setTimeout(() => {
resolve(set);
}, 1000);
unzipper.push(zipFile, true);
});
};
describe("Compiler", () => {
let compiler: Compiler;
@ -42,18 +25,18 @@ describe("Compiler", () => {
describe("#compile()", () => {
it(
"should pack all the content",
async () => {
() => {
const file = new File({
sections: [],
comments: {
children: [],
},
});
const zipFile = await compiler.compile(file);
const fileNames = await unzip(zipFile);
const zipFile = compiler.compile(file);
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
expect(fileNames).has.length(13);
expect(fileNames).to.include("word/document.xml");
expect(fileNames).is.an.instanceof(Array);
expect(fileNames).has.length(17);
expect(fileNames).to.include("word/document.xml");
expect(fileNames).to.include("word/styles.xml");
expect(fileNames).to.include("docProps/core.xml");
@ -75,7 +58,7 @@ describe("Compiler", () => {
it(
"should pack all additional headers and footers",
async () => {
() => {
const file = new File({
sections: [
{
@ -107,10 +90,12 @@ describe("Compiler", () => {
],
});
const zipFile = await compiler.compile(file);
const fileNames = await unzip(zipFile);
const zipFile = compiler.compile(file);
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
expect(fileNames).is.an.instanceof(Array);
expect(fileNames).has.length(25);
expect(fileNames).has.length(21);
expect(fileNames).to.include("word/header1.xml");
expect(fileNames).to.include("word/_rels/header1.xml.rels");
expect(fileNames).to.include("word/header2.xml");

View File

@ -1,4 +1,4 @@
import { strToU8, zip } from "fflate";
import JSZip from "jszip";
import xml from "xml";
import { File } from "@file/file";
@ -44,39 +44,26 @@ export class Compiler {
this.numberingReplacer = new NumberingReplacer();
}
public compile(file: File, prettifyXml?: PrettifyType): Promise<Uint8Array> {
public compile(file: File, prettifyXml?: PrettifyType): JSZip {
const zip = new JSZip();
const xmlifiedFileMapping = this.xmlifyFile(file, prettifyXml);
const map = new Map<string, IXmlifyedFile | readonly IXmlifyedFile[]>(Object.entries(xmlifiedFileMapping));
return new Promise<Uint8Array>((resolve, reject) => {
zip(
{
...Array.from(map.entries()).reduce((acc, [, obj]) => {
if (Array.isArray(obj)) {
for (const subFile of obj as readonly IXmlifyedFile[]) {
// eslint-disable-next-line functional/immutable-data
acc[subFile.path] = strToU8(subFile.data);
}
} else {
// eslint-disable-next-line functional/immutable-data
acc[(obj as IXmlifyedFile).path] = strToU8((obj as IXmlifyedFile).data);
}
for (const [, obj] of map) {
if (Array.isArray(obj)) {
for (const subFile of obj as readonly IXmlifyedFile[]) {
zip.file(subFile.path, subFile.data);
}
} else {
zip.file((obj as IXmlifyedFile).path, (obj as IXmlifyedFile).data);
}
}
return acc;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}, {} as any),
...file.Media.Array.reduce((acc, { stream, fileName }) => ({ ...acc, [`word/media/${fileName}`]: stream }), {}),
},
{},
(err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
},
);
});
for (const { stream, fileName } of file.Media.Array) {
zip.file(`word/media/${fileName}`, stream);
}
return zip;
}
private xmlifyFile(file: File, prettify?: PrettifyType): IXmlifyedFileMapping {

View File

@ -3,7 +3,7 @@ import { afterEach, assert, beforeEach, describe, expect, it, vi } from "vitest"
import { File } from "@file/file";
import { HeadingLevel, Paragraph } from "@file/paragraph";
import { Packer } from "./packer";
import { Packer, PrettifyType } from "./packer";
describe("Packer", () => {
let file: File;
@ -35,6 +35,36 @@ describe("Packer", () => {
});
});
describe("prettify", () => {
afterEach(() => {
vi.restoreAllMocks();
});
it("should use a default prettify value", async () => {
const spy = vi.spyOn((Packer as any).compiler, "compile");
await Packer.toString(file, true);
expect(spy).toBeCalledWith(expect.anything(), PrettifyType.WITH_2_BLANKS);
});
it("should use a prettify value", async () => {
const spy = vi.spyOn((Packer as any).compiler, "compile");
await Packer.toString(file, PrettifyType.WITH_4_BLANKS);
expect(spy).toBeCalledWith(expect.anything(), PrettifyType.WITH_4_BLANKS);
});
it("should use an undefined prettify value", async () => {
const spy = vi.spyOn((Packer as any).compiler, "compile");
await Packer.toString(file, false);
expect(spy).toBeCalledWith(expect.anything(), undefined);
});
});
describe("#toString()", () => {
it("should return a non-empty string", async () => {
const result = await Packer.toString(file);
@ -133,7 +163,10 @@ describe("Packer", () => {
describe("#toStream()", () => {
it("should create a standard docx file", async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
vi.spyOn((Packer as any).compiler, "compile").mockReturnValue(Promise.resolve(new Uint8Array(255)));
vi.spyOn((Packer as any).compiler, "compile").mockReturnValue({
// tslint:disable-next-line: no-empty
generateAsync: () => Promise.resolve(vi.fn()),
});
const stream = Packer.toStream(file);
const p = new Promise<void>((resolve, reject) => {

View File

@ -1,6 +1,5 @@
import { Stream } from "stream";
import { File } from "@file/file";
import { strFromU8 } from "fflate";
import { Compiler } from "./next-compiler";
@ -14,46 +13,63 @@ export enum PrettifyType {
WITH_TAB = "\t",
}
const convertPrettifyType = (prettify?: boolean | PrettifyType): PrettifyType | undefined =>
prettify === true ? PrettifyType.WITH_2_BLANKS : prettify === false ? undefined : prettify;
export class Packer {
public static async toString(file: File, prettify?: boolean | PrettifyType): Promise<string> {
const zip = await this.compiler.compile(
file,
prettify === true ? PrettifyType.WITH_2_BLANKS : prettify === false ? undefined : prettify,
);
return strFromU8(zip);
const zip = this.compiler.compile(file, convertPrettifyType(prettify));
const zipData = await zip.generateAsync({
type: "string",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
compression: "DEFLATE",
});
return zipData;
}
public static async toBuffer(file: File, prettify?: boolean | PrettifyType): Promise<Buffer> {
const zip = await this.compiler.compile(
file,
prettify === true ? PrettifyType.WITH_2_BLANKS : prettify === false ? undefined : prettify,
);
return Buffer.from(zip.buffer);
const zip = this.compiler.compile(file, convertPrettifyType(prettify));
const zipData = await zip.generateAsync({
type: "nodebuffer",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
compression: "DEFLATE",
});
return zipData;
}
public static async toBase64String(file: File, prettify?: boolean | PrettifyType): Promise<string> {
const zip = await this.compiler.compile(
file,
prettify === true ? PrettifyType.WITH_2_BLANKS : prettify === false ? undefined : prettify,
);
const zip = this.compiler.compile(file, convertPrettifyType(prettify));
const zipData = await zip.generateAsync({
type: "base64",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
compression: "DEFLATE",
});
return Promise.resolve(strFromU8(zip));
return zipData;
}
public static async toBlob(file: File, prettify?: boolean | PrettifyType): Promise<Blob> {
const zip = await this.compiler.compile(
file,
prettify === true ? PrettifyType.WITH_2_BLANKS : prettify === false ? undefined : prettify,
);
const zip = this.compiler.compile(file, convertPrettifyType(prettify));
const zipData = await zip.generateAsync({
type: "blob",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
compression: "DEFLATE",
});
return new Blob([zip.buffer]);
return zipData;
}
public static toStream(file: File, prettify?: boolean | PrettifyType): Stream {
const zip = this.compiler.compile(file, prettify === true ? PrettifyType.WITH_2_BLANKS : prettify === false ? undefined : prettify);
const stream = new Stream();
const zip = this.compiler.compile(file, convertPrettifyType(prettify));
zip.then((z) => {
zip.generateAsync({
type: "nodebuffer",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
compression: "DEFLATE",
}).then((z) => {
stream.emit("data", z);
stream.emit("end");
});

View File

@ -1,14 +1,10 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import * as chai from "chai";
import JSZip from "jszip";
import chaiAsPromised from "chai-as-promised";
import { ExternalHyperlink, ImageRun, Paragraph, TextRun } from "@file/paragraph";
import { patchDocument, PatchType } from "./from-docx";
chai.use(chaiAsPromised);
const MOCK_XML = `
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas"

View File

@ -22,6 +22,7 @@ export default defineConfig({
},
},
build: {
minify: false,
lib: {
entry: [resolve(__dirname, "src/index.ts")],
name: "docx",
@ -35,5 +36,13 @@ export default defineConfig({
},
test: {
environment: "jsdom",
coverage: {
provider: "c8",
reporter: ["text", "json", "html"],
statements: 99.93,
branches: 98.85,
functions: 100,
lines: 99.93,
},
},
});