Files
0pt/tests/components/import-wizard-ssr.test.ts
admin c1648c04a7
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
feat(orders): CSV-import admin-yta — första riktiga ImportWizard-värden (C8)
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).
2026-09-30 15:36:02 +02:00

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");
});
});