@operato/board 10.28.1 → 10.28.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/src/figure-provider.d.ts +5 -8
- package/dist/src/figure-provider.js +6 -8
- package/dist/src/figure-provider.js.map +1 -1
- package/dist/test/figure-provider.test.js +37 -61
- package/dist/test/figure-provider.test.js.map +1 -1
- package/dist/test/resolved-scene.test.js +3 -3
- package/dist/test/resolved-scene.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/test/figure-provider.test.ts +36 -63
- package/test/resolved-scene.test.ts +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,24 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
### [10.28.3](https://github.com/hatiolab/operato/compare/v10.28.2...v10.28.3) (2026-09-26)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### :mega: Other
|
|
10
|
+
|
|
11
|
+
* **board:** depend on things-scene 10.1.56 — portal-profile@2 door frames ([5c1ae14](https://github.com/hatiolab/operato/commit/5c1ae14f520c445b4831d1a986784cea164d447b))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### [10.28.2](https://github.com/hatiolab/operato/compare/v10.28.1...v10.28.2) (2026-09-25)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### :bug: Bug Fix
|
|
19
|
+
|
|
20
|
+
* **board:** published figures are V3 assets only ([8fe502d](https://github.com/hatiolab/operato/commit/8fe502da6b8b8a0c45ece38c347bbd80a3f29f57))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
6
24
|
### [10.28.1](https://github.com/hatiolab/operato/compare/v10.28.0...v10.28.1) (2026-09-24)
|
|
7
25
|
|
|
8
26
|
**Note:** Version bump only for package @operato/board
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* What the server hands over for one released type.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* tag, because the thing that draws it takes the asset and compiles it with the kernel (ADR-0087 decision 5).
|
|
6
|
-
* A blueprint carries no `format`, so the two are told apart by what the payload says about itself rather
|
|
7
|
-
* than by its shape.
|
|
2
|
+
* What the server hands over for one released type: the figure asset under a `format` tag. The board does not read
|
|
3
|
+
* the asset; the scene's own registration compiles it with the kernel (ADR-0087 decision 5). V1 and V2 are gone
|
|
4
|
+
* (2026-09-26), so a payload that does not say `format: 'v3'` is not a figure this board can draw.
|
|
8
5
|
*/
|
|
9
6
|
export type FigurePayload = {
|
|
10
7
|
format: 'v3';
|
|
11
8
|
type: string;
|
|
12
9
|
asset: any;
|
|
13
|
-
}
|
|
10
|
+
};
|
|
14
11
|
/** Asks for one type's payload. Null when no released figure carries that name. */
|
|
15
12
|
export type BlueprintFetch = (type: string) => Promise<FigurePayload | null>;
|
|
16
|
-
/** Registers one released figure with the scene
|
|
13
|
+
/** Registers one released figure with the scene. Anything but a figure asset is refused by name. */
|
|
17
14
|
export declare function registerFigurePayload(type: string, payload: FigurePayload): void;
|
|
18
15
|
export declare const fetchFigureBlueprint: BlueprintFetch;
|
|
19
16
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright © HatioLab Inc. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
import gql from 'graphql-tag';
|
|
5
|
-
import { Component,
|
|
5
|
+
import { Component, registerFigureV3, registerTypeResolver } from '@hatiolab/things-scene';
|
|
6
6
|
import { client, gqlContext } from '@operato/graphql';
|
|
7
7
|
/*
|
|
8
8
|
* Where a board gets the figures somebody authored.
|
|
@@ -28,14 +28,12 @@ import { client, gqlContext } from '@operato/graphql';
|
|
|
28
28
|
*/
|
|
29
29
|
/** The figure namespace: `EQ_MIXER`, `CONVEYOR`, `VEH_AGV_01`. */
|
|
30
30
|
const FIGURE_TYPE = /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/;
|
|
31
|
-
|
|
32
|
-
/** Registers one released figure with the scene, by whichever door its format uses. */
|
|
31
|
+
/** Registers one released figure with the scene. Anything but a figure asset is refused by name. */
|
|
33
32
|
export function registerFigurePayload(type, payload) {
|
|
34
33
|
var _a;
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
registerFigure(payload);
|
|
34
|
+
if ((payload === null || payload === void 0 ? void 0 : payload.format) !== 'v3')
|
|
35
|
+
throw new Error(`not a figure asset: ${type}`);
|
|
36
|
+
registerFigureV3((_a = payload.type) !== null && _a !== void 0 ? _a : type, payload.asset);
|
|
39
37
|
}
|
|
40
38
|
const FETCH_FIGURE_BLUEPRINT = gql `
|
|
41
39
|
query FetchFigureBlueprint($type: String!) {
|
|
@@ -90,7 +88,7 @@ export function createFigureResolver(fetch = fetchFigureBlueprint) {
|
|
|
90
88
|
What did register stays registered — the seat reads the registry back, not what this says.
|
|
91
89
|
The throw is how "could not ask" reaches `TypeResolution.failed`.
|
|
92
90
|
*/
|
|
93
|
-
throw new Error(`Figure
|
|
91
|
+
throw new Error(`Figure 를 가져오지 못했다:\n${failures
|
|
94
92
|
.map(({ type, error }) => ` - ${type}: ${error instanceof Error ? error.message : String(error)}`)
|
|
95
93
|
.join('\n')}`);
|
|
96
94
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"figure-provider.js","sourceRoot":"","sources":["../../src/figure-provider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,GAAG,MAAM,aAAa,CAAA;AAE7B,OAAO,EAAE,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"figure-provider.js","sourceRoot":"","sources":["../../src/figure-provider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,GAAG,MAAM,aAAa,CAAA;AAE7B,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC1F,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,kEAAkE;AAClE,MAAM,WAAW,GAAG,+BAA+B,CAAA;AAYnD,oGAAoG;AACpG,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,OAAsB;;IACxE,IAAI,CAAC,OAAgC,aAAhC,OAAO,uBAAP,OAAO,CAA2B,MAAM,MAAK,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAA;IACtG,gBAAgB,CAAC,MAAA,OAAO,CAAC,IAAI,mCAAI,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;AACvD,CAAC;AAED,MAAM,sBAAsB,GAAG,GAAG,CAAA;;;;CAIjC,CAAA;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAmB,KAAK,EAAE,IAAY,EAAE,EAAE;;IACzE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;QAClC,KAAK,EAAE,sBAAsB;QAC7B,SAAS,EAAE,EAAE,IAAI,EAAE;QACnB,OAAO,EAAE,UAAU,EAAE;KACtB,CAAC,CAAA;IAEF;;;;MAIE;IACF,MAAM,MAAM,GAAI,QAAyD,CAAC,MAAM,CAAA;IAChF,IAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,MAAM,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,WAAC,OAAA,MAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,OAAO,mCAAI,MAAM,CAAC,CAAC,CAAC,CAAA,EAAA,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IACtE,CAAC;IAED,OAAO,MAAA,MAAA,QAAQ,CAAC,IAAI,0CAAE,eAAe,mCAAI,IAAI,CAAA;AAC/C,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAwB,oBAAoB;IAC/E,OAAO,KAAK,UAAU,kBAAkB,CAAC,KAAwB;QAC/D,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAA;QACxF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAM;QACR,CAAC;QAED,MAAM,QAAQ,GAAuC,EAAE,CAAA;QAEvD,MAAM,OAAO,CAAC,GAAG,CACf,MAAM,CAAC,GAAG,CAAC,KAAK,EAAC,IAAI,EAAC,EAAE;YACtB,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAA;gBACjC,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,sDAAsD;oBACtD,OAAM;gBACR,CAAC;gBAED,qBAAqB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YACtC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;YAChC,CAAC;QACH,CAAC,CAAC,CACH,CAAA;QAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB;;;cAGE;YACF,MAAM,IAAI,KAAK,CACb,uBAAuB,QAAQ;iBAC5B,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;iBAClG,IAAI,CAAC,IAAI,CAAC,EAAE,CAChB,CAAA;QACH,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAwB,oBAAoB;IAChF,OAAO,oBAAoB,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAA;AAC1D,CAAC","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport gql from 'graphql-tag'\n\nimport { Component, registerFigureV3, registerTypeResolver } from '@hatiolab/things-scene'\nimport { client, gqlContext } from '@operato/graphql'\n\n/*\n * Where a board gets the figures somebody authored.\n *\n * ── Why the seat is filled here ─────────────────────────────────────────────\n * things-scene keeps the seat (`registerTypeResolver`) and knows nothing about figures, GraphQL or\n * who published what. The board package is where a saved model already gets its values from the\n * server — `board-provider.ts` reads a board by id the same way — so this is where the types a\n * model names, and nobody has registered, get fetched. Putting it in the authoring package would\n * make every consumer drag the modeller along, and putting it in each app would make every app\n * build the same thing (ADR-0063 결정 2 · ADR-0045).\n *\n * ── What it asks for, and what it does not ──────────────────────────────────\n * Uppercase names are the figure namespace, so those are the only ones asked about: a model that\n * names `rect` or a mistyped `Conveyor` never reaches the server. A type that is already\n * registered is left alone — a figure never replaces a component that is already drawing.\n *\n * ── An answer of \"nobody published this\" is not a failure ───────────────────\n * `figureBlueprint` returns null for a type that has no released figure, and that type stays\n * unregistered on purpose: the scene draws it as a stand-in and saves it back untouched\n * (ADR-0063 결정 1). Being unable to ask at all is a different fact, so a transport or server\n * error rejects — `resolveUnknownTypes` reports those in `failed` and the board still opens.\n */\n\n/** The figure namespace: `EQ_MIXER`, `CONVEYOR`, `VEH_AGV_01`. */\nconst FIGURE_TYPE = /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/\n\n/**\n * What the server hands over for one released type: the figure asset under a `format` tag. The board does not read\n * the asset; the scene's own registration compiles it with the kernel (ADR-0087 decision 5). V1 and V2 are gone\n * (2026-09-26), so a payload that does not say `format: 'v3'` is not a figure this board can draw.\n */\nexport type FigurePayload = { format: 'v3'; type: string; asset: any }\n\n/** Asks for one type's payload. Null when no released figure carries that name. */\nexport type BlueprintFetch = (type: string) => Promise<FigurePayload | null>\n\n/** Registers one released figure with the scene. Anything but a figure asset is refused by name. */\nexport function registerFigurePayload(type: string, payload: FigurePayload): void {\n if ((payload as { format?: unknown })?.format !== 'v3') throw new Error(`not a figure asset: ${type}`)\n registerFigureV3(payload.type ?? type, payload.asset)\n}\n\nconst FETCH_FIGURE_BLUEPRINT = gql`\n query FetchFigureBlueprint($type: String!) {\n figureBlueprint(type: $type)\n }\n`\n\nexport const fetchFigureBlueprint: BlueprintFetch = async (type: string) => {\n const response = await client.query({\n query: FETCH_FIGURE_BLUEPRINT,\n variables: { type },\n context: gqlContext()\n })\n\n /*\n The client runs with errorPolicy 'all', so a rejected query comes back as a resolved response\n carrying `errors` and a null field. Without this the caller cannot tell \"no such figure\" from\n \"the door said no\".\n */\n const errors = (response as { errors?: readonly { message?: string }[] }).errors\n if (errors?.length) {\n throw new Error(errors.map(e => e?.message ?? String(e)).join('\\n'))\n }\n\n return response.data?.figureBlueprint ?? null\n}\n\n/**\n * Builds the resolver that fills things-scene's seat.\n *\n * `fetch` is a seam for tests — the default one talks to the server.\n */\nexport function createFigureResolver(fetch: BlueprintFetch = fetchFigureBlueprint) {\n return async function resolveFigureTypes(types: readonly string[]): Promise<void> {\n const asking = types.filter(type => FIGURE_TYPE.test(type) && !Component.register(type))\n if (asking.length === 0) {\n return\n }\n\n const failures: { type: string; error: unknown }[] = []\n\n await Promise.all(\n asking.map(async type => {\n try {\n const payload = await fetch(type)\n if (!payload) {\n /* Nobody published this type. It stays a stand-in. */\n return\n }\n\n registerFigurePayload(type, payload)\n } catch (error) {\n failures.push({ type, error })\n }\n })\n )\n\n if (failures.length > 0) {\n /*\n What did register stays registered — the seat reads the registry back, not what this says.\n The throw is how \"could not ask\" reaches `TypeResolution.failed`.\n */\n throw new Error(\n `Figure 를 가져오지 못했다:\\n${failures\n .map(({ type, error }) => ` - ${type}: ${error instanceof Error ? error.message : String(error)}`)\n .join('\\n')}`\n )\n }\n }\n}\n\n/**\n * Takes the seat. The package entry (`index.ts`) calls this, so an app that has `@operato/board`\n * gets published figures on its boards without registering anything itself. Returns the function\n * that takes the seat back.\n */\nexport function installFigureResolver(fetch: BlueprintFetch = fetchFigureBlueprint): () => void {\n return registerTypeResolver(createFigureResolver(fetch))\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect } from '@open-wc/testing';
|
|
2
|
-
import { Component,
|
|
2
|
+
import { Component, hasFigureV3, registerTypeResolver, resetFiguresV3, resolveUnknownTypes } from '@hatiolab/things-scene';
|
|
3
3
|
import { createFigureResolver } from '../src/figure-provider.js';
|
|
4
4
|
/**
|
|
5
5
|
* The path a published figure takes to a board (ADR-0063 결정 2).
|
|
@@ -8,40 +8,11 @@ import { createFigureResolver } from '../src/figure-provider.js';
|
|
|
8
8
|
* checked here is the part that belongs to the board: which names are asked about, what happens to
|
|
9
9
|
* the answer, and how "nobody published this" stays apart from "could not ask".
|
|
10
10
|
*
|
|
11
|
-
* The
|
|
12
|
-
*
|
|
13
|
-
|
|
14
|
-
const BLUEPRINT_VERSION = 1;
|
|
15
|
-
const blueprintOf = (type) => ({
|
|
16
|
-
version: BLUEPRINT_VERSION,
|
|
17
|
-
type,
|
|
18
|
-
base: { x: 100, y: 100, z: 100 },
|
|
19
|
-
groups: [
|
|
20
|
-
{
|
|
21
|
-
materialKey: 'palette.dark|metal|f|-|-|-',
|
|
22
|
-
material: { token: 'palette.dark', preset: 'metal', flatShading: true },
|
|
23
|
-
animated: false,
|
|
24
|
-
members: [
|
|
25
|
-
{
|
|
26
|
-
name: 'body',
|
|
27
|
-
primitive: 'cube',
|
|
28
|
-
geometryKey: 'cube|100x100x100',
|
|
29
|
-
transform: { position: { x: 0, y: 0, z: 0 }, size: { x: 100, y: 100, z: 100 } },
|
|
30
|
-
sizing: 'scale',
|
|
31
|
-
triangles: 12
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
}
|
|
35
|
-
]
|
|
36
|
-
});
|
|
37
|
-
/**
|
|
38
|
-
* The other thing the door can hand over: a V3 asset under a `format` tag (ADR-0087 decision 5).
|
|
39
|
-
*
|
|
40
|
-
* Written out rather than built with figure-model, for the same reason the blueprint above is: this package
|
|
41
|
-
* does not read either payload, it passes each to the scene's own registration, and a fixture that drifts from
|
|
42
|
-
* the format should fail loudly here rather than draw nothing at runtime. One box standing on the floor, with
|
|
43
|
-
* the volume it takes up declared.
|
|
11
|
+
* The payload below is written out rather than built with figure-model: this package does not read it, it passes it
|
|
12
|
+
* to the scene's own registration, and a fixture that drifts from the format should fail loudly here rather than
|
|
13
|
+
* draw nothing at runtime.
|
|
44
14
|
*/
|
|
15
|
+
/** What the door hands over: a figure asset under a `format` tag. One box standing on the floor, its room declared. */
|
|
45
16
|
const v3AssetOf = (type) => ({
|
|
46
17
|
version: 'figure-v3-asset-1',
|
|
47
18
|
name: type,
|
|
@@ -92,54 +63,59 @@ function fetcherOf(published, failing = []) {
|
|
|
92
63
|
if (failing.includes(type)) {
|
|
93
64
|
throw new Error(`no route to the door (${type})`);
|
|
94
65
|
}
|
|
95
|
-
return (_a = published[type]) !== null && _a !== void 0 ? _a : null;
|
|
66
|
+
return ((_a = published[type]) !== null && _a !== void 0 ? _a : null);
|
|
96
67
|
}
|
|
97
68
|
};
|
|
98
69
|
}
|
|
99
70
|
describe('figure 공급자 — 보드가 발행된 Figure 를 받는 길', () => {
|
|
100
71
|
afterEach(() => {
|
|
101
|
-
resetFigures();
|
|
102
72
|
resetFiguresV3();
|
|
103
73
|
});
|
|
104
74
|
it('대문자 이름만 서버에 묻는다 — 씬의 기본 type 과 오타는 문을 지나지 않는다', async () => {
|
|
105
|
-
const { asked, fetch } = fetcherOf({ EQ_MIXER:
|
|
75
|
+
const { asked, fetch } = fetcherOf({ EQ_MIXER: v3PayloadOf('EQ_MIXER') });
|
|
106
76
|
await createFigureResolver(fetch)(['EQ_MIXER', 'rect', 'Conveyor', 'text', 'VEH_AGV_01']);
|
|
107
77
|
expect(asked).to.deep.equal(['EQ_MIXER', 'VEH_AGV_01']);
|
|
108
78
|
});
|
|
109
79
|
it('이미 등록된 type 은 묻지 않는다 — 그리고 있는 컴포넌트를 figure 가 덮지 않는다', async () => {
|
|
110
|
-
const { asked, fetch } = fetcherOf({ EQ_TAKEN:
|
|
80
|
+
const { asked, fetch } = fetcherOf({ EQ_TAKEN: v3PayloadOf('EQ_TAKEN') });
|
|
111
81
|
/* Somebody else already draws this name — a plugin, an app's own component. */
|
|
112
82
|
Component.register('EQ_TAKEN', class Taken extends Component {
|
|
113
83
|
});
|
|
114
84
|
await createFigureResolver(fetch)(['EQ_TAKEN']);
|
|
115
85
|
expect(asked).to.deep.equal([]);
|
|
116
|
-
expect(
|
|
86
|
+
expect(hasFigureV3('EQ_TAKEN')).to.equal(false);
|
|
117
87
|
});
|
|
118
|
-
it('받은
|
|
119
|
-
const { fetch } = fetcherOf({ EQ_REGISTERED:
|
|
88
|
+
it('받은 자산을 등록한다 — 도면은 type 만 적으면 된다', async () => {
|
|
89
|
+
const { fetch } = fetcherOf({ EQ_REGISTERED: v3PayloadOf('EQ_REGISTERED') });
|
|
120
90
|
await createFigureResolver(fetch)(['EQ_REGISTERED']);
|
|
121
|
-
expect(
|
|
91
|
+
expect(hasFigureV3('EQ_REGISTERED')).to.equal(true);
|
|
122
92
|
/* The component type is registered with it — the model writes `type` and nothing else. */
|
|
123
93
|
expect(Component.register('EQ_REGISTERED')).to.not.equal(undefined);
|
|
124
94
|
});
|
|
125
|
-
it('
|
|
95
|
+
it('자산은 씬의 문으로 등록한다 — 보드는 그것을 읽지 않고 씬에 넘긴다', async () => {
|
|
126
96
|
const { fetch } = fetcherOf({ EQ_V3: v3PayloadOf('EQ_V3') });
|
|
127
97
|
await createFigureResolver(fetch)(['EQ_V3']);
|
|
128
98
|
expect(hasFigureV3('EQ_V3')).to.equal(true);
|
|
129
|
-
|
|
130
|
-
/* 도면은 판을 모른다 — 두 판 다 type 하나만 적으면 그려진다. */
|
|
99
|
+
/* 도면은 type 하나만 적으면 그려진다. */
|
|
131
100
|
expect(Component.register('EQ_V3')).to.not.equal(undefined);
|
|
132
101
|
});
|
|
133
|
-
it('
|
|
134
|
-
const { fetch } = fetcherOf({
|
|
135
|
-
|
|
136
|
-
|
|
102
|
+
it('자산이 아닌 것(format 이 v3 가 아닌 답)은 그 type 만 실패로 남는다 — 옛 청사진을 그리지 않는다', async () => {
|
|
103
|
+
const { fetch } = fetcherOf({ EQ_NEW: v3PayloadOf('EQ_NEW'), EQ_OLD: { version: 1, type: 'EQ_OLD', groups: [] } });
|
|
104
|
+
let message = '';
|
|
105
|
+
try {
|
|
106
|
+
await createFigureResolver(fetch)(['EQ_NEW', 'EQ_OLD']);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
message = error instanceof Error ? error.message : String(error);
|
|
110
|
+
}
|
|
111
|
+
expect(message).to.contain('not a figure asset: EQ_OLD');
|
|
137
112
|
expect(hasFigureV3('EQ_NEW')).to.equal(true);
|
|
113
|
+
expect(hasFigureV3('EQ_OLD')).to.equal(false);
|
|
138
114
|
});
|
|
139
115
|
it('읽을 수 없는 V3 자산은 그 type 만 실패로 남는다', async () => {
|
|
140
116
|
const broken = v3PayloadOf('EQ_V3_BROKEN');
|
|
141
117
|
broken.asset.appearance = [];
|
|
142
|
-
const { fetch } = fetcherOf({ EQ_BESIDE_V3:
|
|
118
|
+
const { fetch } = fetcherOf({ EQ_BESIDE_V3: v3PayloadOf('EQ_BESIDE_V3'), EQ_V3_BROKEN: broken });
|
|
143
119
|
let message = '';
|
|
144
120
|
try {
|
|
145
121
|
await createFigureResolver(fetch)(['EQ_BESIDE_V3', 'EQ_V3_BROKEN']);
|
|
@@ -148,7 +124,7 @@ describe('figure 공급자 — 보드가 발행된 Figure 를 받는 길', () =>
|
|
|
148
124
|
message = error instanceof Error ? error.message : String(error);
|
|
149
125
|
}
|
|
150
126
|
expect(message).to.contain('EQ_V3_BROKEN');
|
|
151
|
-
expect(
|
|
127
|
+
expect(hasFigureV3('EQ_BESIDE_V3')).to.equal(true);
|
|
152
128
|
expect(hasFigureV3('EQ_V3_BROKEN')).to.equal(false);
|
|
153
129
|
});
|
|
154
130
|
it('발행된 것이 없으면 조용히 자리표시로 남는다 — 거절이 아니다', async () => {
|
|
@@ -161,10 +137,10 @@ describe('figure 공급자 — 보드가 발행된 Figure 를 받는 길', () =>
|
|
|
161
137
|
rejected = true;
|
|
162
138
|
}
|
|
163
139
|
expect(rejected).to.equal(false);
|
|
164
|
-
expect(
|
|
140
|
+
expect(hasFigureV3('EQ_NOT_PUBLISHED')).to.equal(false);
|
|
165
141
|
});
|
|
166
142
|
it('물을 수 없으면 거절한다. 그때도 답을 받은 것은 등록된다', async () => {
|
|
167
|
-
const { fetch } = fetcherOf({ EQ_BESIDE_BROKEN:
|
|
143
|
+
const { fetch } = fetcherOf({ EQ_BESIDE_BROKEN: v3PayloadOf('EQ_BESIDE_BROKEN') }, ['EQ_BROKEN']);
|
|
168
144
|
let message = '';
|
|
169
145
|
try {
|
|
170
146
|
await createFigureResolver(fetch)(['EQ_BESIDE_BROKEN', 'EQ_BROKEN']);
|
|
@@ -174,12 +150,12 @@ describe('figure 공급자 — 보드가 발행된 Figure 를 받는 길', () =>
|
|
|
174
150
|
}
|
|
175
151
|
expect(message).to.contain('EQ_BROKEN');
|
|
176
152
|
expect(message).to.not.contain('EQ_BESIDE_BROKEN');
|
|
177
|
-
expect(
|
|
153
|
+
expect(hasFigureV3('EQ_BESIDE_BROKEN')).to.equal(true);
|
|
178
154
|
});
|
|
179
|
-
it('읽을 수 없는
|
|
155
|
+
it('읽을 수 없는 자산은 그 type 만 실패로 남는다 — 다른 type 을 막지 않는다', async () => {
|
|
180
156
|
const { fetch } = fetcherOf({
|
|
181
|
-
EQ_BESIDE_MALFORMED:
|
|
182
|
-
EQ_MALFORMED: {
|
|
157
|
+
EQ_BESIDE_MALFORMED: v3PayloadOf('EQ_BESIDE_MALFORMED'),
|
|
158
|
+
EQ_MALFORMED: { format: 'v3', type: 'EQ_MALFORMED', asset: { version: 'figure-v3-asset-1' } }
|
|
183
159
|
});
|
|
184
160
|
let message = '';
|
|
185
161
|
try {
|
|
@@ -189,11 +165,11 @@ describe('figure 공급자 — 보드가 발행된 Figure 를 받는 길', () =>
|
|
|
189
165
|
message = error instanceof Error ? error.message : String(error);
|
|
190
166
|
}
|
|
191
167
|
expect(message).to.contain('EQ_MALFORMED');
|
|
192
|
-
expect(
|
|
193
|
-
expect(
|
|
168
|
+
expect(hasFigureV3('EQ_BESIDE_MALFORMED')).to.equal(true);
|
|
169
|
+
expect(hasFigureV3('EQ_MALFORMED')).to.equal(false);
|
|
194
170
|
});
|
|
195
171
|
it('씬의 자리에 앉아 동작한다 — 모델의 모르는 type 이 해석 뒤에 남지 않는다', async () => {
|
|
196
|
-
const { fetch } = fetcherOf({ EQ_ON_A_BOARD:
|
|
172
|
+
const { fetch } = fetcherOf({ EQ_ON_A_BOARD: v3PayloadOf('EQ_ON_A_BOARD') });
|
|
197
173
|
const takeBack = registerTypeResolver(createFigureResolver(fetch));
|
|
198
174
|
try {
|
|
199
175
|
const model = {
|
|
@@ -206,7 +182,7 @@ describe('figure 공급자 — 보드가 발행된 Figure 를 받는 길', () =>
|
|
|
206
182
|
const resolution = await resolveUnknownTypes(model);
|
|
207
183
|
expect(resolution.unresolved).to.deep.equal(['EQ_NEVER_PUBLISHED']);
|
|
208
184
|
expect(resolution.failed).to.deep.equal([]);
|
|
209
|
-
expect(
|
|
185
|
+
expect(hasFigureV3('EQ_ON_A_BOARD')).to.equal(true);
|
|
210
186
|
}
|
|
211
187
|
finally {
|
|
212
188
|
takeBack();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"figure-provider.test.js","sourceRoot":"","sources":["../../test/figure-provider.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing'\n\nimport { Component, hasFigure, hasFigureV3, registerTypeResolver, resetFigures, resetFiguresV3, resolveUnknownTypes } from '@hatiolab/things-scene'\n\nimport { createFigureResolver } from '../src/figure-provider.js'\n\n/**\n * The path a published figure takes to a board (ADR-0063 결정 2).\n *\n * things-scene holds the seat and knows nothing about figures; this package fills it. What is\n * checked here is the part that belongs to the board: which names are asked about, what happens to\n * the answer, and how \"nobody published this\" stays apart from \"could not ask\".\n *\n * The blueprint below is the shape figure-model emits — `registerFigure` rejects anything it cannot\n * read, so a fixture that drifts fails loudly here instead of drawing nothing at runtime.\n */\n\nconst BLUEPRINT_VERSION = 1\n\nconst blueprintOf = (type: string) => ({\n version: BLUEPRINT_VERSION,\n type,\n base: { x: 100, y: 100, z: 100 },\n groups: [\n {\n materialKey: 'palette.dark|metal|f|-|-|-',\n material: { token: 'palette.dark', preset: 'metal', flatShading: true },\n animated: false,\n members: [\n {\n name: 'body',\n primitive: 'cube',\n geometryKey: 'cube|100x100x100',\n transform: { position: { x: 0, y: 0, z: 0 }, size: { x: 100, y: 100, z: 100 } },\n sizing: 'scale',\n triangles: 12\n }\n ]\n }\n ]\n})\n\n/**\n * The other thing the door can hand over: a V3 asset under a `format` tag (ADR-0087 decision 5).\n *\n * Written out rather than built with figure-model, for the same reason the blueprint above is: this package\n * does not read either payload, it passes each to the scene's own registration, and a fixture that drifts from\n * the format should fail loudly here rather than draw nothing at runtime. One box standing on the floor, with\n * the volume it takes up declared.\n */\nconst v3AssetOf = (type: string) => ({\n version: 'figure-v3-asset-1',\n name: type,\n document: {\n version: 'figure-v3-prototype-1',\n model: {\n version: 'r3-spatial-1',\n inputs: [],\n constants: [\n { id: 'w', unit: 'mm', value: 100 },\n { id: 'half', unit: 'mm', value: 50 },\n { id: 'minusHalf', unit: 'mm', value: -50 },\n { id: 'zero', unit: 'mm', value: 0 },\n { id: 'a0', unit: 'deg', value: 0 }\n ],\n nodes: [\n { id: 'body.shape', op: 'box-shape@1', args: ['w', 'w', 'w'], outputs: { shape: 'body.s' }, params: { frame: 'body.local' } },\n {\n id: 'body.pose',\n op: 'rigid@1',\n args: ['zero', 'half', 'zero', 'a0', 'a0', 'a0'],\n outputs: { pose: 'body.p' },\n params: { from: 'body.local', to: 'asset' }\n },\n { id: 'body', op: 'place@1', args: ['body.s', 'body.p'], outputs: { placed: 'body.placed' } }\n ]\n },\n capabilities: { version: 'r3-capability-3', assetFrame: 'asset', bindings: [] }\n },\n designInputs: {},\n stateDefaults: {},\n palette: { 'palette.primary': '#477b9e' },\n appearance: [{ target: 'body', material: { token: 'palette.primary' } }],\n occupancy: {\n bounds: { x: { min: 'minusHalf', max: 'half' }, y: { min: 'zero', max: 'w' }, z: { min: 'minusHalf', max: 'half' } },\n placement: 'floor'\n }\n})\n\nconst v3PayloadOf = (type: string) => ({ format: 'v3' as const, type, asset: v3AssetOf(type) })\n\n/** Records what was asked for, and answers from a table. */\nfunction fetcherOf(published: Record<string, unknown>, failing: string[] = []) {\n const asked: string[] = []\n\n return {\n asked,\n fetch: async (type: string) => {\n asked.push(type)\n if (failing.includes(type)) {\n throw new Error(`no route to the door (${type})`)\n }\n return published[type] ?? null\n }\n }\n}\n\ndescribe('figure 공급자 — 보드가 발행된 Figure 를 받는 길', () => {\n afterEach(() => {\n resetFigures()\n resetFiguresV3()\n })\n\n it('대문자 이름만 서버에 묻는다 — 씬의 기본 type 과 오타는 문을 지나지 않는다', async () => {\n const { asked, fetch } = fetcherOf({ EQ_MIXER: blueprintOf('EQ_MIXER') })\n\n await createFigureResolver(fetch)(['EQ_MIXER', 'rect', 'Conveyor', 'text', 'VEH_AGV_01'])\n\n expect(asked).to.deep.equal(['EQ_MIXER', 'VEH_AGV_01'])\n })\n\n it('이미 등록된 type 은 묻지 않는다 — 그리고 있는 컴포넌트를 figure 가 덮지 않는다', async () => {\n const { asked, fetch } = fetcherOf({ EQ_TAKEN: blueprintOf('EQ_TAKEN') })\n\n /* Somebody else already draws this name — a plugin, an app's own component. */\n Component.register('EQ_TAKEN', class Taken extends Component {} as never)\n\n await createFigureResolver(fetch)(['EQ_TAKEN'])\n\n expect(asked).to.deep.equal([])\n expect(hasFigure('EQ_TAKEN')).to.equal(false)\n })\n\n it('받은 청사진을 등록한다 — 도면은 type 만 적으면 된다', async () => {\n const { fetch } = fetcherOf({ EQ_REGISTERED: blueprintOf('EQ_REGISTERED') })\n\n await createFigureResolver(fetch)(['EQ_REGISTERED'])\n\n expect(hasFigure('EQ_REGISTERED')).to.equal(true)\n /* The component type is registered with it — the model writes `type` and nothing else. */\n expect(Component.register('EQ_REGISTERED')).to.not.equal(undefined)\n })\n\n it('V3 자산은 V3 문으로 등록한다 — 보드는 그것을 읽지 않고 씬에 넘긴다', async () => {\n const { fetch } = fetcherOf({ EQ_V3: v3PayloadOf('EQ_V3') })\n\n await createFigureResolver(fetch)(['EQ_V3'])\n\n expect(hasFigureV3('EQ_V3')).to.equal(true)\n expect(hasFigure('EQ_V3')).to.equal(false)\n /* 도면은 판을 모른다 — 두 판 다 type 하나만 적으면 그려진다. */\n expect(Component.register('EQ_V3')).to.not.equal(undefined)\n })\n\n it('한 도면에 두 판이 섞여 있어도 각자의 문으로 들어간다', async () => {\n const { fetch } = fetcherOf({ EQ_OLD: blueprintOf('EQ_OLD'), EQ_NEW: v3PayloadOf('EQ_NEW') })\n\n await createFigureResolver(fetch)(['EQ_OLD', 'EQ_NEW'])\n\n expect(hasFigure('EQ_OLD')).to.equal(true)\n expect(hasFigureV3('EQ_NEW')).to.equal(true)\n })\n\n it('읽을 수 없는 V3 자산은 그 type 만 실패로 남는다', async () => {\n const broken = v3PayloadOf('EQ_V3_BROKEN')\n broken.asset.appearance = []\n const { fetch } = fetcherOf({ EQ_BESIDE_V3: blueprintOf('EQ_BESIDE_V3'), EQ_V3_BROKEN: broken })\n\n let message = ''\n try {\n await createFigureResolver(fetch)(['EQ_BESIDE_V3', 'EQ_V3_BROKEN'])\n } catch (error) {\n message = error instanceof Error ? error.message : String(error)\n }\n\n expect(message).to.contain('EQ_V3_BROKEN')\n expect(hasFigure('EQ_BESIDE_V3')).to.equal(true)\n expect(hasFigureV3('EQ_V3_BROKEN')).to.equal(false)\n })\n\n it('발행된 것이 없으면 조용히 자리표시로 남는다 — 거절이 아니다', async () => {\n const { fetch } = fetcherOf({})\n\n let rejected = false\n try {\n await createFigureResolver(fetch)(['EQ_NOT_PUBLISHED'])\n } catch {\n rejected = true\n }\n\n expect(rejected).to.equal(false)\n expect(hasFigure('EQ_NOT_PUBLISHED')).to.equal(false)\n })\n\n it('물을 수 없으면 거절한다. 그때도 답을 받은 것은 등록된다', async () => {\n const { fetch } = fetcherOf({ EQ_BESIDE_BROKEN: blueprintOf('EQ_BESIDE_BROKEN') }, ['EQ_BROKEN'])\n\n let message = ''\n try {\n await createFigureResolver(fetch)(['EQ_BESIDE_BROKEN', 'EQ_BROKEN'])\n } catch (error) {\n message = error instanceof Error ? error.message : String(error)\n }\n\n expect(message).to.contain('EQ_BROKEN')\n expect(message).to.not.contain('EQ_BESIDE_BROKEN')\n expect(hasFigure('EQ_BESIDE_BROKEN')).to.equal(true)\n })\n\n it('읽을 수 없는 청사진은 그 type 만 실패로 남는다 — 다른 type 을 막지 않는다', async () => {\n const { fetch } = fetcherOf({\n EQ_BESIDE_MALFORMED: blueprintOf('EQ_BESIDE_MALFORMED'),\n EQ_MALFORMED: { version: BLUEPRINT_VERSION, type: 'EQ_MALFORMED' }\n })\n\n let message = ''\n try {\n await createFigureResolver(fetch)(['EQ_BESIDE_MALFORMED', 'EQ_MALFORMED'])\n } catch (error) {\n message = error instanceof Error ? error.message : String(error)\n }\n\n expect(message).to.contain('EQ_MALFORMED')\n expect(hasFigure('EQ_BESIDE_MALFORMED')).to.equal(true)\n expect(hasFigure('EQ_MALFORMED')).to.equal(false)\n })\n\n it('씬의 자리에 앉아 동작한다 — 모델의 모르는 type 이 해석 뒤에 남지 않는다', async () => {\n const { fetch } = fetcherOf({ EQ_ON_A_BOARD: blueprintOf('EQ_ON_A_BOARD') })\n const takeBack = registerTypeResolver(createFigureResolver(fetch))\n\n try {\n const model = {\n type: 'container',\n components: [\n { type: 'EQ_ON_A_BOARD', left: 0, top: 0, width: 100, height: 100 },\n { type: 'EQ_NEVER_PUBLISHED', left: 0, top: 0, width: 100, height: 100 }\n ]\n }\n\n const resolution = await resolveUnknownTypes(model)\n\n expect(resolution.unresolved).to.deep.equal(['EQ_NEVER_PUBLISHED'])\n expect(resolution.failed).to.deep.equal([])\n expect(hasFigure('EQ_ON_A_BOARD')).to.equal(true)\n } finally {\n takeBack()\n }\n })\n})\n"]}
|
|
1
|
+
{"version":3,"file":"figure-provider.test.js","sourceRoot":"","sources":["../../test/figure-provider.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing'\n\nimport { Component, hasFigureV3, registerTypeResolver, resetFiguresV3, resolveUnknownTypes } from '@hatiolab/things-scene'\n\nimport { createFigureResolver } from '../src/figure-provider.js'\n\n/**\n * The path a published figure takes to a board (ADR-0063 결정 2).\n *\n * things-scene holds the seat and knows nothing about figures; this package fills it. What is\n * checked here is the part that belongs to the board: which names are asked about, what happens to\n * the answer, and how \"nobody published this\" stays apart from \"could not ask\".\n *\n * The payload below is written out rather than built with figure-model: this package does not read it, it passes it\n * to the scene's own registration, and a fixture that drifts from the format should fail loudly here rather than\n * draw nothing at runtime.\n */\n\n/** What the door hands over: a figure asset under a `format` tag. One box standing on the floor, its room declared. */\nconst v3AssetOf = (type: string) => ({\n version: 'figure-v3-asset-1',\n name: type,\n document: {\n version: 'figure-v3-prototype-1',\n model: {\n version: 'r3-spatial-1',\n inputs: [],\n constants: [\n { id: 'w', unit: 'mm', value: 100 },\n { id: 'half', unit: 'mm', value: 50 },\n { id: 'minusHalf', unit: 'mm', value: -50 },\n { id: 'zero', unit: 'mm', value: 0 },\n { id: 'a0', unit: 'deg', value: 0 }\n ],\n nodes: [\n { id: 'body.shape', op: 'box-shape@1', args: ['w', 'w', 'w'], outputs: { shape: 'body.s' }, params: { frame: 'body.local' } },\n {\n id: 'body.pose',\n op: 'rigid@1',\n args: ['zero', 'half', 'zero', 'a0', 'a0', 'a0'],\n outputs: { pose: 'body.p' },\n params: { from: 'body.local', to: 'asset' }\n },\n { id: 'body', op: 'place@1', args: ['body.s', 'body.p'], outputs: { placed: 'body.placed' } }\n ]\n },\n capabilities: { version: 'r3-capability-3', assetFrame: 'asset', bindings: [] }\n },\n designInputs: {},\n stateDefaults: {},\n palette: { 'palette.primary': '#477b9e' },\n appearance: [{ target: 'body', material: { token: 'palette.primary' } }],\n occupancy: {\n bounds: { x: { min: 'minusHalf', max: 'half' }, y: { min: 'zero', max: 'w' }, z: { min: 'minusHalf', max: 'half' } },\n placement: 'floor'\n }\n})\n\nconst v3PayloadOf = (type: string) => ({ format: 'v3' as const, type, asset: v3AssetOf(type) })\n\n/** Records what was asked for, and answers from a table. */\nfunction fetcherOf(published: Record<string, unknown>, failing: string[] = []) {\n const asked: string[] = []\n\n return {\n asked,\n fetch: async (type: string) => {\n asked.push(type)\n if (failing.includes(type)) {\n throw new Error(`no route to the door (${type})`)\n }\n return (published[type] ?? null) as never\n }\n }\n}\n\ndescribe('figure 공급자 — 보드가 발행된 Figure 를 받는 길', () => {\n afterEach(() => {\n resetFiguresV3()\n })\n\n it('대문자 이름만 서버에 묻는다 — 씬의 기본 type 과 오타는 문을 지나지 않는다', async () => {\n const { asked, fetch } = fetcherOf({ EQ_MIXER: v3PayloadOf('EQ_MIXER') })\n\n await createFigureResolver(fetch)(['EQ_MIXER', 'rect', 'Conveyor', 'text', 'VEH_AGV_01'])\n\n expect(asked).to.deep.equal(['EQ_MIXER', 'VEH_AGV_01'])\n })\n\n it('이미 등록된 type 은 묻지 않는다 — 그리고 있는 컴포넌트를 figure 가 덮지 않는다', async () => {\n const { asked, fetch } = fetcherOf({ EQ_TAKEN: v3PayloadOf('EQ_TAKEN') })\n\n /* Somebody else already draws this name — a plugin, an app's own component. */\n Component.register('EQ_TAKEN', class Taken extends Component {} as never)\n\n await createFigureResolver(fetch)(['EQ_TAKEN'])\n\n expect(asked).to.deep.equal([])\n expect(hasFigureV3('EQ_TAKEN')).to.equal(false)\n })\n\n it('받은 자산을 등록한다 — 도면은 type 만 적으면 된다', async () => {\n const { fetch } = fetcherOf({ EQ_REGISTERED: v3PayloadOf('EQ_REGISTERED') })\n\n await createFigureResolver(fetch)(['EQ_REGISTERED'])\n\n expect(hasFigureV3('EQ_REGISTERED')).to.equal(true)\n /* The component type is registered with it — the model writes `type` and nothing else. */\n expect(Component.register('EQ_REGISTERED')).to.not.equal(undefined)\n })\n\n it('자산은 씬의 문으로 등록한다 — 보드는 그것을 읽지 않고 씬에 넘긴다', async () => {\n const { fetch } = fetcherOf({ EQ_V3: v3PayloadOf('EQ_V3') })\n\n await createFigureResolver(fetch)(['EQ_V3'])\n\n expect(hasFigureV3('EQ_V3')).to.equal(true)\n /* 도면은 type 하나만 적으면 그려진다. */\n expect(Component.register('EQ_V3')).to.not.equal(undefined)\n })\n\n it('자산이 아닌 것(format 이 v3 가 아닌 답)은 그 type 만 실패로 남는다 — 옛 청사진을 그리지 않는다', async () => {\n const { fetch } = fetcherOf({ EQ_NEW: v3PayloadOf('EQ_NEW'), EQ_OLD: { version: 1, type: 'EQ_OLD', groups: [] } })\n\n let message = ''\n try {\n await createFigureResolver(fetch)(['EQ_NEW', 'EQ_OLD'])\n } catch (error) {\n message = error instanceof Error ? error.message : String(error)\n }\n\n expect(message).to.contain('not a figure asset: EQ_OLD')\n expect(hasFigureV3('EQ_NEW')).to.equal(true)\n expect(hasFigureV3('EQ_OLD')).to.equal(false)\n })\n\n it('읽을 수 없는 V3 자산은 그 type 만 실패로 남는다', async () => {\n const broken = v3PayloadOf('EQ_V3_BROKEN')\n broken.asset.appearance = []\n const { fetch } = fetcherOf({ EQ_BESIDE_V3: v3PayloadOf('EQ_BESIDE_V3'), EQ_V3_BROKEN: broken })\n\n let message = ''\n try {\n await createFigureResolver(fetch)(['EQ_BESIDE_V3', 'EQ_V3_BROKEN'])\n } catch (error) {\n message = error instanceof Error ? error.message : String(error)\n }\n\n expect(message).to.contain('EQ_V3_BROKEN')\n expect(hasFigureV3('EQ_BESIDE_V3')).to.equal(true)\n expect(hasFigureV3('EQ_V3_BROKEN')).to.equal(false)\n })\n\n it('발행된 것이 없으면 조용히 자리표시로 남는다 — 거절이 아니다', async () => {\n const { fetch } = fetcherOf({})\n\n let rejected = false\n try {\n await createFigureResolver(fetch)(['EQ_NOT_PUBLISHED'])\n } catch {\n rejected = true\n }\n\n expect(rejected).to.equal(false)\n expect(hasFigureV3('EQ_NOT_PUBLISHED')).to.equal(false)\n })\n\n it('물을 수 없으면 거절한다. 그때도 답을 받은 것은 등록된다', async () => {\n const { fetch } = fetcherOf({ EQ_BESIDE_BROKEN: v3PayloadOf('EQ_BESIDE_BROKEN') }, ['EQ_BROKEN'])\n\n let message = ''\n try {\n await createFigureResolver(fetch)(['EQ_BESIDE_BROKEN', 'EQ_BROKEN'])\n } catch (error) {\n message = error instanceof Error ? error.message : String(error)\n }\n\n expect(message).to.contain('EQ_BROKEN')\n expect(message).to.not.contain('EQ_BESIDE_BROKEN')\n expect(hasFigureV3('EQ_BESIDE_BROKEN')).to.equal(true)\n })\n\n it('읽을 수 없는 자산은 그 type 만 실패로 남는다 — 다른 type 을 막지 않는다', async () => {\n const { fetch } = fetcherOf({\n EQ_BESIDE_MALFORMED: v3PayloadOf('EQ_BESIDE_MALFORMED'),\n EQ_MALFORMED: { format: 'v3', type: 'EQ_MALFORMED', asset: { version: 'figure-v3-asset-1' } }\n })\n\n let message = ''\n try {\n await createFigureResolver(fetch)(['EQ_BESIDE_MALFORMED', 'EQ_MALFORMED'])\n } catch (error) {\n message = error instanceof Error ? error.message : String(error)\n }\n\n expect(message).to.contain('EQ_MALFORMED')\n expect(hasFigureV3('EQ_BESIDE_MALFORMED')).to.equal(true)\n expect(hasFigureV3('EQ_MALFORMED')).to.equal(false)\n })\n\n it('씬의 자리에 앉아 동작한다 — 모델의 모르는 type 이 해석 뒤에 남지 않는다', async () => {\n const { fetch } = fetcherOf({ EQ_ON_A_BOARD: v3PayloadOf('EQ_ON_A_BOARD') })\n const takeBack = registerTypeResolver(createFigureResolver(fetch))\n\n try {\n const model = {\n type: 'container',\n components: [\n { type: 'EQ_ON_A_BOARD', left: 0, top: 0, width: 100, height: 100 },\n { type: 'EQ_NEVER_PUBLISHED', left: 0, top: 0, width: 100, height: 100 }\n ]\n }\n\n const resolution = await resolveUnknownTypes(model)\n\n expect(resolution.unresolved).to.deep.equal(['EQ_NEVER_PUBLISHED'])\n expect(resolution.failed).to.deep.equal([])\n expect(hasFigureV3('EQ_ON_A_BOARD')).to.equal(true)\n } finally {\n takeBack()\n }\n })\n})\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { expect, fixture, html } from '@open-wc/testing';
|
|
2
2
|
import { executeServerCommand } from '@web/test-runner-commands';
|
|
3
|
-
import { registerTypeResolver,
|
|
3
|
+
import { registerTypeResolver, resetFiguresV3 } from '@hatiolab/things-scene';
|
|
4
4
|
import { createResolvedScene } from '../src/resolved-scene.js';
|
|
5
5
|
import '../src/ox-board-preview.js';
|
|
6
6
|
/**
|
|
@@ -40,7 +40,7 @@ describe('모르는 type 이 없으면 아무에게도 묻지 않는다', () =>
|
|
|
40
40
|
});
|
|
41
41
|
afterEach(() => {
|
|
42
42
|
takeBack();
|
|
43
|
-
|
|
43
|
+
resetFiguresV3();
|
|
44
44
|
});
|
|
45
45
|
it('씬의 기본 type 만 있는 보드는 공급자를 부르지 않는다', async () => {
|
|
46
46
|
var _a;
|
|
@@ -60,7 +60,7 @@ describe('기다리는 동안 화면이 바뀌면 늦게 온 씬은 들어서지
|
|
|
60
60
|
let takeBack;
|
|
61
61
|
afterEach(() => {
|
|
62
62
|
takeBack === null || takeBack === void 0 ? void 0 : takeBack();
|
|
63
|
-
|
|
63
|
+
resetFiguresV3();
|
|
64
64
|
});
|
|
65
65
|
it('앞의 모델을 풀고 돌아왔을 때 미리보기가 다른 모델을 들고 있으면 그 씬은 버린다', async () => {
|
|
66
66
|
/* The first model's type resolves slowly; the second one has nothing to resolve. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolved-scene.test.js","sourceRoot":"","sources":["../../test/resolved-scene.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAExD,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAEhE,OAAO,EAAE,oBAAoB,EAAE,
|
|
1
|
+
{"version":3,"file":"resolved-scene.test.js","sourceRoot":"","sources":["../../test/resolved-scene.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture, html } from '@open-wc/testing'\nimport type { LitElement } from 'lit'\nimport { executeServerCommand } from '@web/test-runner-commands'\n\nimport { registerTypeResolver, resetFiguresV3 } from '@hatiolab/things-scene'\n\nimport { createResolvedScene } from '../src/resolved-scene.js'\nimport '../src/ox-board-preview.js'\n\n/**\n * The one door a model goes through to become a scene (ADR-0063 결정 2).\n *\n * Three things are counted here, and each of them is something that goes wrong quietly otherwise:\n * a scene built somewhere else never resolves its types, a board of ordinary components pays for a\n * server round trip it does not need, and a scene that was waiting lands on a viewer that has\n * already moved on.\n */\n\nconst KNOWN_MODEL = {\n type: 'container',\n components: [{ type: 'rect', left: 0, top: 0, width: 100, height: 100 }]\n}\n\nconst modelWith = (types: string[]) => ({\n type: 'container',\n components: types.map((type, index) => ({ type, left: index * 10, top: 0, width: 100, height: 100 }))\n})\n\ndescribe('씬을 만드는 문은 하나다', () => {\n it('things-scene 의 create 를 읽는 파일은 resolved-scene.ts 하나뿐이다', async () => {\n /*\n * 세는 범위는 이 꾸러미와 `@operato/process` 다 — 문은 여기 있고 그 꾸러미가 둘째 소비자다.\n * 이 꾸러미만 보던 규칙은 옆에 씬 둘이 서 있는 동안 「문은 하나」라고 답하고 있었다.\n */\n const files = await executeServerCommand<unknown, string[]>('files-importing-scene-create')\n\n expect(files).to.deep.equal(['src/resolved-scene.ts'])\n })\n})\n\ndescribe('모르는 type 이 없으면 아무에게도 묻지 않는다', () => {\n let asked: string[][]\n let takeBack: () => void\n\n beforeEach(() => {\n asked = []\n takeBack = registerTypeResolver(async types => {\n asked.push([...types])\n })\n })\n\n afterEach(() => {\n takeBack()\n resetFiguresV3()\n })\n\n it('씬의 기본 type 만 있는 보드는 공급자를 부르지 않는다', async () => {\n const scene = await createResolvedScene({ model: KNOWN_MODEL })\n\n expect(asked).to.deep.equal([])\n expect(scene).to.not.equal(null)\n\n scene?.release?.()\n })\n\n it('모르는 type 이 있을 때만 부른다', async () => {\n const scene = await createResolvedScene({ model: modelWith(['rect', 'EQ_ASKED_ABOUT']) })\n\n expect(asked).to.deep.equal([['EQ_ASKED_ABOUT']])\n\n scene?.release?.()\n })\n})\n\ndescribe('기다리는 동안 화면이 바뀌면 늦게 온 씬은 들어서지 않는다', () => {\n let takeBack: () => void\n\n afterEach(() => {\n takeBack?.()\n resetFiguresV3()\n })\n\n it('앞의 모델을 풀고 돌아왔을 때 미리보기가 다른 모델을 들고 있으면 그 씬은 버린다', async () => {\n /* The first model's type resolves slowly; the second one has nothing to resolve. */\n let releaseFirst: () => void = () => {}\n const firstResolved = new Promise<void>(resolve => {\n releaseFirst = resolve\n })\n\n takeBack = registerTypeResolver(async types => {\n if (types.includes('EQ_SLOW')) {\n await firstResolved\n }\n })\n\n const element = (await fixture(html`<ox-board-preview></ox-board-preview>`)) as LitElement & {\n boardModel: unknown\n _scene: { root: { components: unknown[] } } | null\n }\n\n element.boardModel = modelWith(['EQ_SLOW'])\n await element.updateComplete\n\n /* The second model arrives while the first one is still waiting on its type. */\n element.boardModel = modelWith(['rect', 'rect'])\n await element.updateComplete\n\n releaseFirst()\n await new Promise(resolve => setTimeout(resolve, 0))\n\n expect(element._scene).to.not.equal(null)\n expect(element._scene!.root.components.length).to.equal(2)\n })\n})\n"]}
|