Skip to content
Closed
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions src/__tests__/unit/services/enb.service.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import mockFs from "mock-fs";
import {
createStubInstance,
expect,
StubbedInstanceWithSinonAccessor,
} from "@loopback/testlab";
import { EnbService } from "@/main/services/enb.service";
import { ConfigService, userPreferences } from "@/main/services/config.service";
import { USER_PREFERENCE_KEYS } from "@/shared/enums/userPreferenceKeys";
import { InstructionService } from "@/main/services/instruction.service";
import fs from "fs";
import { AdditionalInstructions } from "@/additional-instructions";
import { DirectoryItems } from "mock-fs/lib/filesystem";

describe("ENB Service", () => {
let enbService: EnbService;
let configService: StubbedInstanceWithSinonAccessor<ConfigService>;
let instructionService: StubbedInstanceWithSinonAccessor<InstructionService>;
const modDir = "/mock/mods";
const enbPresetDir = `${modDir}/launcher/ENB Presets`;
const baseFS = {
[`${modDir}/Stock Game`]: { "exampleB.json": "[]" },
[`${modDir}/launcher`]: {
"namesENB.json": JSON.stringify([{ real: "A", friendly: "Letter A" }]),
},
[`${enbPresetDir}/A`]: { "example.json": "{}" },
[`${enbPresetDir}/B`]: { "exampleB.json": "{}" },
} as DirectoryItems;
beforeEach(() => {
configService = createStubInstance(ConfigService);
instructionService = createStubInstance(InstructionService);
enbService = new EnbService(configService, instructionService);
mockFs(baseFS);
});

afterEach(() => {
mockFs.restore();
});

it("should override unmapped with mapped ENB presets", async () => {
// setting the userPref in beforeEach doesn't work for some reason -> undefined
userPreferences.set(USER_PREFERENCE_KEYS.MOD_DIRECTORY, modDir);
expect(await enbService.getENBPresets()).to.eql([
{
friendly: "Letter A",
real: "A",
},
{
friendly: "B",
real: "B",
},
{
friendly: "No Shaders",
real: "noEnb",
},
]);
});

it("should backup the entire ENB presets directory", async () => {
userPreferences.set(USER_PREFERENCE_KEYS.MOD_DIRECTORY, modDir);
const enbBackupDir = `${modDir}/launcher/_backups`;
configService.stubs.backupDirectory.returns(enbBackupDir);
const orig = fs.readdirSync(enbPresetDir);

await enbService.backupOriginalENBs();
const backup = fs.readdirSync(`${enbBackupDir}/ENB Presets`);
expect(orig).eql(backup);
});

it("should reset to the correct ENB preset", async () => {
const instructions = [
{
action: "disable-plugin",
type: "enb",
target: ["21:9"],
plugin: "example.esp",
},
{
action: "disable-ultra-widescreen",
type: "resolution-ratio",
},
] as AdditionalInstructions;
userPreferences.set(USER_PREFERENCE_KEYS.MOD_DIRECTORY, modDir);
userPreferences.set(USER_PREFERENCE_KEYS.ENB_PROFILE, "A");
userPreferences.set(USER_PREFERENCE_KEYS.PREVIOUS_ENB_PROFILE, "B");
configService.stubs.getPreference.returns("A");
configService.stubs.skyrimDirectory.returns(`${modDir}/Stock Game`);
instructionService.stubs.getInstructions.returns(instructions);
instructionService.stubs.execute.resolves(true);
await enbService.resetCurrentEnb(true);
expect(
instructionService.stubs.execute.calledOnceWithExactly([instructions[0]])
);
expect(
configService.stubs.setPreference.calledWithExactly(
USER_PREFERENCE_KEYS.PREVIOUS_ENB_PROFILE,
"A"
)
).true();
expect(
configService.stubs.setPreference.calledWithExactly(
USER_PREFERENCE_KEYS.ENB_PROFILE,
"A"
)
).true();
});
it("should restore ENB presets and overwrite existing ones", async () => {
const enbBackupDir = `${modDir}/launcher/_backups`;
mockFs({
...baseFS,
[`${enbBackupDir}/ENB Presets/Z`]: { "Z.json": "{}" },
[`${enbBackupDir}/ENB Presets/A`]: {
"example.json": JSON.stringify({ different: true }),
},
});
userPreferences.set(USER_PREFERENCE_KEYS.MOD_DIRECTORY, modDir);
userPreferences.set(USER_PREFERENCE_KEYS.ENB_PROFILE, "A");
configService.stubs.backupDirectory.returns(enbBackupDir);
configService.stubs.skyrimDirectory.returns(`${modDir}/Stock Game`);
// right now Z should not exist within the current presets
expect(fs.readdirSync(enbPresetDir).includes("Z")).false();
const backup = fs.readdirSync(`${enbBackupDir}/ENB Presets`);
// verify A/example.json is overwritten
const presetAPrior = fs
.readFileSync(`${enbPresetDir}/A/example.json`)
.toJSON();
await enbService.restoreENBPresets();
const presetACurrent = fs
.readFileSync(`${enbPresetDir}/A/example.json`)
.toJSON();
expect(presetAPrior).not.eql(presetACurrent);
// current Presets should now contain the previously missing Z
const current = fs.readdirSync(enbPresetDir);
expect(backup.every((value) => current.includes(value))).true();
});
});
1 change: 1 addition & 0 deletions src/__tests__/unit/services/error.service.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO
225 changes: 225 additions & 0 deletions src/__tests__/unit/services/instruction.service.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import mockFs from "mock-fs";
import { InstructionService } from "@/main/services/instruction.service";
import { AdditionalInstruction } from "@/additional-instructions";
import { ConfigService } from "@/main/services/config.service";
import { ProfileService } from "@/main/services/profile.service";
import { WabbajackService } from "@/main/services/wabbajack.service";
import modpackAdditionalInstructions from "@/additional-instructions.json";
import {
expect,
StubbedInstanceWithSinonAccessor,
createStubInstance,
} from "@loopback/testlab";
import * as os from "os";
import fs from "fs";

describe("Instruction service", () => {
let instructionService: InstructionService;
let configService: StubbedInstanceWithSinonAccessor<ConfigService>;
let wabbajackService: StubbedInstanceWithSinonAccessor<WabbajackService>;
let profileService: StubbedInstanceWithSinonAccessor<ProfileService>;

beforeEach(() => {
configService = createStubInstance(ConfigService);
wabbajackService = createStubInstance(WabbajackService);
profileService = createStubInstance(ProfileService);
instructionService = new InstructionService(
configService,
profileService,
wabbajackService
);
});

afterEach(() => {
mockFs.restore();
});

it("should match json imported instructions", () => {
// not mocked because mock-require doesn't seem to work here for some reason
expect(instructionService.getInstructions()).to.eql(
modpackAdditionalInstructions
);
});

it("should ignore or compute instruction based on modpack version", async () => {
wabbajackService.stubs.getModpackVersion.resolves("1.1.0");
const instruction = {
action: "disable-ultra-widescreen",
version: "1.0.0",
} as AdditionalInstruction;
// instruction is skipped if version mismatch -> undefined
expect(await instructionService.execute([instruction])).undefined();

// however having the same modpack version will result in a different result
wabbajackService.stubs.getModpackVersion.resolves("1.0.0");
expect(await instructionService.execute([instruction])).not.undefined();
});

describe("modlist.txt", () => {
const mockProfilesDir = "/mock/profiles";
const mockModlistArr = ["+Mod1E", "-Mod2D", "+Mod3E", "-Mod4D"];
const mockModlistStr = mockModlistArr.join(os.EOL);
const mockModlistFile = `${mockProfilesDir}/profileA/modlist.txt`;
beforeEach(async () => {
// this is a heavily simplified version of the FS compared to the real thing
// it does not represent how it actually looks whatsoever, only facilitates this test
mockFs({
[mockModlistFile]: mockModlistStr,
});
profileService.stubs.getPhysicalProfiles.resolves(
await fs.promises.readdir(mockProfilesDir, { withFileTypes: true })
);
profileService.stubs.profileDirectory.returns(mockProfilesDir);
});

it("Should not change anything if instruction matches state", async () => {
const instructionEnabledAlready = {
action: "enable-mod",
type: "resolution-ratio",
target: "21:9",
mod: "Mod1E",
} as AdditionalInstruction;
await instructionService.execute([instructionEnabledAlready], "21:9");
const currentModlistContent = fs.readFileSync(mockModlistFile).toString();
expect(currentModlistContent).to.eql(mockModlistStr);
});
it("Should enable disabled mod", async () => {
const instructionEnableDisabled = {
action: "enable-mod",
type: "resolution-ratio",
target: "21:9",
mod: "Mod2D",
} as AdditionalInstruction;
await instructionService.execute([instructionEnableDisabled], "21:9");
const currentModlistContent = fs.readFileSync(mockModlistFile).toString();
expect(currentModlistContent.includes("+Mod2D")).true();
});
it("Should disable enabled mod", async () => {
const instructionDisableEnabled = {
action: "disable-mod",
type: "resolution-ratio",
target: "21:9",
mod: "Mod3E",
} as AdditionalInstruction;
await instructionService.execute([instructionDisableEnabled], "21:9");
const currentModlistContent = fs.readFileSync(mockModlistFile).toString();
expect(currentModlistContent.includes("-Mod3E")).true();
});
it("Should disable on enable instruction if target mismatch", async () => {
const instructionInverseEnableTargetMismatch = {
action: "enable-mod",
type: "resolution-ratio",
target: "not matching",
mod: "Mod1E",
} as AdditionalInstruction;
await instructionService.execute(
[instructionInverseEnableTargetMismatch],
"21:9"
);
const currentModlistContent = fs.readFileSync(mockModlistFile).toString();
expect(currentModlistContent.includes("-Mod1E")).true();
});
it("should enable on disable instruction if target mismatch", async () => {
const instructionInverseDisableTargetMismatch = {
action: "disable-mod",
type: "resolution-ratio",
target: "missing target in call",
mod: "Mod1E",
} as AdditionalInstruction;
await instructionService.execute([
instructionInverseDisableTargetMismatch,
]);
const currentModlistContent = fs.readFileSync(mockModlistFile).toString();
expect(currentModlistContent.includes("+Mod1E")).true();
});
});

describe("plugins.txt", () => {
const mockProfilesDir = "/mock/profiles";
const mockPluginList = [
"*Plugin1E.esp",
"Plugin2D.esp",
"*Plugin3E.esp",
"Plugin4D.esp",
];
const mockPlugins = mockPluginList.join(os.EOL);
const mockPluginsFile = `${mockProfilesDir}/profileA/plugins.txt`;
beforeEach(async () => {
mockFs({
[mockPluginsFile]: mockPlugins,
});
profileService.stubs.getPhysicalProfiles.resolves(
await fs.promises.readdir(mockProfilesDir, { withFileTypes: true })
);
profileService.stubs.profileDirectory.returns(mockProfilesDir);
});
it("should not change anything if instruction matches state", async () => {
const instructionEnabledAlready = {
action: "enable-plugin",
type: "resolution-ratio",
target: ["21:9", "32:9"],
plugin: "Plugin1E.esp",
} as AdditionalInstruction;
await instructionService.execute([instructionEnabledAlready], "21:9");
const currentPluginsContent = fs.readFileSync(mockPluginsFile).toString();
expect(currentPluginsContent).to.eql(mockPlugins);
});
it("should enable disabled", async () => {
const instructionEnableDisabled = {
action: "enable-plugin",
type: "resolution-ratio",
target: ["21:9", "32:9"],
plugin: "Plugin2D.esp",
} as AdditionalInstruction;
await instructionService.execute([instructionEnableDisabled], "21:9");
const currentPluginsContent = fs.readFileSync(mockPluginsFile).toString();
expect(
currentPluginsContent.split(os.EOL).includes("*Plugin2D.esp")
).true();
});
it("should disable enabled", async () => {
const instructionDisableEnabled = {
action: "disable-plugin",
type: "enb",
target: "21:9",
plugin: "Plugin3E.esp",
} as AdditionalInstruction;
await instructionService.execute([instructionDisableEnabled], "21:9");
const currentPluginsContent = fs.readFileSync(mockPluginsFile).toString();
expect(
currentPluginsContent.split(os.EOL).includes("Plugin3E.esp")
).true();
});
it("should disable on enable instruction if target mismatch", async () => {
const instructionInverseEnableTargetMismatch = {
action: "enable-plugin",
type: "resolution-ratio",
target: ["21:9", "32:9"],
plugin: "Plugin1E.esp",
} as AdditionalInstruction;
await instructionService.execute(
[instructionInverseEnableTargetMismatch],
"enb"
);
const currentPluginsContent = fs.readFileSync(mockPluginsFile).toString();
expect(
currentPluginsContent.split(os.EOL).includes("Plugin1E.esp")
).true();
});
it("should enable on disable instruction if target mismatch", async () => {
const instructionInverseDisableTargetMismatch = {
action: "disable-plugin",
type: "enb",
target: "noEnb",
plugin: "Plugin1E.esp",
} as AdditionalInstruction;
await instructionService.execute([
instructionInverseDisableTargetMismatch,
]);
const currentPluginsContent = fs.readFileSync(mockPluginsFile).toString();
expect(
currentPluginsContent.split(os.EOL).includes("*Plugin1E.esp")
).true();
});
});
});
1 change: 1 addition & 0 deletions src/__tests__/unit/services/launcher.service.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO
Loading