Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tests/integration/converters/outputs
3 changes: 3 additions & 0 deletions bunfig.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ coverage = true
coveragePathIgnorePatterns = [
"tests/converters/helpers/commonTests.ts"
]
pathIgnorePatterns = [
"**/integration/**"
]
2 changes: 1 addition & 1 deletion eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import tseslint, { parser as eslintParserTypeScript } from "typescript-eslint";

export default defineConfig(
{
ignores: ["**/node_modules/**", "dist/**"],
ignores: ["**/node_modules/**", "dist/**", "tests/integration/converters/outputs/**"],
},
js.configs.recommended,
tseslint.configs.recommended,
Expand Down
1 change: 1 addition & 0 deletions knip.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"$schema": "https://unpkg.com/knip@6/schema.json",
"entry": ["tests/**/*.test.ts"],
"project": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.css", "tests/**/*.ts"],
"ignore": ["tests/integration/converters/outputs/**"],
"ignoreBinaries": [
"pandoc",
"ffmpeg",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"lint:eslint": "eslint .",
"lint:prettier": "prettier --check .",
"lint:xss": "xss-scan",
"test": "bun test --path-ignore-patterns=tests/integration",
"test": "bun test",
"test:integration": "docker build --target test -t convertx:test . && docker run --rm -t -v ./tests:/app/tests -v ./src:/app/src -v ./package.json:/app/package.json convertx:test bun test tests/integration"
},
"dependencies": {
Expand Down
100 changes: 100 additions & 0 deletions tests/integration/converters/all-formats.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { expect, test } from "bun:test";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { convert as ffmpeg } from "../../../src/converters/ffmpeg";
import { convert as graphicsmagick } from "../../../src/converters/graphicsmagick";
import { convert as imagemagick } from "../../../src/converters/imagemagick";
import { convert as inkscape } from "../../../src/converters/inkscape";
import { convert as libjxl } from "../../../src/converters/libjxl";
import { getPossibleTargets } from "../../../src/converters/main";
import { convert as vips } from "../../../src/converters/vips";
import { convert as vtracer } from "../../../src/converters/vtracer";

type Converter = (
filePath: string,
fileType: string,
convertTo: string,
targetPath: string,
options?: unknown,
) => Promise<string>;

const converters: Record<string, Converter> = {
ffmpeg,
graphicsmagick,
imagemagick,
inkscape,
libjxl,
vips,
vtracer,
};

test("converts every saved PNG target for each converter", async () => {
const possibleTargets = getPossibleTargets("png");
const inputFileName = "sample.png";
const inputStem = path.parse(inputFileName).name;
const inputPath = path.resolve(`tests/integration/converters/inputs/${inputFileName}`);
const expectedFailuresByInput = (await Bun.file(
"tests/integration/converters/expected-failures.json",
).json()) as Record<string, string[]>;
expect(Object.keys(expectedFailuresByInput)).toContain(inputFileName);
const expectedFailures = expectedFailuresByInput[inputFileName] ?? [];
const expectedFailureSet = new Set(expectedFailures);
const failures: string[] = [];
const observedFailures: string[] = [];
let testedTargets = 0;
let successfulTargets = 0;
const originalConsoleLog = console.log;
const originalConsoleError = console.error;
console.log = () => {};
console.error = () => {};

try {
for (const [converterName, convert] of Object.entries(converters)) {
const outputDirectory = path.resolve(`tests/integration/converters/outputs/${converterName}`);
const declaredTargets = [...new Set(possibleTargets[converterName] ?? [])];

for (const target of declaredTargets) {
const targetKey = `${converterName}:${target}`;
const outputFile = `${inputStem}.${target}`;
testedTargets++;

const temporaryDirectory = await mkdtemp(path.join(tmpdir(), `convertx-${converterName}-`));
const actualPath = path.join(temporaryDirectory, outputFile);
const expectedPath = path.join(outputDirectory, outputFile);

try {
await convert(inputPath, "png", target, actualPath);
const actualSize = (await Bun.file(actualPath).stat())?.size ?? 0;
if (expectedFailureSet.has(targetKey)) {
if (actualSize > 0) {
failures.push(`${targetKey}: expected failure became successful`);
} else {
observedFailures.push(targetKey);
}
} else {
expect(actualSize).toBeGreaterThan(0);
expect((await Bun.file(expectedPath).stat())?.size).toBeGreaterThan(0);
successfulTargets++;
}
} catch (error) {
if (expectedFailureSet.has(targetKey)) {
observedFailures.push(targetKey);
} else {
failures.push(`${targetKey}: ${String(error)}`);
}
} finally {
await rm(temporaryDirectory, { recursive: true, force: true });
}
}
}
} finally {
console.log = originalConsoleLog;
console.error = originalConsoleError;
}

expect(testedTargets).toBe(558);
expect(successfulTargets).toBe(415);
expect(observedFailures.sort()).toEqual(expectedFailures);
expect(failures).toEqual([]);
}, 120000);
147 changes: 147 additions & 0 deletions tests/integration/converters/expected-failures.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
{
"sample.png": [
"ffmpeg:265",
"ffmpeg:266",
"ffmpeg:302",
"ffmpeg:3g2",
"ffmpeg:3gp",
"ffmpeg:aac",
"ffmpeg:ac3",
"ffmpeg:ac4",
"ffmpeg:adts",
"ffmpeg:adx",
"ffmpeg:afc",
"ffmpeg:aif",
"ffmpeg:aifc",
"ffmpeg:aiff",
"ffmpeg:al",
"ffmpeg:amr",
"ffmpeg:amv",
"ffmpeg:apm",
"ffmpeg:aptx",
"ffmpeg:aptxhd",
"ffmpeg:ass",
"ffmpeg:ast",
"ffmpeg:au",
"ffmpeg:aud",
"ffmpeg:avs",
"ffmpeg:avs2",
"ffmpeg:avs3",
"ffmpeg:bit",
"ffmpeg:c2",
"ffmpeg:caf",
"ffmpeg:cavs",
"ffmpeg:chk",
"ffmpeg:cvg",
"ffmpeg:dfpwm",
"ffmpeg:dnxhd",
"ffmpeg:dnxhr",
"ffmpeg:drc",
"ffmpeg:dts",
"ffmpeg:dv",
"ffmpeg:eac3",
"ffmpeg:ec3",
"ffmpeg:evc",
"ffmpeg:flac",
"ffmpeg:g722",
"ffmpeg:gsm",
"ffmpeg:gxf",
"ffmpeg:h261",
"ffmpeg:h263",
"ffmpeg:h265.mkv",
"ffmpeg:h265.mp4",
"ffmpeg:h266.mkv",
"ffmpeg:hevc",
"ffmpeg:ircam",
"ffmpeg:ivf",
"ffmpeg:js",
"ffmpeg:jss",
"ffmpeg:latm",
"ffmpeg:lbc",
"ffmpeg:loas",
"ffmpeg:lrc",
"ffmpeg:m2a",
"ffmpeg:m4a",
"ffmpeg:mka",
"ffmpeg:mlp",
"ffmpeg:mmf",
"ffmpeg:mp2",
"ffmpeg:mp3",
"ffmpeg:mpa",
"ffmpeg:msbc",
"ffmpeg:oga",
"ffmpeg:ogg",
"ffmpeg:oma",
"ffmpeg:opus",
"ffmpeg:pcm",
"ffmpeg:psp",
"ffmpeg:rco",
"ffmpeg:rcv",
"ffmpeg:rso",
"ffmpeg:sb",
"ffmpeg:sbc",
"ffmpeg:scc",
"ffmpeg:sf",
"ffmpeg:sox",
"ffmpeg:spdif",
"ffmpeg:spx",
"ffmpeg:srt",
"ffmpeg:ssa",
"ffmpeg:sub",
"ffmpeg:sup",
"ffmpeg:sw",
"ffmpeg:tco",
"ffmpeg:thd",
"ffmpeg:tta",
"ffmpeg:ttml",
"ffmpeg:tun",
"ffmpeg:ub",
"ffmpeg:ul",
"ffmpeg:uw",
"ffmpeg:vag",
"ffmpeg:vc1",
"ffmpeg:vc2",
"ffmpeg:voc",
"ffmpeg:vtt",
"ffmpeg:vvc",
"ffmpeg:w64",
"ffmpeg:wav",
"ffmpeg:webm",
"ffmpeg:wv",
"ffmpeg:xface",
"ffmpeg:xml",
"ffmpeg:y4m",
"graphicsmagick:8bim",
"graphicsmagick:8bimtext",
"graphicsmagick:8bimwtext",
"graphicsmagick:app1",
"graphicsmagick:app1jpeg",
"graphicsmagick:exif",
"graphicsmagick:icc",
"graphicsmagick:icm",
"graphicsmagick:iptc",
"graphicsmagick:iptctext",
"graphicsmagick:iptcwtext",
"graphicsmagick:m2v",
"graphicsmagick:mpeg",
"graphicsmagick:mpg",
"graphicsmagick:xmp",
"imagemagick:ashlar",
"imagemagick:clip",
"imagemagick:clipboard",
"imagemagick:jpt",
"imagemagick:mask",
"imagemagick:thumbnail",
"vips:avif",
"vips:dzi",
"vips:hdr.gz",
"vips:heic",
"vips:heif",
"vips:img.gz",
"vips:jpx",
"vips:nia",
"vips:nia.gz",
"vips:nii",
"vips:nii.gz"
]
}
6 changes: 6 additions & 0 deletions tests/integration/converters/ffmpeg.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { test } from "bun:test";
import { convert } from "../../../src/converters/ffmpeg";
import { runGoldenConversion } from "./golden";

test("converts the sample PNG to the expected JPEG bytes", () =>
runGoldenConversion("ffmpeg", convert, "sample.png", "jpg"));
34 changes: 34 additions & 0 deletions tests/integration/converters/golden.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { expect } from "bun:test";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";

type Converter = (
filePath: string,
fileType: string,
convertTo: string,
targetPath: string,
options?: unknown,
) => Promise<string>;

export async function runGoldenConversion(
converterName: string,
convert: Converter,
inputFileName: string,
outputExtension: string,
) {
const outputDirectory = await mkdtemp(path.join(tmpdir(), `convertx-${converterName}-`));
const inputStem = path.parse(inputFileName).name;
const outputPath = path.join(outputDirectory, `${inputStem}.${outputExtension}`);
const inputPath = path.resolve(`tests/integration/converters/inputs/${inputFileName}`);
const expectedPath = path.resolve(
`tests/integration/converters/outputs/${converterName}/${inputStem}.${outputExtension}`,
);

try {
await convert(inputPath, "png", outputExtension, outputPath);
expect(await Bun.file(outputPath).bytes()).toEqual(await Bun.file(expectedPath).bytes());
} finally {
await rm(outputDirectory, { recursive: true, force: true });
}
}
6 changes: 6 additions & 0 deletions tests/integration/converters/graphicsmagick.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { test } from "bun:test";
import { convert } from "../../../src/converters/graphicsmagick";
import { runGoldenConversion } from "./golden";

test("converts the sample PNG to the expected JPEG bytes", () =>
runGoldenConversion("graphicsmagick", convert, "sample.png", "jpeg"));
6 changes: 6 additions & 0 deletions tests/integration/converters/imagemagick.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { test } from "bun:test";
import { convert } from "../../../src/converters/imagemagick";
import { runGoldenConversion } from "./golden";

test("converts the sample PNG to the expected JPEG bytes", () =>
runGoldenConversion("imagemagick", convert, "sample.png", "jpg"));
6 changes: 6 additions & 0 deletions tests/integration/converters/inkscape.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { test } from "bun:test";
import { convert } from "../../../src/converters/inkscape";
import { runGoldenConversion } from "./golden";

test("converts the sample PNG to the expected SVG bytes", () =>
runGoldenConversion("inkscape", convert, "sample.png", "svg"));
Binary file added tests/integration/converters/inputs/sample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions tests/integration/converters/inputs/sample.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions tests/integration/converters/libjxl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { test } from "bun:test";
import { convert } from "../../../src/converters/libjxl";
import { runGoldenConversion } from "./golden";

test("converts the sample PNG to the expected JPEG XL bytes", () =>
runGoldenConversion("libjxl", convert, "sample.png", "jxl"));
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 2 additions & 0 deletions tests/integration/converters/outputs/ffmpeg/sample.ffmeta
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
;FFMETADATA1
encoder=Lavf63.1.102
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added tests/integration/converters/outputs/ffmpeg/sample.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file added tests/integration/converters/outputs/ffmpeg/sample.hdr
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
7 changes: 7 additions & 0 deletions tests/integration/converters/outputs/ffmpeg/sample.m3u8
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:0
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:0.040000,
sample0.ts
#EXT-X-ENDLIST
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
26 changes: 26 additions & 0 deletions tests/integration/converters/outputs/ffmpeg/sample.mpd
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="urn:mpeg:dash:schema:mpd:2011"
xmlns:xlink="http://www.w3.org/1999/xlink"
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
profiles="urn:mpeg:dash:profile:isoff-live:2011"
type="static"
mediaPresentationDuration="PT0.0S"
maxSegmentDuration="PT5.0S"
minBufferTime="PT0.0S">
<ProgramInformation>
</ProgramInformation>
<ServiceDescription id="0">
</ServiceDescription>
<Period id="0" start="PT0.0S">
<AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="25/1" maxWidth="16" maxHeight="16" par="1:1">
<Representation id="0" mimeType="video/mp4" codecs="avc1.f4000a" bandwidth="349600" width="16" height="16" sar="1:1">
<SegmentTemplate timescale="12800" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="1">
<SegmentTimeline>
<S t="0" d="512" />
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
</Period>
</MPD>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Loading