This commit is contained in:
parent
34523e78b1
commit
5c86fd2ceb
19 changed files with 724 additions and 519 deletions
78
tests/data-validation.test.mjs
Normal file
78
tests/data-validation.test.mjs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import { readFile } from "node:fs/promises";
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
validateDiablo4Affixes,
|
||||
validateEndemicData,
|
||||
validateMonsterData,
|
||||
validateSiteContent,
|
||||
validateTranslationKeys
|
||||
} from "./helpers/data-validation.mjs";
|
||||
|
||||
test("site content json is complete", async () => {
|
||||
const site = JSON.parse(await readFile("website/public/data/site.json", "utf8"));
|
||||
validateSiteContent(site);
|
||||
});
|
||||
|
||||
test("mhwilds data and assets are available", async () => {
|
||||
const site = JSON.parse(await readFile("website/public/data/site.json", "utf8"));
|
||||
const games = JSON.parse(await readFile("website/public/data/games.json", "utf8"));
|
||||
const monsters = JSON.parse(await readFile("website/public/data/mhwilds/monsters.json", "utf8"));
|
||||
const endemicLife = JSON.parse(await readFile("website/public/data/mhwilds/endemic_life.json", "utf8"));
|
||||
const translations = JSON.parse(await readFile("website/public/data/mhwilds/i18n/fr.json", "utf8"));
|
||||
const enTranslations = JSON.parse(await readFile("website/public/data/mhwilds/i18n/en.json", "utf8"));
|
||||
const listingSource = await readFile("website/src/features/games/mhwilds/MhwildsListing.jsx", "utf8");
|
||||
|
||||
assert.equal(site.brand.name, "Sokko G");
|
||||
assert.equal(site.home.hero.title, "Sokko G");
|
||||
assert.equal(site.gamesPage.title, "Jeux disponibles");
|
||||
assert.match(site.toolboxes.summary, /espaces modulaires/);
|
||||
assert.ok(site.toolboxes.storageHelp.items.length >= 3);
|
||||
assert.equal(games.games[0].id, "mhwilds");
|
||||
assert.equal(games.games[0].eyebrow, "Guide de jeu");
|
||||
assert.match(games.games[0].images.cardCover, /games\/mhwilds\/card-cover\.png/);
|
||||
assert.match(games.games[0].images.heroBg, /games\/mhwilds\/hero-bg\.png/);
|
||||
assert.ok(monsters.monsters.length > 0);
|
||||
assert.deepEqual(monsters.elements, ["fire", "water", "thunder", "ice", "dragon"]);
|
||||
validateMonsterData(monsters.monsters);
|
||||
assert.ok(Array.isArray(endemicLife.locations), "endemicLife.locations must be an array");
|
||||
assert.ok(endemicLife.locations.length > 0, "endemicLife.locations must not be empty");
|
||||
assert.ok(endemicLife.endemicLife.length > 0);
|
||||
validateEndemicData(endemicLife);
|
||||
validateTranslationKeys(translations, "fr");
|
||||
validateTranslationKeys(enTranslations, "en");
|
||||
assert.equal(translations.monsters, "monstres");
|
||||
assert.equal(translations.dark_hornet, "frelon clair-obscur");
|
||||
assert.equal(translations["dark hornet"], undefined);
|
||||
assert.match(listingSource, /function MhwildsListing/);
|
||||
const monsterCardSource = await readFile("website/src/features/games/mhwilds/cards/MonsterCard.jsx", "utf8");
|
||||
assert.match(monsterCardSource, /monster-card/);
|
||||
assert.match(monsterCardSource, /monster\.hitzones/);
|
||||
await readFile("website/public/static/img/mhwilds/chatacabra.png");
|
||||
await readFile("website/public/static/img/games/mhwilds/card-cover.png");
|
||||
await readFile("website/public/static/img/games/mhwilds/hero-bg.png");
|
||||
});
|
||||
|
||||
test("diablo4 data and route are available", async () => {
|
||||
const games = JSON.parse(await readFile("website/public/data/games.json", "utf8"));
|
||||
const affixes = JSON.parse(await readFile("website/public/data/diablo4/affixes_types.json", "utf8"));
|
||||
const routeSource = await readFile("website/src/features/games/GameRoute.jsx", "utf8");
|
||||
const pageSource = await readFile("website/src/features/games/diablo4/Diablo4Page.jsx", "utf8");
|
||||
const listingSource = await readFile("website/src/features/games/diablo4/Diablo4Listing.jsx", "utf8");
|
||||
const diablo4Game = games.games.find((game) => game.id === "diablo4");
|
||||
|
||||
assert.ok(diablo4Game, "games.json must declare diablo4");
|
||||
assert.match(diablo4Game.images.cardCover, /games\/diablo4\/card-cover\.png/);
|
||||
assert.match(diablo4Game.images.heroBg, /games\/diablo4\/hero-bg\.png/);
|
||||
validateDiablo4Affixes(affixes);
|
||||
assert.match(routeSource, /game\.id === "diablo4"/);
|
||||
assert.match(pageSource, /category === "affixes"/);
|
||||
assert.match(listingSource, /diablo4\.filterOptions\.affixes/);
|
||||
await readFile("website/public/static/img/games/diablo4/card-cover.png");
|
||||
await readFile("website/public/static/img/games/diablo4/hero-bg.png");
|
||||
await readFile("website/public/static/img/diablo4/axe.svg");
|
||||
await readFile("website/public/static/img/diablo4/cube.svg");
|
||||
await readFile("website/public/static/img/diablo4/fire.svg");
|
||||
await readFile("website/public/static/img/diablo4/shield.svg");
|
||||
await readFile("website/public/static/img/diablo4/wing.svg");
|
||||
});
|
||||
178
tests/helpers/data-validation.mjs
Normal file
178
tests/helpers/data-validation.mjs
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
import assert from "node:assert/strict";
|
||||
|
||||
const SNAKE_CASE_RE = /^[a-z0-9]+(?:_[a-z0-9]+)*$/;
|
||||
const PHYSICAL_HITZONE_KEYS = ["cut", "blunt", "ammo"];
|
||||
const ELEMENTAL_HITZONE_KEYS = ["fire", "water", "thunder", "ice", "dragon"];
|
||||
|
||||
function valueAt(object, path) {
|
||||
return path.split(".").reduce((value, key) => value?.[key], object);
|
||||
}
|
||||
|
||||
function assertNonEmptyString(object, path) {
|
||||
const value = valueAt(object, path);
|
||||
assert.equal(typeof value, "string", `${path} must be a string`);
|
||||
assert.ok(value.trim(), `${path} must not be empty`);
|
||||
}
|
||||
|
||||
export function assertStringArray(object, path, minLength = 1) {
|
||||
const value = valueAt(object, path);
|
||||
assert.ok(Array.isArray(value), `${path} must be an array`);
|
||||
assert.ok(value.length >= minLength, `${path} must contain at least ${minLength} item(s)`);
|
||||
value.forEach((item, index) => {
|
||||
assert.equal(typeof item, "string", `${path}[${index}] must be a string`);
|
||||
assert.ok(item.trim(), `${path}[${index}] must not be empty`);
|
||||
});
|
||||
}
|
||||
|
||||
export function validateSiteContent(site) {
|
||||
[
|
||||
"brand.name",
|
||||
"brand.homeAriaLabel",
|
||||
"navigation.home",
|
||||
"navigation.toolboxes",
|
||||
"navigation.games",
|
||||
"navigation.mobileGames",
|
||||
"sidebar.badge",
|
||||
"sidebar.note",
|
||||
"topbar.dashboard",
|
||||
"topbar.toolbox",
|
||||
"topbar.games",
|
||||
"gamesPage.eyebrow",
|
||||
"gamesPage.title",
|
||||
"gamesPage.description",
|
||||
"gamesPage.emptyTitle",
|
||||
"gamesPage.emptyText",
|
||||
"home.hero.eyebrow",
|
||||
"home.hero.title",
|
||||
"home.hero.description",
|
||||
"home.hero.primaryAction",
|
||||
"home.hero.secondaryAction",
|
||||
"home.stats.toolboxSingular",
|
||||
"home.stats.toolboxPlural",
|
||||
"home.stats.toolSingular",
|
||||
"home.stats.toolPlural",
|
||||
"home.origin.eyebrow",
|
||||
"home.origin.title",
|
||||
"home.origin.visualAlt",
|
||||
"home.origin.dialogueAriaLabel",
|
||||
"home.origin.caption",
|
||||
"home.legal.copyright",
|
||||
"home.legal.disclaimer",
|
||||
"toolboxes.eyebrow",
|
||||
"toolboxes.title",
|
||||
"toolboxes.newButton",
|
||||
"toolboxes.importAll",
|
||||
"toolboxes.exportAll",
|
||||
"toolboxes.importOne",
|
||||
"toolboxes.summary",
|
||||
"toolboxes.storageHelp.title",
|
||||
"toolboxes.storageHelp.text",
|
||||
"toolboxes.emptyTitle",
|
||||
"toolboxes.emptyText"
|
||||
].forEach((path) => assertNonEmptyString(site, path));
|
||||
|
||||
assertStringArray(site, "toolboxes.storageHelp.items", 3);
|
||||
|
||||
const lines = valueAt(site, "home.origin.lines");
|
||||
assert.ok(Array.isArray(lines), "home.origin.lines must be an array");
|
||||
assert.ok(lines.length >= 2, "home.origin.lines must contain at least 2 items");
|
||||
lines.forEach((line, index) => {
|
||||
assert.ok(["user", "app"].includes(line?.speaker), `home.origin.lines[${index}].speaker must be "user" or "app"`);
|
||||
assert.equal(typeof line?.text, "string", `home.origin.lines[${index}].text must be a string`);
|
||||
assert.ok(line.text.trim(), `home.origin.lines[${index}].text must not be empty`);
|
||||
});
|
||||
}
|
||||
|
||||
function assertSnakeCase(value, path) {
|
||||
assert.equal(typeof value, "string", `${path} must be a string`);
|
||||
assert.match(value, SNAKE_CASE_RE, `${path} must be snake_case`);
|
||||
}
|
||||
|
||||
function validateConditionValues(items, path) {
|
||||
assert.ok(Array.isArray(items), `${path} must be an array`);
|
||||
items.forEach((item, index) => {
|
||||
assertSnakeCase(item?.condition, `${path}[${index}].condition`);
|
||||
assert.ok(Array.isArray(item?.values), `${path}[${index}].values must be an array`);
|
||||
item.values.forEach((value, valueIndex) => assertSnakeCase(value, `${path}[${index}].values[${valueIndex}]`));
|
||||
});
|
||||
}
|
||||
|
||||
export function validateMonsterData(monsters) {
|
||||
assert.ok(Array.isArray(monsters), "monsters must be an array");
|
||||
const ids = new Set();
|
||||
const names = new Set();
|
||||
|
||||
monsters.forEach((monster, monsterIndex) => {
|
||||
const path = `monsters[${monsterIndex}]`;
|
||||
assert.equal(typeof monster.id, "number", `${path}.id must be a number`);
|
||||
assert.ok(Number.isInteger(monster.id), `${path}.id must be an integer`);
|
||||
assert.ok(!ids.has(monster.id), `${path}.id must be unique`);
|
||||
ids.add(monster.id);
|
||||
|
||||
assertSnakeCase(monster.name, `${path}.name`);
|
||||
assert.ok(!names.has(monster.name), `${path}.name must be unique`);
|
||||
names.add(monster.name);
|
||||
assertSnakeCase(monster.type, `${path}.type`);
|
||||
validateConditionValues(monster.ailments, `${path}.ailments`);
|
||||
validateConditionValues(monster.weaknesses, `${path}.weaknesses`);
|
||||
validateConditionValues(monster.weakpoints, `${path}.weakpoints`);
|
||||
|
||||
assert.equal(monster.damage, undefined, `${path}.damage must not be used`);
|
||||
assert.ok(Array.isArray(monster.hitzones), `${path}.hitzones must be an array`);
|
||||
assert.ok(monster.hitzones.length > 0, `${path}.hitzones must not be empty`);
|
||||
monster.hitzones.forEach((hitzone, hitzoneIndex) => {
|
||||
const hitzonePath = `${path}.hitzones[${hitzoneIndex}]`;
|
||||
assertSnakeCase(hitzone.name, `${hitzonePath}.name`);
|
||||
assert.equal(typeof hitzone.physical, "object", `${hitzonePath}.physical must be an object`);
|
||||
assert.equal(typeof hitzone.elemental, "object", `${hitzonePath}.elemental must be an object`);
|
||||
PHYSICAL_HITZONE_KEYS.forEach((key) => assert.equal(typeof hitzone.physical[key], "number", `${hitzonePath}.physical.${key} must be a number`));
|
||||
ELEMENTAL_HITZONE_KEYS.forEach((key) => assert.equal(typeof hitzone.elemental[key], "number", `${hitzonePath}.elemental.${key} must be a number`));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function validateEndemicData(endemicPayload) {
|
||||
["endemicLife", "aquaticLife"].forEach((collection) => {
|
||||
assert.ok(Array.isArray(endemicPayload[collection]), `${collection} must be an array`);
|
||||
endemicPayload[collection].forEach((item, itemIndex) => {
|
||||
const path = `${collection}[${itemIndex}]`;
|
||||
assertSnakeCase(item.name, `${path}.name`);
|
||||
if (item.description) assertSnakeCase(item.description, `${path}.description`);
|
||||
validateConditionValues(item.locations, `${path}.locations`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function validateTranslationKeys(translations, path) {
|
||||
Object.keys(translations).forEach((key) => {
|
||||
assert.ok(!/\s/.test(key), `${path}.${key} must not contain spaces`);
|
||||
});
|
||||
}
|
||||
|
||||
export function validateDiablo4Affixes(payload) {
|
||||
assert.ok(Array.isArray(payload.categories), "diablo4.categories must be an array");
|
||||
assert.ok(payload.categories.length > 0, "diablo4.categories must not be empty");
|
||||
assert.ok(Array.isArray(payload.affixes), "diablo4.affixes must be an array");
|
||||
assert.ok(payload.affixes.length > 0, "diablo4.affixes must not be empty");
|
||||
|
||||
const ids = new Set();
|
||||
const categoryIds = payload.categories.map((category, index) => {
|
||||
assertSnakeCase(category.id, `diablo4.categories[${index}].id`);
|
||||
assertNonEmptyString({ category }, "category.label");
|
||||
assertSnakeCase(category.icon, `diablo4.categories[${index}].icon`);
|
||||
assertSnakeCase(category.tone, `diablo4.categories[${index}].tone`);
|
||||
return category.id;
|
||||
});
|
||||
|
||||
payload.affixes.forEach((affix, index) => {
|
||||
assertSnakeCase(affix.id, `diablo4.affixes[${index}].id`);
|
||||
assert.ok(!ids.has(affix.id), `diablo4.affixes[${index}].id must be unique`);
|
||||
ids.add(affix.id);
|
||||
assertNonEmptyString({ affix }, "affix.label");
|
||||
assert.ok(Array.isArray(affix.categories), `diablo4.affixes[${index}].categories must be an array`);
|
||||
affix.categories.forEach((category, categoryIndex) => {
|
||||
assertSnakeCase(category, `diablo4.affixes[${index}].categories[${categoryIndex}]`);
|
||||
assert.ok(categoryIds.includes(category), `diablo4.affixes[${index}].categories[${categoryIndex}] must exist in diablo4.categories`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -2,182 +2,6 @@ import { readFile } from "node:fs/promises";
|
|||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const SNAKE_CASE_RE = /^[a-z0-9]+(?:_[a-z0-9]+)*$/;
|
||||
const PHYSICAL_HITZONE_KEYS = ["cut", "blunt", "ammo"];
|
||||
const ELEMENTAL_HITZONE_KEYS = ["fire", "water", "thunder", "ice", "dragon"];
|
||||
|
||||
function valueAt(object, path) {
|
||||
return path.split(".").reduce((value, key) => value?.[key], object);
|
||||
}
|
||||
|
||||
function assertNonEmptyString(object, path) {
|
||||
const value = valueAt(object, path);
|
||||
assert.equal(typeof value, "string", `${path} must be a string`);
|
||||
assert.ok(value.trim(), `${path} must not be empty`);
|
||||
}
|
||||
|
||||
function assertStringArray(object, path, minLength = 1) {
|
||||
const value = valueAt(object, path);
|
||||
assert.ok(Array.isArray(value), `${path} must be an array`);
|
||||
assert.ok(value.length >= minLength, `${path} must contain at least ${minLength} item(s)`);
|
||||
value.forEach((item, index) => {
|
||||
assert.equal(typeof item, "string", `${path}[${index}] must be a string`);
|
||||
assert.ok(item.trim(), `${path}[${index}] must not be empty`);
|
||||
});
|
||||
}
|
||||
|
||||
function validateSiteContent(site) {
|
||||
[
|
||||
"brand.name",
|
||||
"brand.homeAriaLabel",
|
||||
"navigation.home",
|
||||
"navigation.toolboxes",
|
||||
"navigation.games",
|
||||
"navigation.mobileGames",
|
||||
"sidebar.badge",
|
||||
"sidebar.note",
|
||||
"topbar.dashboard",
|
||||
"topbar.toolbox",
|
||||
"topbar.games",
|
||||
"gamesPage.eyebrow",
|
||||
"gamesPage.title",
|
||||
"gamesPage.description",
|
||||
"gamesPage.emptyTitle",
|
||||
"gamesPage.emptyText",
|
||||
"home.hero.eyebrow",
|
||||
"home.hero.title",
|
||||
"home.hero.description",
|
||||
"home.hero.primaryAction",
|
||||
"home.hero.secondaryAction",
|
||||
"home.stats.toolboxSingular",
|
||||
"home.stats.toolboxPlural",
|
||||
"home.stats.toolSingular",
|
||||
"home.stats.toolPlural",
|
||||
"home.origin.eyebrow",
|
||||
"home.origin.title",
|
||||
"home.origin.visualAlt",
|
||||
"home.origin.dialogueAriaLabel",
|
||||
"home.origin.caption",
|
||||
"home.legal.copyright",
|
||||
"home.legal.disclaimer",
|
||||
"toolboxes.eyebrow",
|
||||
"toolboxes.title",
|
||||
"toolboxes.newButton",
|
||||
"toolboxes.importAll",
|
||||
"toolboxes.exportAll",
|
||||
"toolboxes.importOne",
|
||||
"toolboxes.summary",
|
||||
"toolboxes.storageHelp.title",
|
||||
"toolboxes.storageHelp.text",
|
||||
"toolboxes.emptyTitle",
|
||||
"toolboxes.emptyText"
|
||||
].forEach((path) => assertNonEmptyString(site, path));
|
||||
|
||||
assertStringArray(site, "toolboxes.storageHelp.items", 3);
|
||||
|
||||
const lines = valueAt(site, "home.origin.lines");
|
||||
assert.ok(Array.isArray(lines), "home.origin.lines must be an array");
|
||||
assert.ok(lines.length >= 2, "home.origin.lines must contain at least 2 items");
|
||||
lines.forEach((line, index) => {
|
||||
assert.ok(["user", "app"].includes(line?.speaker), `home.origin.lines[${index}].speaker must be "user" or "app"`);
|
||||
assert.equal(typeof line?.text, "string", `home.origin.lines[${index}].text must be a string`);
|
||||
assert.ok(line.text.trim(), `home.origin.lines[${index}].text must not be empty`);
|
||||
});
|
||||
}
|
||||
|
||||
function assertSnakeCase(value, path) {
|
||||
assert.equal(typeof value, "string", `${path} must be a string`);
|
||||
assert.match(value, SNAKE_CASE_RE, `${path} must be snake_case`);
|
||||
}
|
||||
|
||||
function validateConditionValues(items, path) {
|
||||
assert.ok(Array.isArray(items), `${path} must be an array`);
|
||||
items.forEach((item, index) => {
|
||||
assertSnakeCase(item?.condition, `${path}[${index}].condition`);
|
||||
assert.ok(Array.isArray(item?.values), `${path}[${index}].values must be an array`);
|
||||
item.values.forEach((value, valueIndex) => assertSnakeCase(value, `${path}[${index}].values[${valueIndex}]`));
|
||||
});
|
||||
}
|
||||
|
||||
function validateMonsterData(monsters) {
|
||||
assert.ok(Array.isArray(monsters), "monsters must be an array");
|
||||
const ids = new Set();
|
||||
const names = new Set();
|
||||
|
||||
monsters.forEach((monster, monsterIndex) => {
|
||||
const path = `monsters[${monsterIndex}]`;
|
||||
assert.equal(typeof monster.id, "number", `${path}.id must be a number`);
|
||||
assert.ok(Number.isInteger(monster.id), `${path}.id must be an integer`);
|
||||
assert.ok(!ids.has(monster.id), `${path}.id must be unique`);
|
||||
ids.add(monster.id);
|
||||
|
||||
assertSnakeCase(monster.name, `${path}.name`);
|
||||
assert.ok(!names.has(monster.name), `${path}.name must be unique`);
|
||||
names.add(monster.name);
|
||||
assertSnakeCase(monster.type, `${path}.type`);
|
||||
validateConditionValues(monster.ailments, `${path}.ailments`);
|
||||
validateConditionValues(monster.weaknesses, `${path}.weaknesses`);
|
||||
validateConditionValues(monster.weakpoints, `${path}.weakpoints`);
|
||||
|
||||
assert.equal(monster.damage, undefined, `${path}.damage must not be used`);
|
||||
assert.ok(Array.isArray(monster.hitzones), `${path}.hitzones must be an array`);
|
||||
assert.ok(monster.hitzones.length > 0, `${path}.hitzones must not be empty`);
|
||||
monster.hitzones.forEach((hitzone, hitzoneIndex) => {
|
||||
const hitzonePath = `${path}.hitzones[${hitzoneIndex}]`;
|
||||
assertSnakeCase(hitzone.name, `${hitzonePath}.name`);
|
||||
assert.equal(typeof hitzone.physical, "object", `${hitzonePath}.physical must be an object`);
|
||||
assert.equal(typeof hitzone.elemental, "object", `${hitzonePath}.elemental must be an object`);
|
||||
PHYSICAL_HITZONE_KEYS.forEach((key) => assert.equal(typeof hitzone.physical[key], "number", `${hitzonePath}.physical.${key} must be a number`));
|
||||
ELEMENTAL_HITZONE_KEYS.forEach((key) => assert.equal(typeof hitzone.elemental[key], "number", `${hitzonePath}.elemental.${key} must be a number`));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function validateEndemicData(endemicPayload) {
|
||||
["endemicLife", "aquaticLife"].forEach((collection) => {
|
||||
assert.ok(Array.isArray(endemicPayload[collection]), `${collection} must be an array`);
|
||||
endemicPayload[collection].forEach((item, itemIndex) => {
|
||||
const path = `${collection}[${itemIndex}]`;
|
||||
assertSnakeCase(item.name, `${path}.name`);
|
||||
if (item.description) assertSnakeCase(item.description, `${path}.description`);
|
||||
validateConditionValues(item.locations, `${path}.locations`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function validateTranslationKeys(translations, path) {
|
||||
Object.keys(translations).forEach((key) => {
|
||||
assert.ok(!/\s/.test(key), `${path}.${key} must not contain spaces`);
|
||||
});
|
||||
}
|
||||
|
||||
function validateDiablo4Affixes(payload) {
|
||||
assert.ok(Array.isArray(payload.categories), "diablo4.categories must be an array");
|
||||
assert.ok(payload.categories.length > 0, "diablo4.categories must not be empty");
|
||||
assert.ok(Array.isArray(payload.affixes), "diablo4.affixes must be an array");
|
||||
assert.ok(payload.affixes.length > 0, "diablo4.affixes must not be empty");
|
||||
|
||||
const ids = new Set();
|
||||
const categoryIds = payload.categories.map((category, index) => {
|
||||
assertSnakeCase(category.id, `diablo4.categories[${index}].id`);
|
||||
assertNonEmptyString({ category }, "category.label");
|
||||
assertSnakeCase(category.icon, `diablo4.categories[${index}].icon`);
|
||||
assertSnakeCase(category.tone, `diablo4.categories[${index}].tone`);
|
||||
return category.id;
|
||||
});
|
||||
payload.affixes.forEach((affix, index) => {
|
||||
assertSnakeCase(affix.id, `diablo4.affixes[${index}].id`);
|
||||
assert.ok(!ids.has(affix.id), `diablo4.affixes[${index}].id must be unique`);
|
||||
ids.add(affix.id);
|
||||
assertNonEmptyString({ affix }, "affix.label");
|
||||
assert.ok(Array.isArray(affix.categories), `diablo4.affixes[${index}].categories must be an array`);
|
||||
affix.categories.forEach((category, categoryIndex) => {
|
||||
assertSnakeCase(category, `diablo4.affixes[${index}].categories[${categoryIndex}]`);
|
||||
assert.ok(categoryIds.includes(category), `diablo4.affixes[${index}].categories[${categoryIndex}] must exist in diablo4.categories`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test("vite entrypoint loads the react application", async () => {
|
||||
const html = await readFile("website/index.html", "utf8");
|
||||
|
||||
|
|
@ -192,6 +16,7 @@ test("react application defines the expected local toolbox primitives", async ()
|
|||
const styleNebula = await readFile("website/src/styles/_nebula.scss", "utf8");
|
||||
const styleTokens = await readFile("website/src/styles/_tokens.scss", "utf8");
|
||||
const styleBase = await readFile("website/src/styles/_base.scss", "utf8");
|
||||
const styleCards = await readFile("website/src/styles/_cards.scss", "utf8");
|
||||
const styleHome = await readFile("website/src/styles/_home.scss", "utf8");
|
||||
const styleToolboxes = await readFile("website/src/styles/_toolboxes.scss", "utf8");
|
||||
const styleIcons = await readFile("website/src/styles/_icons.scss", "utf8");
|
||||
|
|
@ -200,6 +25,8 @@ test("react application defines the expected local toolbox primitives", async ()
|
|||
const iconComponent = await readFile("website/src/components/Icon.jsx", "utf8");
|
||||
const gamesPage = await readFile("website/src/features/games/GamesPage.jsx", "utf8");
|
||||
const gameRoute = await readFile("website/src/features/games/GameRoute.jsx", "utf8");
|
||||
const gameLoaders = await readFile("website/src/features/games/loaders.js", "utf8");
|
||||
const gameFiltersPanel = await readFile("website/src/features/games/GameFiltersPanel.jsx", "utf8");
|
||||
const diablo4Page = await readFile("website/src/features/games/diablo4/Diablo4Page.jsx", "utf8");
|
||||
const diablo4Overview = await readFile("website/src/features/games/diablo4/Diablo4Overview.jsx", "utf8");
|
||||
const diablo4Listing = await readFile("website/src/features/games/diablo4/Diablo4Listing.jsx", "utf8");
|
||||
|
|
@ -218,6 +45,7 @@ test("react application defines the expected local toolbox primitives", async ()
|
|||
const screenshotsModule = await readFile("website/src/features/toolboxes/modules/ScreenshotsModule.jsx", "utf8");
|
||||
const linksModule = await readFile("website/src/features/toolboxes/modules/LinksModule.jsx", "utf8");
|
||||
const countersModule = await readFile("website/src/features/toolboxes/modules/CountersModule.jsx", "utf8");
|
||||
const reorderHook = await readFile("website/src/hooks/usePointerReorder.js", "utf8");
|
||||
|
||||
assert.match(source, /sokkog:toolboxes/);
|
||||
assert.match(source, /sokkog:game-toolbox-links/);
|
||||
|
|
@ -244,6 +72,10 @@ test("react application defines the expected local toolbox primitives", async ()
|
|||
assert.match(styleIcons, /ui-icon-add/);
|
||||
assert.match(styleMhwilds, /\.mhwilds-card/);
|
||||
assert.match(styleMhwilds, /\.mhwilds-flip-indicator/);
|
||||
assert.match(styleMhwilds, /\.game-home-card/);
|
||||
assert.match(styleMhwilds, /\.game-layout/);
|
||||
assert.match(styleMhwilds, /\.game-grid/);
|
||||
assert.match(styleCards, /compact-data-card/);
|
||||
assert.match(styleResponsive, /@media \(max-width: 760px\)/);
|
||||
assert.match(source, /features\/toolboxes\/modules\/index\.jsx/);
|
||||
assert.match(source, /features\/games\/GamesPage\.jsx/);
|
||||
|
|
@ -256,10 +88,17 @@ test("react application defines the expected local toolbox primitives", async ()
|
|||
assert.match(gameRoute, /export function GameRoute/);
|
||||
assert.match(gameRoute, /MhwildsPage/);
|
||||
assert.match(gameRoute, /Diablo4Page/);
|
||||
assert.match(gameLoaders, /export async function loadMhwildsData/);
|
||||
assert.match(gameLoaders, /export async function loadDiablo4Data/);
|
||||
assert.match(gameLoaders, /\/data\/diablo4\/affixes_types\.json/);
|
||||
assert.match(gameLoaders, /filterOptionKeys/);
|
||||
assert.match(gameFiltersPanel, /export function GameFiltersPanel/);
|
||||
assert.match(gameFiltersPanel, /filter-reset-button/);
|
||||
assert.match(diablo4Page, /export function Diablo4Page/);
|
||||
assert.match(diablo4Overview, /export function Diablo4Overview/);
|
||||
assert.match(diablo4Listing, /export function Diablo4Listing/);
|
||||
assert.match(diablo4Filters, /export function Diablo4Filters/);
|
||||
assert.match(diablo4Filters, /GameFiltersPanel/);
|
||||
assert.match(diablo4AffixCard, /export function Diablo4AffixCard/);
|
||||
assert.match(diablo4AffixCard, /categoryMap/);
|
||||
assert.match(diablo4Utils, /getFilteredDiablo4Affixes/);
|
||||
|
|
@ -268,6 +107,7 @@ test("react application defines the expected local toolbox primitives", async ()
|
|||
assert.match(mhwildsListing, /export function MhwildsListing/);
|
||||
assert.match(mhwildsListing, /endemic_life/);
|
||||
assert.match(mhwildsFilters, /export function MhwildsFilters/);
|
||||
assert.match(mhwildsFilters, /GameFiltersPanel/);
|
||||
assert.match(monsterCard, /export function MonsterCard/);
|
||||
assert.match(monsterCard, /ui-icon-flip/);
|
||||
assert.match(endemicCard, /export function EndemicCard/);
|
||||
|
|
@ -297,14 +137,15 @@ test("react application defines the expected local toolbox primitives", async ()
|
|||
assert.match(linksModule, /copyText/);
|
||||
assert.match(source, /DEFAULT_SITE_CONTENT/);
|
||||
assert.match(source, /\/data\/site\.json/);
|
||||
assert.match(source, /\/data\/diablo4\/affixes_types\.json/);
|
||||
assert.match(source, /filterOptionKeys/);
|
||||
assert.match(source, /APP_STORAGE_LIMIT_BYTES/);
|
||||
assert.match(source, /DEFAULT_TOOLBOX_ICON/);
|
||||
assert.match(source, /TOOLBOX_ICON_FILES/);
|
||||
assert.match(source, /function normalizeToolboxIcon/);
|
||||
assert.match(source, /function ToolboxIconPicker/);
|
||||
assert.match(source, /function ToolboxGameIcon/);
|
||||
assert.match(source, /updateToolboxOrder/);
|
||||
assert.match(source, /draggingToolboxId/);
|
||||
assert.match(source, /toolbox-card-drag-handle/);
|
||||
assert.match(source, /function StorageQuota/);
|
||||
assert.match(source, /function writeStorageValue/);
|
||||
assert.match(source, /role="progressbar"/);
|
||||
|
|
@ -323,10 +164,13 @@ test("react application defines the expected local toolbox primitives", async ()
|
|||
assert.match(source, /toolbox-icon-cover/);
|
||||
assert.match(source, /moduleColumns/);
|
||||
assert.match(source, /function ToolboxView/);
|
||||
assert.match(source, /usePointerReorder/);
|
||||
assert.match(moduleRegistry, /onDragStart/);
|
||||
assert.match(moduleRegistry, /onPointerDown/);
|
||||
assert.match(moduleRegistry, /setPointerCapture/);
|
||||
assert.match(moduleRegistry, /elementFromPoint/);
|
||||
assert.match(moduleRegistry, /usePointerReorder/);
|
||||
assert.match(reorderHook, /export function usePointerReorder/);
|
||||
assert.match(reorderHook, /setPointerCapture/);
|
||||
assert.match(reorderHook, /elementFromPoint/);
|
||||
assert.match(moduleRegistry, /tool-add-card/);
|
||||
assert.match(moduleRegistry, /tool-quick-add-button/);
|
||||
assert.match(moduleRegistry, /tool-add-quick-toggle/);
|
||||
|
|
@ -344,74 +188,7 @@ test("react application defines the expected local toolbox primitives", async ()
|
|||
assert.match(source, /function openImageInNewTab/);
|
||||
assert.match(source, /<Icon name="zoom"/);
|
||||
assert.match(screenshotsModule, /dataTransfer\.files/);
|
||||
});
|
||||
|
||||
test("site content json is complete", async () => {
|
||||
const site = JSON.parse(await readFile("website/public/data/site.json", "utf8"));
|
||||
validateSiteContent(site);
|
||||
});
|
||||
|
||||
test("mhwilds data and assets are available", async () => {
|
||||
const site = JSON.parse(await readFile("website/public/data/site.json", "utf8"));
|
||||
const games = JSON.parse(await readFile("website/public/data/games.json", "utf8"));
|
||||
const monsters = JSON.parse(await readFile("website/public/data/mhwilds/monsters.json", "utf8"));
|
||||
const endemicLife = JSON.parse(await readFile("website/public/data/mhwilds/endemic_life.json", "utf8"));
|
||||
const translations = JSON.parse(await readFile("website/public/data/mhwilds/i18n/fr.json", "utf8"));
|
||||
const enTranslations = JSON.parse(await readFile("website/public/data/mhwilds/i18n/en.json", "utf8"));
|
||||
const listingSource = await readFile("website/src/features/games/mhwilds/MhwildsListing.jsx", "utf8");
|
||||
|
||||
assert.equal(site.brand.name, "Sokko G");
|
||||
assert.equal(site.home.hero.title, "Sokko G");
|
||||
assert.equal(site.gamesPage.title, "Jeux disponibles");
|
||||
assert.match(site.toolboxes.summary, /espaces modulaires/);
|
||||
assert.ok(site.toolboxes.storageHelp.items.length >= 3);
|
||||
assert.equal(games.games[0].id, "mhwilds");
|
||||
assert.equal(games.games[0].eyebrow, "Guide de jeu");
|
||||
assert.match(games.games[0].images.cardCover, /games\/mhwilds\/card-cover\.png/);
|
||||
assert.match(games.games[0].images.heroBg, /games\/mhwilds\/hero-bg\.png/);
|
||||
assert.ok(monsters.monsters.length > 0);
|
||||
assert.deepEqual(monsters.elements, ["fire", "water", "thunder", "ice", "dragon"]);
|
||||
validateMonsterData(monsters.monsters);
|
||||
assert.ok(Array.isArray(endemicLife.locations), "endemicLife.locations must be an array");
|
||||
assert.ok(endemicLife.locations.length > 0, "endemicLife.locations must not be empty");
|
||||
assert.ok(endemicLife.endemicLife.length > 0);
|
||||
validateEndemicData(endemicLife);
|
||||
validateTranslationKeys(translations, "fr");
|
||||
validateTranslationKeys(enTranslations, "en");
|
||||
assert.equal(translations.monsters, "monstres");
|
||||
assert.equal(translations.dark_hornet, "frelon clair-obscur");
|
||||
assert.equal(translations["dark hornet"], undefined);
|
||||
assert.match(listingSource, /function MhwildsListing/);
|
||||
const monsterCardSource = await readFile("website/src/features/games/mhwilds/cards/MonsterCard.jsx", "utf8");
|
||||
assert.match(monsterCardSource, /monster-card/);
|
||||
assert.match(monsterCardSource, /monster\.hitzones/);
|
||||
await readFile("website/public/static/img/mhwilds/chatacabra.png");
|
||||
await readFile("website/public/static/img/games/mhwilds/card-cover.png");
|
||||
await readFile("website/public/static/img/games/mhwilds/hero-bg.png");
|
||||
});
|
||||
|
||||
test("diablo4 data and route are available", async () => {
|
||||
const games = JSON.parse(await readFile("website/public/data/games.json", "utf8"));
|
||||
const affixes = JSON.parse(await readFile("website/public/data/diablo4/affixes_types.json", "utf8"));
|
||||
const routeSource = await readFile("website/src/features/games/GameRoute.jsx", "utf8");
|
||||
const pageSource = await readFile("website/src/features/games/diablo4/Diablo4Page.jsx", "utf8");
|
||||
const listingSource = await readFile("website/src/features/games/diablo4/Diablo4Listing.jsx", "utf8");
|
||||
const diablo4Game = games.games.find((game) => game.id === "diablo4");
|
||||
|
||||
assert.ok(diablo4Game, "games.json must declare diablo4");
|
||||
assert.match(diablo4Game.images.cardCover, /games\/diablo4\/card-cover\.png/);
|
||||
assert.match(diablo4Game.images.heroBg, /games\/diablo4\/hero-bg\.png/);
|
||||
validateDiablo4Affixes(affixes);
|
||||
assert.match(routeSource, /game\.id === "diablo4"/);
|
||||
assert.match(pageSource, /category === "affixes"/);
|
||||
assert.match(listingSource, /diablo4\.filterOptions\.affixes/);
|
||||
await readFile("website/public/static/img/games/diablo4/card-cover.png");
|
||||
await readFile("website/public/static/img/games/diablo4/hero-bg.png");
|
||||
await readFile("website/public/static/img/diablo4/axe.svg");
|
||||
await readFile("website/public/static/img/diablo4/cube.svg");
|
||||
await readFile("website/public/static/img/diablo4/fire.svg");
|
||||
await readFile("website/public/static/img/diablo4/shield.svg");
|
||||
await readFile("website/public/static/img/diablo4/wing.svg");
|
||||
assert.doesNotMatch(`${mhwildsListing}\n${diablo4Listing}\n${diablo4Overview}`, /mhwilds-(home-grid|home-card|heading|title-row|layout|filters|results|grid)/);
|
||||
});
|
||||
|
||||
test("server and vite support local env configuration", async () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue