Skip to content

Commit

Permalink
project: Initial tests implementation.
Browse files Browse the repository at this point in the history
• maker-appimage: Implement tests validating structure and execution
  of maker.
• project: Prepare metadata for monorepo test execution.
  • Loading branch information
SpacingBat3 committed Nov 17, 2024
1 parent 7765763 commit 235d3e5
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 1 deletion.
3 changes: 2 additions & 1 deletion makers/appimage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"description": "An AppImage maker implementation for the Electron Forge.",
"main": "dist/main.js",
"scripts": {
"prepack": "tsc"
"prepack": "tsc",
"test": "tsc -b tsconfig.test.json && NODE_NO_WARNINGS=1 node --test --experimental-strip-types"
},
"repository": {
"type": "git",
Expand Down
134 changes: 134 additions & 0 deletions makers/appimage/test/make.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Test current maker from perspective of 'make' process.
* ---
* TODO: Consider full mocking of test env for runtime-independent
* testing, including:
*
* - File system access.
* - "mksquashfs" binary.
*
* Maybe do it as some kind of Forge-standard mocking env for Node's
* testing? Or create a shared local module with such implementation?
*
* Right now, we're skipping tests if local env is not satisfied.
*/

import {
access,
constants,
mkdtemp,
open,
rm,
writeFile
} from "node:fs/promises";

import { describe, it } from "node:test"
import { resolve } from "node:path";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { tmpdir } from "node:os";


import assert from "node:assert";
import MakerAppImage from "@reforged/maker-appimage";

import type { ForgeArch } from "@reforged/maker-appimage";

/** Maker to test. */
const maker = new MakerAppImage();
await maker.prepareConfig(process.arch);

// Mock app metadata
const packageJSON = {
name:"mock-app",
productName: "Mock Electron App",
version:"0.0.0-mock"
}
const mockAppPath = mkdtemp(resolve(tmpdir(),`${packageJSON.name}-src-`))
// Initialize mock application data
.then(path => {
// Mock executable.
writeFile(resolve(path,packageJSON.name),[
"#!/usr/bin/sh -e",
"echo 'Hello world!';"
].join("\n"),{ mode:0o755 })
return path;
});
const mockMkPath = mkdtemp(resolve(tmpdir(),`${packageJSON.name}-make-`));
const forgeConfig = {
makers:[maker],
plugins:[],
publishers:[],
packagerConfig:{},
rebuildConfig:{},
get pluginInterface():never {
throw new Error("Unsupported mock property access: 'pluginInterface'.");
}
}

/** Cleanup hook after Electron mock make process. */
async function cleanup() {
rm(await mockAppPath,{recursive:true});
rm(await mockMkPath,{recursive:true});
}

//
let AResolve: (arg0: string) => any, AReject: (reason?: unknown) => void;
const AppImageDir:Promise<string> = new Promise(
(resolve,reject) => { AResolve = resolve, AReject = reject }
);


describe("MakerAppimage is working correctly", {
skip: maker.isSupportedOnCurrentPlatform() ?
maker.externalBinariesExist() ?
false :
`One or more binaries are missing: ${maker.requiredExternalBinaries.join(', ')}` :
`Unsupported platform: ${process.platform}-${process.arch}`
}, () => {
it("should create valid AppImage binary", async() => {
maker.make({
packageJSON,
forgeConfig,
appName: packageJSON.productName,
dir: await mockAppPath,
makeDir: await mockMkPath,
targetArch: process.arch as ForgeArch,
targetPlatform: process.platform
}).then(dir => AResolve(dir[0]),reason => AReject(reason));

await assert.doesNotReject(AppImageDir);

assert.strictEqual(
await AppImageDir,
resolve(
await mockMkPath,
maker.name,
process.arch,
`${packageJSON.productName}-${packageJSON.version}-${process.arch}.AppImage`
)
)
});

it("should resulting AppImage be an ELF file", async() => {
const fd = await open(await AppImageDir);
const buff = new Uint8Array(8);
await fd.read(buff,0,8);
assert.strictEqual(
[...buff].map(v=>v.toString(16)).join(' '),
// ELF magic HEX
"7f 45 4c 46 2 1 1 0"
)
fd.close();
})

it("should AppImage be runnable and working fine", async ctx => {
const exec = promisify(execFile);
// Skip this test for non-exec tmpdir.
if(await access(await AppImageDir,constants.X_OK).then(_=>false,_=>true))
return ctx.skip("Non-executable tmpdir.");
const cp = exec(await AppImageDir)
await assert.doesNotReject(cp);
assert.strictEqual((await cp).stdout,"Hello world!\n")
})
}).then(cleanup);
36 changes: 36 additions & 0 deletions makers/appimage/test/struct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Test current maker from perspective of its structure, aiding what's
* currently tested by TypeScript and precising the expectancies in APIs
* even more than it is currently.
*/

import { describe, it } from "node:test";

import assert from "node:assert";
import MakerAppImage from "@reforged/maker-appimage";

describe("MakerAppImage is valid object", () => {
const maker = new MakerAppImage();

it("extends MakerBase object directly", async() => {
const { MakerBase } = await import("@electron-forge/maker-base");
assert.strictEqual(Object.getPrototypeOf(MakerAppImage),MakerBase);
})

it("has name of the distributable format, AppImage", () => {
assert.strictEqual(maker.name,"AppImage");
})

it("supports being built on Linux only by the default", () => {
assert.deepStrictEqual(maker.defaultPlatforms,["linux"])
})

it("has a 'make' method", async ctx => {
await ctx.test("should be an asynchronous function", () => {
assert.strictEqual(maker.make.constructor, (async()=>{}).constructor);
})
await ctx.test("should be given at least one argument", () => {
assert.strictEqual(maker.make.length, 1);
})
})
})
12 changes: 12 additions & 0 deletions makers/appimage/tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"tsBuildInfoFile": "../../cache/tsc/maker-appimage.json",
"rootDir": "./test",
"noEmit": true
},
"files": [
"test/make.ts",
"test/struct.ts"
]
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"description": "A set of Electron Forge tools, makers and publishers (project package).",
"scripts": {
"build": "tsc -b",
"test": "tsc -b tsconfig.test.json && NODE_NO_WARNINGS=1 node --test --experimental-strip-types",
"docs": "typedoc"
},
"repository": {
Expand Down
11 changes: 11 additions & 0 deletions tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"tsBuildInfoFile": "cache/tsc/base.json",
"noEmit": true
},
"references": [
{"path": "makers/appimage/tsconfig.test.json"},
],
"files": []
}

0 comments on commit 235d3e5

Please sign in to comment.