CI (SIAX Cloud) / integration (push) Skipped
CI (SIAX Cloud) / sonar (push) Skipped
CI (SIAX Cloud) / security (push) Successful in 22s
Security Scan (SIAX Cloud) / security (push) Successful in 22s
masterplan-lock / check (push) Successful in 31s
CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (pull_request) Successful in 6s
CI (SIAX Cloud) / security (pull_request) Successful in 18s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 20s
CI (SIAX Cloud) / quality (push) Successful in 1m1s
CI (SIAX Cloud) / integration (pull_request) Successful in 30s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m56s
Symmetrisk mot våg 3.4 CSV-export: POST /api/orders/import (validate | dry_run | execute) + /admin/import som renderar @siax/ui ImportWizard (0.14.0) över @siax/import-export runtime (0.2.0). - Servern är enda auktoritet: RFC4180-parse (ingen eval), omvalidering via frysta @siax/contracts-kerneln (validateRows) + hostregler, exekvering genom BEFINTLIG orderlogik (createOrder, tenant-guardad). - Fail-closed: 401 utan principal, 400 okänd header, 413 vid radtak (500), per-rad-atomsäkra inserts — felrad kan aldrig skada OK-rader. - Partiell import: OK-rader skapas, felrader rapporteras per rad (row/field/code). AUD0-audit via 0pts enda producer-boundary (durable outbox under DATABASE_URL). - Reload-persistens: efter execute läses /api/orders om och de nya raderna listas på sidan. - Tester: 12 route-test (happy, partial, auth fail-closed, tak, no-write validate, header, quoting, rad-fel mitt i batch) + 3 SSR-render-test av ImportWizard över runtime-storen. - Deps: @siax/ui ^0.14.0, @siax/charts ^0.5.0, @siax/import-export ^0.2.0; pnpm-overrides till contracts 0.10.0 / import-export 0.2.0 (ui 0.14.0 deklarerar 0.11.0/0.2.1 som ännu ej publicerats).
64 lines
2.8 KiB
TypeScript
64 lines
2.8 KiB
TypeScript
/**
|
|
* C8: SSR-render proof that the host can render the @siax/ui ImportWizard over
|
|
* the @siax/import-export runtime store (the exact packages the admin import
|
|
* page uses). Node environment — react-dom/server, no DOM needed.
|
|
*/
|
|
import { describe, it, expect, vi } from "vitest";
|
|
|
|
// ImportWizard subscribes to the runtime store via useSyncExternalStore, which
|
|
// demands getServerSnapshot under react-dom/server. For a static render proof
|
|
// the snapshot read is enough — reactivity is the browser's concern.
|
|
vi.mock("react", async (importOriginal) => {
|
|
const React = await importOriginal<typeof import("react")>();
|
|
return { ...React, useSyncExternalStore: ((_subscribe: unknown, getSnapshot: () => unknown) => getSnapshot()) as typeof React.useSyncExternalStore };
|
|
});
|
|
import { createElement } from "react";
|
|
import { renderToString } from "react-dom/server";
|
|
import { ImportWizard } from "@siax/ui";
|
|
import { createImportRunStore } from "@siax/import-export";
|
|
import { createImportJob } from "@siax/contracts/import-export";
|
|
|
|
function makeStore(rows: Record<string, string>[]) {
|
|
return createImportRunStore({
|
|
job: createImportJob({
|
|
id: "test-import",
|
|
tenantId: "tenant-test",
|
|
entity: "orders",
|
|
format: "csv",
|
|
source: { system: "0pt-admin", channel: "web" },
|
|
createdAt: "2026-09-30T00:00:00Z",
|
|
}),
|
|
rows,
|
|
});
|
|
}
|
|
|
|
describe("ImportWizard renders over the runtime store (host integration)", () => {
|
|
it("empty store → explicit empty state with the pipeline <ol>", () => {
|
|
const html = renderToString(
|
|
createElement(ImportWizard, { store: makeStore([]), onExecute: async () => {} }),
|
|
);
|
|
expect(html).toContain("<ol");
|
|
expect(html.length).toBeGreaterThan(100);
|
|
});
|
|
|
|
it("rows present → pipeline steps render; mapped stage shows mapping table", () => {
|
|
const store = makeStore([{ flow: "custom", service_type: "icp", type: "Bolag", volume: "10", frequency: "Engångs", markets: "SE" }]);
|
|
store.detect();
|
|
let html = renderToString(createElement(ImportWizard, { store, columns: [{ source: "flow", target: "flow" }], onExecute: async () => {} }));
|
|
expect(html).toContain("<ol");
|
|
expect(html).toContain("aria-current=\"step\"");
|
|
store.map({ columns: [{ source: "flow", target: "flow", required: true }] });
|
|
html = renderToString(createElement(ImportWizard, { store, onExecute: async () => {} }));
|
|
expect(html).toContain("flow");
|
|
});
|
|
|
|
it("failed store renders the failure report with role=alert", () => {
|
|
const store = makeStore([{ flow: "custom" }]);
|
|
store.detect();
|
|
store.fail("Servern avvisade exekveringen");
|
|
const html = renderToString(createElement(ImportWizard, { store, onExecute: async () => {} }));
|
|
expect(html).toContain("alert");
|
|
expect(html).toContain("Servern avvisade exekveringen");
|
|
});
|
|
});
|