feat(test): Add comprehensive unit tests for custom material commands
This commit is contained in:
234
tests/domain/custom-material.command.test.ts
Normal file
234
tests/domain/custom-material.command.test.ts
Normal file
@@ -0,0 +1,234 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { createEditorStore } from "../../src/app/editor-store";
|
||||
import { createCreateBoxBrushCommand } from "../../src/commands/create-box-brush-command";
|
||||
import { createCreateCustomMaterialCommand } from "../../src/commands/create-custom-material-command";
|
||||
import { createSetCustomMaterialTextureCommand } from "../../src/commands/set-custom-material-texture-command";
|
||||
import { createUpdateCustomMaterialCommand } from "../../src/commands/update-custom-material-command";
|
||||
import {
|
||||
createProjectAssetStorageKey,
|
||||
type ImageAssetRecord
|
||||
} from "../../src/assets/project-assets";
|
||||
import {
|
||||
BOX_FACE_IDS,
|
||||
type WhiteboxFaceId
|
||||
} from "../../src/document/brushes";
|
||||
import { createCustomMaterialDef } from "../../src/materials/starter-material-library";
|
||||
|
||||
function createImageAsset(id: string): ImageAssetRecord {
|
||||
return {
|
||||
id,
|
||||
kind: "image",
|
||||
sourceName: `${id}.png`,
|
||||
mimeType: "image/png",
|
||||
storageKey: createProjectAssetStorageKey(id),
|
||||
byteLength: 128,
|
||||
metadata: {
|
||||
kind: "image",
|
||||
width: 4,
|
||||
height: 4,
|
||||
hasAlpha: false,
|
||||
warnings: []
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
describe("custom material commands", () => {
|
||||
it("creates a custom material and applies it to the selected face with undo/redo", () => {
|
||||
const store = createEditorStore();
|
||||
const material = createCustomMaterialDef({
|
||||
id: "material-custom-face",
|
||||
name: "Blue Test",
|
||||
albedoColorHex: "#3366ff"
|
||||
});
|
||||
|
||||
store.executeCommand(createCreateBoxBrushCommand());
|
||||
|
||||
const brush = Object.values(store.getState().document.brushes)[0];
|
||||
|
||||
store.executeCommand(
|
||||
createCreateCustomMaterialCommand({
|
||||
material,
|
||||
applyTo: {
|
||||
kind: "face",
|
||||
brushId: brush.id,
|
||||
faceId: "posZ"
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
expect(store.getState().document.materials[material.id]).toMatchObject({
|
||||
kind: "custom",
|
||||
name: "Blue Test",
|
||||
albedoColorHex: "#3366ff"
|
||||
});
|
||||
expect(
|
||||
store.getState().document.brushes[brush.id].faces.posZ.materialId
|
||||
).toBe(material.id);
|
||||
expect(store.getState().selection).toEqual({
|
||||
kind: "brushFace",
|
||||
brushId: brush.id,
|
||||
faceId: "posZ"
|
||||
});
|
||||
|
||||
expect(store.undo()).toBe(true);
|
||||
expect(store.getState().document.materials[material.id]).toBeUndefined();
|
||||
expect(
|
||||
store.getState().document.brushes[brush.id].faces.posZ.materialId
|
||||
).toBeNull();
|
||||
|
||||
expect(store.redo()).toBe(true);
|
||||
expect(store.getState().document.materials[material.id]).toBeDefined();
|
||||
expect(
|
||||
store.getState().document.brushes[brush.id].faces.posZ.materialId
|
||||
).toBe(material.id);
|
||||
});
|
||||
|
||||
it("creates a custom material and applies it to all selected solid faces", () => {
|
||||
const store = createEditorStore();
|
||||
const material = createCustomMaterialDef({
|
||||
id: "material-custom-solid",
|
||||
name: "Solid Test"
|
||||
});
|
||||
|
||||
store.executeCommand(createCreateBoxBrushCommand());
|
||||
|
||||
const brush = Object.values(store.getState().document.brushes)[0];
|
||||
|
||||
store.executeCommand(
|
||||
createCreateCustomMaterialCommand({
|
||||
material,
|
||||
applyTo: {
|
||||
kind: "brush",
|
||||
brushId: brush.id
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
for (const faceId of BOX_FACE_IDS) {
|
||||
expect(
|
||||
store.getState().document.brushes[brush.id].faces[
|
||||
faceId as WhiteboxFaceId
|
||||
].materialId
|
||||
).toBe(material.id);
|
||||
}
|
||||
|
||||
expect(store.undo()).toBe(true);
|
||||
|
||||
for (const faceId of BOX_FACE_IDS) {
|
||||
expect(
|
||||
store.getState().document.brushes[brush.id].faces[
|
||||
faceId as WhiteboxFaceId
|
||||
].materialId
|
||||
).toBeNull();
|
||||
}
|
||||
});
|
||||
|
||||
it("updates custom material scalar settings and restores them on undo", () => {
|
||||
const store = createEditorStore();
|
||||
const material = createCustomMaterialDef({
|
||||
id: "material-custom-update",
|
||||
roughness: 0.8,
|
||||
metallic: 0
|
||||
});
|
||||
|
||||
store.executeCommand(
|
||||
createCreateCustomMaterialCommand({
|
||||
material
|
||||
})
|
||||
);
|
||||
store.executeCommand(
|
||||
createUpdateCustomMaterialCommand({
|
||||
materialId: material.id,
|
||||
update: {
|
||||
opacity: 0.45,
|
||||
roughness: 0.2,
|
||||
metallic: 0.9,
|
||||
normalStrength: 0.35
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
expect(store.getState().document.materials[material.id]).toMatchObject({
|
||||
opacity: 0.45,
|
||||
roughness: 0.2,
|
||||
metallic: 0.9,
|
||||
normalStrength: 0.35
|
||||
});
|
||||
|
||||
expect(store.undo()).toBe(true);
|
||||
expect(store.getState().document.materials[material.id]).toMatchObject({
|
||||
opacity: 1,
|
||||
roughness: 0.8,
|
||||
metallic: 0,
|
||||
normalStrength: 1
|
||||
});
|
||||
});
|
||||
|
||||
it("imports and assigns image texture assets to custom material slots", () => {
|
||||
const store = createEditorStore();
|
||||
const material = createCustomMaterialDef({
|
||||
id: "material-custom-texture"
|
||||
});
|
||||
const imageAsset = createImageAsset("asset-material-albedo");
|
||||
|
||||
store.executeCommand(
|
||||
createCreateCustomMaterialCommand({
|
||||
material
|
||||
})
|
||||
);
|
||||
store.executeCommand(
|
||||
createSetCustomMaterialTextureCommand({
|
||||
materialId: material.id,
|
||||
slot: "albedo",
|
||||
assetId: imageAsset.id,
|
||||
asset: imageAsset
|
||||
})
|
||||
);
|
||||
|
||||
const assignedMaterial = store.getState().document.materials[material.id];
|
||||
|
||||
expect(store.getState().document.assets[imageAsset.id]).toEqual(imageAsset);
|
||||
expect(assignedMaterial).toMatchObject({
|
||||
kind: "custom",
|
||||
textures: {
|
||||
albedo: {
|
||||
assetId: imageAsset.id
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(store.undo()).toBe(true);
|
||||
expect(store.getState().document.assets[imageAsset.id]).toBeUndefined();
|
||||
expect(store.getState().document.materials[material.id]).toMatchObject({
|
||||
textures: {
|
||||
albedo: null
|
||||
}
|
||||
});
|
||||
|
||||
expect(store.redo()).toBe(true);
|
||||
expect(store.getState().document.assets[imageAsset.id]).toEqual(imageAsset);
|
||||
expect(store.getState().document.materials[material.id]).toMatchObject({
|
||||
textures: {
|
||||
albedo: {
|
||||
assetId: imageAsset.id
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps starter materials read-only", () => {
|
||||
const store = createEditorStore();
|
||||
|
||||
expect(() =>
|
||||
store.executeCommand(
|
||||
createUpdateCustomMaterialCommand({
|
||||
materialId: "starter-amber-grid",
|
||||
update: {
|
||||
roughness: 0.4
|
||||
}
|
||||
})
|
||||
)
|
||||
).toThrow("Starter materials are read-only.");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user