Merge pull request 'dev' (#115) from dev into main
Reviewed-on: #115
This commit was merged in pull request #115.
This commit is contained in:
@@ -207,10 +207,92 @@ describe("CategoriesService", () => {
|
||||
expect(service.getChildren).toHaveBeenCalledWith("c1");
|
||||
expect(partsCatalogsService.fetchParts).not.toHaveBeenCalled();
|
||||
expect(result.parts).toEqual([]);
|
||||
expect(result.children).toEqual(discovered);
|
||||
expect((result as { children?: unknown }).children).toEqual(discovered);
|
||||
// The wrapper attaches the server-resolved ancestor trail (id+name only).
|
||||
expect(result.ancestors).toEqual([{ id: "root", name: "Kök" }]);
|
||||
});
|
||||
|
||||
// Regression guard for the PSA "0 parça" bug: VIN-decoded Peugeot/Citroën
|
||||
// scope categories carry a json-vin-main-groups.action linkPath (and the
|
||||
// next level json-vin-illustrations.action). These are PARENTS — they must
|
||||
// route to getChildren, not fall through to the leaf path and render empty.
|
||||
// The dispatch must stay in sync with PL24Service.isPsaVin*Path; this test
|
||||
// fails loudly if it drifts again.
|
||||
it("routes a PSA VIN scope (json-vin-main-groups.action) to children, not an empty leaf", async () => {
|
||||
const category = {
|
||||
id: "psa1",
|
||||
name: "mekanik",
|
||||
nameOriginal: "mekanik",
|
||||
parentId: null,
|
||||
vehicleId: "v1",
|
||||
source: "pl24",
|
||||
linkPath: "/psa/peugeot_parts/json-vin-main-groups.action?scope=_FCT0001&vin=VF3ABCDE",
|
||||
linkWid: null,
|
||||
hasSubgroups: null,
|
||||
hasParts: null,
|
||||
};
|
||||
let selectCall = 0;
|
||||
const db = {
|
||||
select: vi.fn().mockImplementation(() => {
|
||||
selectCall++;
|
||||
const captured = selectCall;
|
||||
const c: Record<string, any> = {};
|
||||
c.from = vi.fn().mockReturnValue(c);
|
||||
c.where = vi.fn().mockImplementation(() => (captured === 2 ? [] : c));
|
||||
c.limit = vi.fn().mockReturnValue([category]);
|
||||
return c;
|
||||
}),
|
||||
execute: vi.fn().mockResolvedValue([]),
|
||||
};
|
||||
|
||||
const { service } = createService(db);
|
||||
const discovered = [{ id: "mg1", name: "motor", children: undefined }];
|
||||
vi.spyOn(service, "getChildren").mockResolvedValue(discovered as any);
|
||||
|
||||
const result = await service.getCategoryWithParts("psa1");
|
||||
|
||||
expect(service.getChildren).toHaveBeenCalledWith("psa1");
|
||||
expect(result.parts).toEqual([]);
|
||||
expect((result as { children?: unknown }).children).toEqual(discovered);
|
||||
});
|
||||
|
||||
it("surfaces loadError (not an empty grid) when a PSA VIN main-group drill comes back empty", async () => {
|
||||
const category = {
|
||||
id: "psa2",
|
||||
name: "motor",
|
||||
nameOriginal: "motor",
|
||||
parentId: "psa1",
|
||||
vehicleId: "v1",
|
||||
source: "pl24",
|
||||
linkPath: "/psa/peugeot_parts/json-vin-illustrations.action?mainGroup=P8&vin=VF3ABCDE",
|
||||
linkWid: null,
|
||||
hasSubgroups: null,
|
||||
hasParts: null,
|
||||
};
|
||||
let selectCall = 0;
|
||||
const db = {
|
||||
select: vi.fn().mockImplementation(() => {
|
||||
selectCall++;
|
||||
const captured = selectCall;
|
||||
const c: Record<string, any> = {};
|
||||
c.from = vi.fn().mockReturnValue(c);
|
||||
c.where = vi.fn().mockImplementation(() => (captured === 2 ? [] : c));
|
||||
c.limit = vi.fn().mockReturnValue([category]);
|
||||
return c;
|
||||
}),
|
||||
execute: vi.fn().mockResolvedValue([]),
|
||||
};
|
||||
|
||||
const { service } = createService(db);
|
||||
// Drill comes back empty (transient upstream failure).
|
||||
vi.spyOn(service, "getChildren").mockResolvedValue([] as any);
|
||||
|
||||
const result = await service.getCategoryWithParts("psa2");
|
||||
|
||||
expect(service.getChildren).toHaveBeenCalledWith("psa2");
|
||||
expect((result as { loadError?: boolean }).loadError).toBe(true);
|
||||
expect((result as { children?: unknown }).children).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAncestors", () => {
|
||||
|
||||
@@ -918,15 +918,29 @@ export class CategoriesService {
|
||||
}
|
||||
|
||||
// PSA parent nodes — always fetch children on-demand:
|
||||
// • psa:: scope paths (top-level scopes)
|
||||
// • json-illustrations.action paths (mid-level main groups → illustration lists)
|
||||
// • psa:: scope paths (catalog-browse top-level scopes)
|
||||
// • json-illustrations.action (catalog-browse main group → illustrations)
|
||||
// • json-vin-main-groups.action (VIN-indexed scope → main groups)
|
||||
// • json-vin-illustrations.action (VIN-indexed main group → illustrations)
|
||||
// The two VIN-indexed levels were MISSING here, so VIN-decoded Peugeot/Citroën
|
||||
// scope categories ("mekanik", "kaporta", "ekipmanlar"…) fell through every
|
||||
// parent branch to the leaf path, fetched no parts (the main-groups endpoint
|
||||
// returns groups, not parts) and rendered an empty panel — the single biggest
|
||||
// "0 parça" complaint. The drill itself works: getChildren →
|
||||
// fetchSubGroupsByPath → fetchVinMainGroups / fetchVinIllustrations. These four
|
||||
// predicates must stay in sync with PL24Service.isPsaPath / isPsaIllusPath /
|
||||
// isPsaVinMainGroupsPath / isPsaVinIllusPath; the leaf level
|
||||
// (vin-image-board.action / image-board.action) correctly stays a leaf below.
|
||||
const psaLink = category.linkPath ?? "";
|
||||
const isPsaParent =
|
||||
category.linkPath?.startsWith("psa::") ||
|
||||
(category.linkPath?.includes("/psa/") &&
|
||||
category.linkPath?.includes("json-illustrations.action"));
|
||||
psaLink.startsWith("psa::") ||
|
||||
(psaLink.includes("/psa/") &&
|
||||
(psaLink.includes("json-illustrations.action") ||
|
||||
psaLink.includes("json-vin-main-groups.action") ||
|
||||
psaLink.includes("json-vin-illustrations.action")));
|
||||
if (isPsaParent && category.vehicleId) {
|
||||
const psaChildren = await this.getChildren(categoryId);
|
||||
return {
|
||||
const base = {
|
||||
id: category.id,
|
||||
name: category.name,
|
||||
description: category.nameOriginal || null,
|
||||
@@ -934,8 +948,14 @@ export class CategoriesService {
|
||||
parts: [],
|
||||
schemaPics: [],
|
||||
hotspots: [],
|
||||
children: psaChildren,
|
||||
};
|
||||
// A PSA scope / main group is never a parts leaf. If the on-demand drill
|
||||
// comes back empty the upstream snapshot failed transiently — surface a
|
||||
// retryable load error (matching the pl24/emex group-node branches below)
|
||||
// instead of a misleading empty subcategory grid.
|
||||
return psaChildren.length > 0
|
||||
? { ...base, children: psaChildren }
|
||||
: { ...base, loadError: true };
|
||||
}
|
||||
|
||||
// parts-catalogs parent group — the groups2 API marked this node as having
|
||||
|
||||
@@ -57,6 +57,12 @@ async function bootstrap() {
|
||||
],
|
||||
objectSrc: ["'none'"],
|
||||
frameSrc: ["https://challenges.cloudflare.com", "https://destek.sase.tr"],
|
||||
// The Meta Pixel (fbevents 2.9.x) dispatches ALL events from a Web Worker
|
||||
// created off a blob: URL. With no worker-src, the browser falls back to
|
||||
// script-src — which lacks blob: — so the worker is CSP-blocked and NO
|
||||
// pixel events fire (PageView, CompleteRegistration, everything were 0
|
||||
// until this). Also unblocks Remotion's blob worker on the demos.
|
||||
workerSrc: ["'self'", "blob:"],
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user