@things-factory/figure-ui 10.1.145 → 10.1.146
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/client/modeller/refusals.ts +16 -7
- package/dist-client/modeller/refusals.js +18 -8
- package/dist-client/modeller/refusals.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/test/align.test.ts +2 -2
- package/test/browser/modeller-page.js +3 -3
- package/test/browser/modeller.check.mjs +93 -51
- package/test/cavity-drawing.test.ts +1 -1
- package/test/clip-consent.test.ts +1 -1
- package/test/clips.test.ts +1 -1
- package/test/command-preview.test.ts +5 -5
- package/test/controls.test.ts +1 -1
- package/test/fasten-drawing.test.ts +1 -1
- package/test/fields.test.ts +1 -1
- package/test/figure-assistant-surface.test.ts +1 -1
- package/test/first-minute.test.ts +1 -1
- package/test/first-save-suggest.test.ts +1 -1
- package/test/gizmo.test.ts +1 -1
- package/test/layout.test.ts +2 -2
- package/test/links.test.ts +1 -1
- package/test/maturity.test.ts +1 -1
- package/test/parts-wiring.test.ts +3 -3
- package/test/properties.test.ts +1 -1
- package/test/proposal-record.test.ts +1 -1
- package/test/refusals.test.ts +40 -0
- package/test/release-entry.test.ts +1 -1
- package/test/revert-choice.test.ts +1 -1
- package/test/revert-keeping.test.ts +1 -1
- package/test/revision.test.ts +7 -4
- package/test/save-landed.test.ts +2 -2
- package/test/section-editor.test.ts +3 -3
- package/test/section-panel.test.ts +1 -1
- package/test/verdict.test.ts +1 -1
- package/translations/en.json +4 -1
- package/translations/es.json +991 -0
- package/translations/ja.json +3 -0
- package/translations/ko.json +3 -0
- package/translations/ms.json +3 -0
- package/translations/zh.json +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/figure-ui",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.146",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"@things-factory:registry": "https://registry.npmjs.org"
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"@operato/shell": "^10.0.0",
|
|
41
41
|
"@operato/styles": "^10.0.0",
|
|
42
42
|
"@operato/utils": "^10.0.0",
|
|
43
|
-
"@things-factory/ai-assistant": "^10.1.
|
|
44
|
-
"@things-factory/figure-service": "^10.1.
|
|
43
|
+
"@things-factory/ai-assistant": "^10.1.146",
|
|
44
|
+
"@things-factory/figure-service": "^10.1.146",
|
|
45
45
|
"three": "^0.185.1"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "7aeb3de935ec913d271866b27d931b2278259c13"
|
|
48
48
|
}
|
package/test/align.test.ts
CHANGED
|
@@ -91,7 +91,7 @@ test('a tilted part is told it touches by a point or an edge, a ball by its lowe
|
|
|
91
91
|
const editor = read('../client/modeller/editor.ts')
|
|
92
92
|
assert.ok(editor.includes("const told = contact === 'tilted' || contact === 'extreme' ? `-${contact}` : ''"), 'the tooltip follows the contact')
|
|
93
93
|
assert.ok(editor.includes("this.notice = i18next.t(`figure.text.aligned-${contact}-${asset.placement ?? 'floor'}`)"), 'and so does the word after')
|
|
94
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) {
|
|
94
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) {
|
|
95
95
|
const t = JSON.parse(read(`../translations/${lang}.json`))
|
|
96
96
|
for (const placement of ['floor', 'ceiling', 'wall'])
|
|
97
97
|
for (const contact of ['tilted', 'extreme'])
|
|
@@ -106,7 +106,7 @@ test('a tilted part is told it touches by a point or an edge, a ball by its lowe
|
|
|
106
106
|
test('the size gizmo\'s reason names what the placement keeps, in every language', () => {
|
|
107
107
|
const editor = read('../client/modeller/editor.ts')
|
|
108
108
|
assert.equal(editor.match(/'figure\.text\.gizmo-size-turned-fastening'[^\n]*extreme: this\.keptExtremeWord\(\)/g)?.length, 3, 'every place it is said')
|
|
109
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) {
|
|
109
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) {
|
|
110
110
|
const t = JSON.parse(read(`../translations/${lang}.json`))
|
|
111
111
|
assert.ok(t['figure.text.gizmo-size-turned-fastening'].includes('{extreme}'), lang)
|
|
112
112
|
for (const placement of ['floor', 'ceiling', 'wall', 'center']) assert.ok(t[`figure.label.kept-extreme-${placement}`], `${lang} ${placement}`)
|
|
@@ -172,9 +172,9 @@ window.probe = {
|
|
|
172
172
|
ids() {
|
|
173
173
|
return editor().session.read().geometry.map(g => g.id)
|
|
174
174
|
},
|
|
175
|
-
/** The
|
|
176
|
-
|
|
177
|
-
return deep('
|
|
175
|
+
/** The way the fastening drawing shows the part fastened: meet, flush or centre. */
|
|
176
|
+
fastenMode() {
|
|
177
|
+
return deep('figure-fasten-drawing')[0]?.mode ?? null
|
|
178
178
|
},
|
|
179
179
|
/** The page point of an input by its aria-label, or of the part name field. */
|
|
180
180
|
nameField() {
|
|
@@ -133,9 +133,9 @@ describe('the gizmo on a part the modeller made (own numbers)', () => {
|
|
|
133
133
|
const after = await state()
|
|
134
134
|
assert.equal(after.revision, start.revision + 1, 'one step of the history')
|
|
135
135
|
assert.equal(after.selected, 'box')
|
|
136
|
-
assert.ok(now.dimensions.
|
|
137
|
-
assert.equal(now.dimensions.
|
|
138
|
-
assert.equal(now.dimensions.
|
|
136
|
+
assert.ok(now.dimensions['size.x'] > was.dimensions['size.x'] * 1.2, `grew: ${was.dimensions['size.x']} → ${now.dimensions['size.x']}`)
|
|
137
|
+
assert.equal(now.dimensions.length, was.dimensions.length)
|
|
138
|
+
assert.equal(now.dimensions['size.z'], was.dimensions['size.z'])
|
|
139
139
|
})
|
|
140
140
|
|
|
141
141
|
test('rotate: one step, the part turns about the handle axis and keeps the turn', async () => {
|
|
@@ -182,7 +182,7 @@ describe('the gizmo on a part placed by worked-out rules', () => {
|
|
|
182
182
|
assert.equal(after.error, '', 'not refused')
|
|
183
183
|
assert.equal(after.revision, start.revision + 1, 'one step of the history')
|
|
184
184
|
assert.equal(after.selected, 'pedestal')
|
|
185
|
-
assert.ok(now.dimensions.
|
|
185
|
+
assert.ok(now.dimensions['radius.x'] > was.dimensions['radius.x'] * 1.1, `wider: ${was.dimensions['radius.x']} → ${now.dimensions['radius.x']}`)
|
|
186
186
|
assert.equal(now.dimensions.length, was.dimensions.length)
|
|
187
187
|
})
|
|
188
188
|
|
|
@@ -219,7 +219,7 @@ describe('the gizmo on a part placed by worked-out rules', () => {
|
|
|
219
219
|
describe('a pose slider dragged with the mouse (user 2026-09-24: the arm jumped on release)', () => {
|
|
220
220
|
before(async () => {
|
|
221
221
|
await mount('six-axis')
|
|
222
|
-
await click(await read(() => probe.tab('figure.label.motion')))
|
|
222
|
+
await click(await read(() => probe.tab('figure.label.tab-short-motion')))
|
|
223
223
|
})
|
|
224
224
|
|
|
225
225
|
/** Presses a slider's thumb, moves it `units`, reads the canvas while still held, then releases. */
|
|
@@ -328,33 +328,50 @@ describe('the fastening form belongs to the part it is for', () => {
|
|
|
328
328
|
})
|
|
329
329
|
|
|
330
330
|
/* The select whose label (the i18n key: the check loads no translations) is `label`, in the parts' links form. */
|
|
331
|
-
const
|
|
331
|
+
const select = label =>
|
|
332
332
|
page().evaluateHandle(label => {
|
|
333
333
|
const deep = (root = document) => [...root.querySelectorAll('select')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
334
334
|
return deep().find(s => s.previousElementSibling?.textContent?.trim() === label) ?? null
|
|
335
335
|
}, label)
|
|
336
|
+
/* Where the part sits across the target face, as the face drawing holds it (the drawing sets it since 2026-10-02). */
|
|
337
|
+
const placing = () =>
|
|
338
|
+
read(() => {
|
|
339
|
+
const deep = (root = document) => [...root.querySelectorAll('figure-fasten-drawing')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
340
|
+
const drawing = deep()[0]
|
|
341
|
+
return drawing ? { target: drawing.target, x: drawing.align.x ?? 'centre', mm: drawing.alignMm.x ?? 0 } : null
|
|
342
|
+
})
|
|
336
343
|
|
|
337
344
|
test('a choice made for one part is not carried to the next part picked; the next opens on its own fastening (셔터 커튼 2026-09-25, walk 1 2026-09-26)', async () => {
|
|
338
|
-
/* The placing across the target face is asked once there is a target: the form starts with none. */
|
|
339
345
|
await click(await read(() => probe.row('forearm')))
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
await
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
346
|
+
assert.equal(await (await select('figure.label.target-part')).evaluate(s => s?.value), 'elbow', 'the forearm opens on what it is fastened to')
|
|
347
|
+
/* Placed off centre on the elbow's face, as a hand does: the face drawing focused, the arrows. */
|
|
348
|
+
const face = await read(() => {
|
|
349
|
+
const deep = (root = document) => [...root.querySelectorAll('figure-fasten-drawing')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
350
|
+
const rect = deep()[0]?.shadowRoot.querySelector('div[where] svg > rect')
|
|
351
|
+
if (!rect) return null
|
|
352
|
+
rect.scrollIntoView({ block: 'center' })
|
|
353
|
+
const r = rect.getBoundingClientRect()
|
|
354
|
+
return { x: r.left + 4, y: r.top + 4 }
|
|
355
|
+
})
|
|
356
|
+
await click(face)
|
|
357
|
+
await page().keyboard.down('Shift')
|
|
358
|
+
for (let i = 0; i < 3; i++) await page().keyboard.press('ArrowRight')
|
|
359
|
+
await page().keyboard.up('Shift')
|
|
360
|
+
await sleep(600)
|
|
361
|
+
const chosen = await placing()
|
|
362
|
+
assert.equal(chosen?.x, 'mm', `placed off centre for the forearm: ${JSON.stringify(chosen)}`)
|
|
363
|
+
assert.ok(chosen.mm > 0)
|
|
347
364
|
|
|
348
365
|
await click(await read(() => probe.row('elbow')))
|
|
349
366
|
/* The elbow opens on its own fastening (walk 1, 2026-09-26), not on the forearm's choices. */
|
|
350
367
|
const own = await read(() => {
|
|
351
368
|
const joint = JSON.parse(document.querySelector('figure-editor').session.source()).joints.find(j => j.body1.part === 'elbow')
|
|
352
|
-
return { target: joint.body0.part, x: joint.origin?.align?.x ?? 'centre' }
|
|
369
|
+
return { target: joint.body0.part, x: Object.keys(joint.origin?.align?.x ?? {})[0] ?? 'centre' }
|
|
353
370
|
})
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
assert.
|
|
371
|
+
assert.equal(await (await select('figure.label.target-part')).evaluate(s => s?.value), own.target, 'the elbow opens on what it is fastened to')
|
|
372
|
+
const next = await placing()
|
|
373
|
+
assert.equal(next?.target, own.target)
|
|
374
|
+
assert.equal(next.x, own.x, 'and on its own placing, not the forearm\'s')
|
|
358
375
|
})
|
|
359
376
|
})
|
|
360
377
|
|
|
@@ -410,11 +427,22 @@ describe('a figure made in the modeller is given its size from its parts (user 2
|
|
|
410
427
|
assert.equal(after.revision, start.revision + 1, 'one step')
|
|
411
428
|
assert.equal((await declared())['size.x'][1], 3000, 'the largest width is what was typed')
|
|
412
429
|
await type('size.x.min', '500')
|
|
413
|
-
assert.
|
|
430
|
+
assert.notEqual((await state()).error, '', 'the figure is 400 wide: a smallest of 500 is refused')
|
|
414
431
|
assert.equal((await declared())['size.x'][0] < 400, true, 'and the range is as it was')
|
|
415
432
|
assert.equal(await (await field('size.x.min')).evaluate(i => Number(i.value) < 400), true, 'the field shows the range it still has')
|
|
416
433
|
})
|
|
417
434
|
|
|
435
|
+
/*
|
|
436
|
+
The smallest refused is said with the figure's size (2026-10-11): figure-model refuses it with INSTANCE_SIZE_RANGE
|
|
437
|
+
「size.x: the authored size 400 mm must lie within 500…4000 mm」, which the screen once showed as the bare
|
|
438
|
+
figure.text.edit-refused. The page loads no translations: the numbers are the unit suite's to check, the sentence's key here.
|
|
439
|
+
*/
|
|
440
|
+
test('the smallest refused is said in its own words, with the figure\'s size, not as a bare refusal', async () => {
|
|
441
|
+
const said = (await state()).error
|
|
442
|
+
assert.notEqual(said, '', 'refused')
|
|
443
|
+
assert.notEqual(said, 'figure.text.edit-refused', `said in its own words: ${said}`)
|
|
444
|
+
})
|
|
445
|
+
|
|
418
446
|
test('a part follows the figure\'s width less a difference: chosen and typed, the width is the size less it', async () => {
|
|
419
447
|
await click(await read(() => probe.partOnPage('box')))
|
|
420
448
|
assert.equal((await state()).selected, 'box')
|
|
@@ -428,15 +456,19 @@ describe('a figure made in the modeller is given its size from its parts (user 2
|
|
|
428
456
|
what,
|
|
429
457
|
label
|
|
430
458
|
)
|
|
459
|
+
/* The way to make a side follow is folded under the advanced choice (2026-09-28): opened, as a hand does. */
|
|
460
|
+
await click(await read(() => {
|
|
461
|
+
const deep = (root = document) => [...root.querySelectorAll('details[resize-choice] > summary')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
462
|
+
const summary = deep()[0]
|
|
463
|
+
if (!summary) return null
|
|
464
|
+
summary.scrollIntoView({ block: 'center' })
|
|
465
|
+
const r = summary.getBoundingClientRect()
|
|
466
|
+
return { x: r.left + r.width / 2, y: r.top + r.height / 2 }
|
|
467
|
+
}))
|
|
431
468
|
const width = await handle('select', 'X')
|
|
432
469
|
assert.ok(await width.evaluate(s => !!s), 'the width can follow the figure size')
|
|
433
470
|
await width.select('instance:x')
|
|
434
471
|
await sleep(600)
|
|
435
|
-
/* Refusals and notices go to the app's notification: heard here as the event it is. */
|
|
436
|
-
await page().evaluate(() => {
|
|
437
|
-
window.__told = []
|
|
438
|
-
document.addEventListener('notify', e => window.__told.push(e.detail))
|
|
439
|
-
})
|
|
440
472
|
const plus = await handle('input')
|
|
441
473
|
assert.ok(await plus.evaluate(i => !!i), 'a following width offers its difference')
|
|
442
474
|
await plus.evaluate(i => i.scrollIntoView({ block: 'center' }))
|
|
@@ -446,17 +478,21 @@ describe('a figure made in the modeller is given its size from its parts (user 2
|
|
|
446
478
|
await sleep(600)
|
|
447
479
|
/* The declared sizes start at 1 mm: less 50 the box would be gone below 50, so the link is refused -- with the way on. */
|
|
448
480
|
assert.match((await state()).error, /figure.text.link-needs-smaller-range/, 'refused, said in words')
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
481
|
+
/* A refusal with a way on is asked in the app's prompt (OxPrompt): confirmed, the narrowed link is made. */
|
|
482
|
+
const asked = await read(() => {
|
|
483
|
+
const deep = (root = document) => [...root.querySelectorAll('ox-prompt')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
484
|
+
const prompt = deep().at(-1)
|
|
485
|
+
const confirm = prompt?.shadowRoot?.querySelector('#confirm')
|
|
486
|
+
if (!confirm) return null
|
|
487
|
+
const r = confirm.getBoundingClientRect()
|
|
488
|
+
return { text: prompt.text, label: confirm.textContent.trim(), x: r.left + r.width / 2, y: r.top + r.height / 2 }
|
|
452
489
|
})
|
|
453
|
-
assert.
|
|
454
|
-
assert.match(
|
|
455
|
-
|
|
456
|
-
await read(() => window.__told.at(-1).option.action.callback())
|
|
490
|
+
assert.match(asked?.text ?? '', /figure.text.link-needs-smaller-range/, 'asked in the app\'s prompt')
|
|
491
|
+
assert.match(asked.label, /figure.button.narrow-range-from/, 'the same link is offered with the sizes narrowed')
|
|
492
|
+
await click(asked)
|
|
457
493
|
await sleep(600)
|
|
458
494
|
assert.equal((await state()).error, '', 'taken')
|
|
459
|
-
assert.equal((await body('box')).dimensions.
|
|
495
|
+
assert.equal((await body('box')).dimensions['size.x'], 350, 'the figure\'s 400 less 50')
|
|
460
496
|
})
|
|
461
497
|
|
|
462
498
|
test('grown by one ratio, every part is made to follow it in one step, and the offer goes', async () => {
|
|
@@ -503,7 +539,7 @@ describe('a motion is changed where it is (2026-09-25)', () => {
|
|
|
503
539
|
})
|
|
504
540
|
|
|
505
541
|
test('the shoulder\'s range is changed from the motion tab: one step, the same joint, its new limit', async () => {
|
|
506
|
-
await click(await read(() => probe.tab('figure.label.motion')))
|
|
542
|
+
await click(await read(() => probe.tab('figure.label.tab-short-motion')))
|
|
507
543
|
const jointOf = part => read(part => (JSON.parse(document.querySelector('figure-editor').session.source()).joints ?? []).find(j => j.body1.part === part) ?? null, part)
|
|
508
544
|
const was = await jointOf('upper-arm')
|
|
509
545
|
assert.ok(was, 'the upper arm has its joint')
|
|
@@ -582,23 +618,25 @@ describe('the modeller made easier to find and to keep (2026-09-26 baseline)', (
|
|
|
582
618
|
await page().keyboard.press('Enter')
|
|
583
619
|
await sleep(600)
|
|
584
620
|
const now = await body('box')
|
|
585
|
-
|
|
586
|
-
assert.equal(now.dimensions.
|
|
621
|
+
/* The box is drawn up y from its start: its bottom is its centre plus the start. */
|
|
622
|
+
assert.equal(Math.round(now.t[1] + now.dimensions.start), 0, `bottom on the floor: centre ${now.t[1]}, starts at ${now.dimensions.start}`)
|
|
623
|
+
assert.equal(now.dimensions.length, 1000)
|
|
587
624
|
})
|
|
588
625
|
|
|
589
626
|
test('fastened to the floor, a leaf set off its frame stays where it stood', async () => {
|
|
590
627
|
await mount('door')
|
|
591
628
|
await click(await read(() => probe.row('leaf')))
|
|
592
629
|
const was = await body('leaf')
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
return { x: r.left + r.width / 2, y: r.top + r.height / 2 }
|
|
630
|
+
/* The floor is chosen as what the leaf is fastened to, in the fastening form; a new fastening waits for 붙이기. */
|
|
631
|
+
await click(await centre(await find('button', 'figure.button.fasten-start')))
|
|
632
|
+
const target = await page().evaluateHandle(() => {
|
|
633
|
+
const deep = (root = document) => [...root.querySelectorAll('select')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
634
|
+
return deep().find(s => s.previousElementSibling?.textContent?.trim() === 'figure.label.target-part') ?? null
|
|
599
635
|
})
|
|
600
|
-
await
|
|
601
|
-
|
|
636
|
+
assert.ok(await target.evaluate(s => !!s), 'the form asks what the leaf is fastened to')
|
|
637
|
+
await target.select('__plane')
|
|
638
|
+
await sleep(500)
|
|
639
|
+
const fasten = await find('button[act]', 'figure.button.fasten')
|
|
602
640
|
const start = await state()
|
|
603
641
|
await click(await centre(fasten))
|
|
604
642
|
const after = await state()
|
|
@@ -611,7 +649,7 @@ describe('the modeller made easier to find and to keep (2026-09-26 baseline)', (
|
|
|
611
649
|
test('a part is shown while a control is at least a value: chosen in the motion tab, one step each, drawn or not', async () => {
|
|
612
650
|
await mount('arm')
|
|
613
651
|
await click(await read(() => probe.row('upper-arm')))
|
|
614
|
-
await click(await read(() => probe.tab('figure.label.motion')))
|
|
652
|
+
await click(await read(() => probe.tab('figure.label.tab-short-motion')))
|
|
615
653
|
const shownSelect = () =>
|
|
616
654
|
page().evaluateHandle(() => {
|
|
617
655
|
const deep = (root = document) => [...root.querySelectorAll('select')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
@@ -680,7 +718,7 @@ describe('a part follows the figure size in one step (follow-size, 2026-09-26)',
|
|
|
680
718
|
editor.session.apply([{ kind: 'set-design', input: 'size.x', unit: 'mm', value: 800 }])
|
|
681
719
|
return editor.session.read().geometry.find(g => g.id === 'box').shape.dimensions
|
|
682
720
|
})
|
|
683
|
-
assert.equal(Math.round(grown.
|
|
721
|
+
assert.equal(Math.round(grown['size.x']), 800, 'twice the size, twice the box')
|
|
684
722
|
})
|
|
685
723
|
})
|
|
686
724
|
|
|
@@ -745,7 +783,7 @@ describe('the recipe catalog: a template chosen, filled in, put onto the chosen
|
|
|
745
783
|
const ids = await parts()
|
|
746
784
|
for (const part of ['frame', 'left', 'right', 'lamp']) assert.ok(ids.includes(`sliding-door-1-${part}`), `${part} is in`)
|
|
747
785
|
const frame = await body('sliding-door-1-frame')
|
|
748
|
-
assert.equal(Math.round(frame.dimensions.
|
|
786
|
+
assert.equal(Math.round(frame.dimensions['size.x']), 2000, "half the room's 4000 mm")
|
|
749
787
|
assert.equal(await at('figure-recipe-catalog'), null, 'the catalog closes')
|
|
750
788
|
})
|
|
751
789
|
|
|
@@ -760,8 +798,12 @@ describe('the recipe catalog: a template chosen, filled in, put onto the chosen
|
|
|
760
798
|
assert.equal((await state()).revision, start.revision, 'no step')
|
|
761
799
|
assert.deepEqual(await parts(), before, 'nothing added')
|
|
762
800
|
const said = await at('section[detail] p[role="alert"]')
|
|
763
|
-
/*
|
|
764
|
-
|
|
801
|
+
/*
|
|
802
|
+
The step it stopped at and the kernel's code -- not the kernel's English (2026-09-27). The page loads no
|
|
803
|
+
translations, so the sentence shows as its key; the step and the code it is given are the unit suite's
|
|
804
|
+
(refusals.test.ts, recipeRefusalOf).
|
|
805
|
+
*/
|
|
806
|
+
assert.equal(said?.text, 'figure.text.recipe-step-refused', `the step's sentence, not the kernel's English: ${said?.text}`)
|
|
765
807
|
await click(await at('button[close]'))
|
|
766
808
|
})
|
|
767
809
|
})
|
|
@@ -823,8 +865,8 @@ describe('a row that carries a part (ADR-0093, 2026-09-26)', () => {
|
|
|
823
865
|
await click(await read(() => probe.row('roller')))
|
|
824
866
|
assert.equal((await state()).selected, 'roller')
|
|
825
867
|
assert.ok(await read(() => probe.hasInput('roller.repeat.pitch')), 'the roller offers its row')
|
|
826
|
-
/* Walk 1 (2026-09-26): the fastening form opens on the roller's own fastening -- by its centre. */
|
|
827
|
-
assert.
|
|
868
|
+
/* Walk 1 (2026-09-26): the fastening form opens on the roller's own fastening -- by its centre, the drawing's way (2026-10-01). */
|
|
869
|
+
assert.equal(await read(() => probe.fastenMode()), 'centre', 'the way it is fastened reads centre')
|
|
828
870
|
/* And the row's part keeps a name of its own to change. */
|
|
829
871
|
const name = await read(() => probe.nameField())
|
|
830
872
|
assert.equal(name.disabled, false, 'the roller can be renamed')
|
|
@@ -66,7 +66,7 @@ test('단면 칸에 그림이 들어간다: 속 공간이 있을 때, 면마다
|
|
|
66
66
|
assert.ok(!panel.includes("cavityField(") && !panel.includes('renderEnd('), 'no number fields repeating the drawing')
|
|
67
67
|
has(drawing, /@pointerdown=\$\{\(ev: PointerEvent\) => this\.type\(ev, \{ kind: 'wall', side \}, 'top', w\.wall\[side\]!\)\}/)
|
|
68
68
|
has(drawing, /!open && this\.type\(ev, \{ kind: 'end', end \}, 'side'/)
|
|
69
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
69
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
70
70
|
const t = JSON.parse(read(`../translations/${lang}.json`)) as Record<string, string>
|
|
71
71
|
for (const key of ['figure.label.section-cavity-top', 'figure.label.section-cavity-side']) assert.ok(t[key], `${lang} ${key}`)
|
|
72
72
|
}
|
|
@@ -80,7 +80,7 @@ test('the modeller asks on every path an edit can stop a clip, in every language
|
|
|
80
80
|
assert.ok(editor.includes(`if (this.askClipLoss(error, ${path})) return`), path)
|
|
81
81
|
assert.ok(editor.includes('if (!now || now.context !== asked.context || now.revision !== asked.revision) {'), 'what was asked about is checked after the answer')
|
|
82
82
|
assert.ok(editor.includes('asked.session.withConsent({ dropClips: clips.map(c => c.id), context: asked.context, revision: asked.revision }, again)'))
|
|
83
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) {
|
|
83
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) {
|
|
84
84
|
const t = JSON.parse(read(`../translations/${lang}.json`))
|
|
85
85
|
for (const key of ['figure.text.clips-would-go', 'figure.text.clip-gone', 'figure.text.clip-changed', 'figure.button.let-clips-go', 'figure.text.clips-consent-stale']) assert.ok(t[key], `${lang} ${key}`)
|
|
86
86
|
assert.ok(t['figure.text.clips-would-go'].includes('{clips}') && t['figure.text.clip-gone'].includes('{clip}'), lang)
|
package/test/clips.test.ts
CHANGED
|
@@ -64,7 +64,7 @@ test('움직임 탭에 서고, 막대는 입력 한 묶음 · 나머지는 부
|
|
|
64
64
|
const clips = read('../client/modeller/figure-clips.ts')
|
|
65
65
|
assert.ok(clips.includes("@change=${(e: Event) => this.scrub(clip, number(e))}"), '막대는 놓을 때 한 번')
|
|
66
66
|
const keys = [...clips.matchAll(/'(figure\.[a-z.-]*[a-z])'/g)].map(m => m[1])
|
|
67
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
67
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
68
68
|
const table = JSON.parse(read(`../translations/${lang}.json`))
|
|
69
69
|
for (const key of keys) assert.ok(table[key], `${lang}: ${key}`)
|
|
70
70
|
}
|
|
@@ -94,7 +94,7 @@ test('the details fold, and what they hold that is not the default stays said --
|
|
|
94
94
|
/* The way and the place on the face are the drawing's: not said again in the summary, nor chosen again in the details. */
|
|
95
95
|
assert.ok(!links.includes('fasten-summary-edge') && !links.includes('fasten-summary-off-centre') && !links.includes("'figure.label.facing-flush'"))
|
|
96
96
|
assert.ok(links.includes('({ axis }) => !view || !view.axes.includes(axis) || !!f.alignInput?.[axis] || hasInstanceSize(asset, axis)'), 'a row only where the drawing cannot set it')
|
|
97
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) {
|
|
97
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) {
|
|
98
98
|
const t = JSON.parse(read(`../translations/${lang}.json`))
|
|
99
99
|
for (const key of ['preview-refused', 'preview-stays', 'preview-moves', 'preview-turns-named', 'preview-turns-slanted', 'preview-carries', 'preview-copies', 'preview-clips', 'preview-face-unknown', 'target-would-loop', 'fasten-summary-gap', 'fasten-summary-twist'])
|
|
100
100
|
assert.ok(t[`figure.text.${key}`], `${lang} ${key}`)
|
|
@@ -124,7 +124,7 @@ test('detaching says what it would do: stays, what stays fastened to it, the cli
|
|
|
124
124
|
assert.ok(editor.includes(': /* It stays where it is: as expected, so nothing is said (user 2026-10-01). */ []'), 'nothing said when it stays')
|
|
125
125
|
const links = read('../client/modeller/figure-links.ts')
|
|
126
126
|
assert.equal(links.match(/\$\{this\.renderDetachWords\(\)\}/g)?.length, 3, 'beside every button that detaches (a part at an origin has its own)')
|
|
127
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) {
|
|
127
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) {
|
|
128
128
|
const t = JSON.parse(read(`../translations/${lang}.json`))
|
|
129
129
|
for (const key of ['detach-moves', 'detach-keeps-children', 'detach-clips', 'detach-refused']) assert.ok(t[`figure.text.${key}`], `${lang} ${key}`)
|
|
130
130
|
}
|
|
@@ -164,7 +164,7 @@ test('a row is seen before it is made, read as figure-model draws it -- and said
|
|
|
164
164
|
const editor = read('../client/modeller/editor.ts')
|
|
165
165
|
assert.ok(editor.includes("const row = candidate.action.kind === 'make-repeat' ? figureRowOf(seen.asset, this.selected, this.shown()!.inputs) : undefined"))
|
|
166
166
|
assert.ok(editor.includes('<ul row-reading>${rowWordsOf(this.rowNow(shown)'), 'a row made says the same, as it stands')
|
|
167
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) {
|
|
167
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) {
|
|
168
168
|
const t = JSON.parse(read(`../translations/${lang}.json`))
|
|
169
169
|
for (const key of ['row-count-pitch', 'row-count-uneven', 'row-ends-centred', 'row-ends', 'row-ends-unknown', 'row-companions', 'row-count-spreads', 'row-blocked-unfastened', 'row-blocked-on-plane', 'row-blocked-holds-parts'])
|
|
170
170
|
assert.ok(t[`figure.text.${key}`], `${lang} ${key}`)
|
|
@@ -241,7 +241,7 @@ test('past the edge of its face is said beside the fastening, with the way to ch
|
|
|
241
241
|
assert.ok(links.includes("<button act @click=${() => this.showWhere()}><md-icon>tune</md-icon>${i18next.t('figure.button.fix-alignment')}</button>"))
|
|
242
242
|
assert.ok(links.includes("shadowRoot?.querySelector<HTMLElement>('div[where]')"), 'to the face in the drawing, where the place is dragged or keyed')
|
|
243
243
|
assert.equal(say('figure.text.overhang-out', { on: 'top', face: '윗면', sides: say('figure.text.overhang-side-high', { axis: 'X', mm: '100' }) }), 'top의 윗면 가장자리 밖으로 나와 있습니다: X 높은 쪽 100 mm. 모델의 점유 공간과는 따로 봅니다.')
|
|
244
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) {
|
|
244
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) {
|
|
245
245
|
const t = JSON.parse(read(`../translations/${lang}.json`))
|
|
246
246
|
for (const key of ['figure.text.overhang-out', 'figure.text.overhang-possible', 'figure.text.overhang-unknown', 'figure.text.overhang-held', 'figure.text.overhang-side-low', 'figure.text.overhang-side-high', 'figure.button.fix-alignment']) assert.ok(t[key], `${lang} ${key}`)
|
|
247
247
|
}
|
|
@@ -290,7 +290,7 @@ test('a part wider than its face is said in the relation, with its numbers and w
|
|
|
290
290
|
const links = read('../client/modeller/figure-links.ts')
|
|
291
291
|
assert.ok(links.includes("${this.overhangNote.length ? html`<p hint overhang-note>${this.overhangNote.join(' ')}</p>` : nothing}"), 'in the details, not the warning')
|
|
292
292
|
assert.equal(say('figure.text.overhang-larger', { on: 'leg', face: '윗면', sides: 'X 낮은 쪽 270 mm, X 높은 쪽 870 mm' }), 'leg의 윗면보다 넓어 가장자리 밖으로 나와 있습니다: X 낮은 쪽 270 mm, X 높은 쪽 870 mm.')
|
|
293
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) {
|
|
293
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) {
|
|
294
294
|
const t = JSON.parse(read(`../translations/${lang}.json`))
|
|
295
295
|
assert.ok(t['figure.text.overhang-larger'] && t['figure.text.overhang-lopsided'], lang)
|
|
296
296
|
}
|
package/test/controls.test.ts
CHANGED
|
@@ -62,7 +62,7 @@ test('편집기에 닿는다 — 값 바꾸기는 입력으로, 이름 · 범위
|
|
|
62
62
|
const controls = read('../client/modeller/figure-controls.ts')
|
|
63
63
|
assert.ok(controls.includes("@change=${(e: Event) => this.try(c, number(e))}"), '슬라이더는 놓을 때 한 번 — 끄는 동안 기록이 쌓이지 않는다')
|
|
64
64
|
const keys = [...controls.matchAll(/'(figure\.[a-z.-]*[a-z])'/g)].map(m => m[1])
|
|
65
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
65
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
66
66
|
const table = JSON.parse(read(`../translations/${lang}.json`))
|
|
67
67
|
for (const key of keys) assert.ok(table[key], `${lang}: ${key}`)
|
|
68
68
|
}
|
|
@@ -40,6 +40,6 @@ test('화면 문구는 결과와 예외만 — 한 줄 요약, 가장자리 밖,
|
|
|
40
40
|
assert.equal(say('figure.text.fasten-sentence-meet', { part: 'lid', face: '아랫면', to: 'bin의 윗면' }), 'lid의 아랫면을 bin의 윗면에 맞닿게')
|
|
41
41
|
assert.equal(say('figure.text.fasten-sentence-centre', { part: 'roller', face: '', to: 'frame의 앞면' }), 'roller의 가운데를 frame의 앞면 위에')
|
|
42
42
|
for (const key of ['figure.text.fasten-off-face', 'figure.text.fasten-past-edges', 'figure.label.fasten-mode-meet', 'figure.label.fasten-mode-flush', 'figure.label.fasten-mode-centre'])
|
|
43
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) assert.ok((JSON.parse(read(`../translations/${lang}.json`)) as Record<string, string>)[key], `${lang} ${key}`)
|
|
43
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) assert.ok((JSON.parse(read(`../translations/${lang}.json`)) as Record<string, string>)[key], `${lang} ${key}`)
|
|
44
44
|
assert.ok(!/끌어 옮깁니다|면 밖으로도/.test(JSON.stringify(ko)), 'no instructions on the screen')
|
|
45
45
|
})
|
package/test/fields.test.ts
CHANGED
|
@@ -96,7 +96,7 @@ test('pitch 는 한 곳에서 하나의 값이고, 늘 set-repeat 로 바꿔 최
|
|
|
96
96
|
const pitch = editor.slice(editor.indexOf(' private setPitch('), editor.indexOf(' /** Choose a part,'))
|
|
97
97
|
assert.ok(pitch.includes("this.session!.command({ kind: 'set-repeat', part: this.selected, pitch })"), '늘 명령으로 — 최대 길이가 따라온다')
|
|
98
98
|
assert.ok(!pitch.includes('changeField'))
|
|
99
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
99
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
100
100
|
const table = JSON.parse(readFileSync(fileURLToPath(new URL(`../translations/${lang}.json`, import.meta.url)), 'utf8'))
|
|
101
101
|
/* The term stays pitch; what it measures is said beside it (ADR-0097 §5.5: centre to centre). */
|
|
102
102
|
assert.ok(table['figure.label.repeat-gap'].startsWith('pitch') && table['figure.label.repeat-gap'].length > 'pitch'.length, lang)
|
|
@@ -48,7 +48,7 @@ test('a figure proposal is answered by its handler, in words, never left without
|
|
|
48
48
|
assert.match(handler, /export const FIGURE_CANDIDATE_TOOL = 'proposeFigureCandidate'/)
|
|
49
49
|
assert.match(handler, /runFigureProposal\(/)
|
|
50
50
|
assert.match(handler, /context\.notify\(/)
|
|
51
|
-
for (const lang of ['en', 'ko', 'ja', 'zh', 'ms']) {
|
|
51
|
+
for (const lang of ['en', 'ko', 'ja', 'zh', 'ms', 'es']) {
|
|
52
52
|
const words = JSON.parse(readFileSync(fileURLToPath(new URL(`../translations/${lang}.json`, import.meta.url)), 'utf8'))
|
|
53
53
|
for (const key of ['applied', 'stale', 'refused', 'needs-modeller', 'not-opened', 'new-figure-unsaved'])
|
|
54
54
|
assert.ok(words[`figure.text.proposal-${key}`], `${lang} has no words for proposal-${key}`)
|
|
@@ -50,7 +50,7 @@ test('the side tabs say their names, in every language', () => {
|
|
|
50
50
|
assert.ok(editor.includes("const name = i18next.t('figure.label.tab-short-' + tab)"))
|
|
51
51
|
assert.ok(editor.includes('aria-label=${name}'), 'its accessible name is the name it shows')
|
|
52
52
|
assert.ok(editor.includes('<span tab-label aria-hidden="true">${name}</span>'))
|
|
53
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
53
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
54
54
|
const table = JSON.parse(read(`../translations/${lang}.json`))
|
|
55
55
|
for (const tab of ['properties', 'capability', 'motion', 'verdict', 'view', 'history']) assert.ok(table[`figure.label.tab-short-${tab}`], `${lang} ${tab}`)
|
|
56
56
|
}
|
|
@@ -35,7 +35,7 @@ test('the first save asks the name first; the identifier offered stays through r
|
|
|
35
35
|
assert.ok(popup.includes('if (!this.type || offeredAndTaken) this.type = this.suggested = suggestTypeOf(this.name, this.taken)'), 'offered again only when empty, or offered and taken')
|
|
36
36
|
const page = read('../client/pages/figure-modeller-page.ts')
|
|
37
37
|
assert.ok(page.includes('if (answer) this.row = { ...this.row, type: answer.type }'), 'a retry opens on the identifier answered before')
|
|
38
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) {
|
|
38
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) {
|
|
39
39
|
const t = JSON.parse(read(`../translations/${lang}.json`))
|
|
40
40
|
assert.ok(t['figure.label.figure-identifier'] && t['figure.text.identifier-suggested'], lang)
|
|
41
41
|
}
|
package/test/gizmo.test.ts
CHANGED
|
@@ -94,7 +94,7 @@ test('캔버스 — 기즈모 · W E R · 스냅은 씬의 편집 모드가 한
|
|
|
94
94
|
})
|
|
95
95
|
|
|
96
96
|
test('기즈모의 말은 다섯 말 모두에 있다', () => {
|
|
97
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
97
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
98
98
|
const table = JSON.parse(read(`../translations/${lang}.json`))
|
|
99
99
|
for (const mode of ['translate', 'rotate', 'scale']) assert.ok(table[`figure.label.gizmo-${mode}`], `${lang}: ${mode}`)
|
|
100
100
|
}
|
package/test/layout.test.ts
CHANGED
|
@@ -66,7 +66,7 @@ test('띠 — 칸 접기 · 되돌리기 · 전체 보기 · 저장 상태 ·
|
|
|
66
66
|
assert.ok(bar.includes(piece), piece)
|
|
67
67
|
assert.doesNotMatch(renderBody, /[가-힣]/, '편집기의 화면 말에 한국어가 박혀 있지 않다')
|
|
68
68
|
const keys = [...editor.matchAll(/'(figure\.[a-z.-]*[a-z])'/g)].map(m => m[1])
|
|
69
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
69
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
70
70
|
const table = JSON.parse(read(`../translations/${lang}.json`))
|
|
71
71
|
for (const key of keys) assert.ok(table[key], `${lang}: ${key}`)
|
|
72
72
|
}
|
|
@@ -103,7 +103,7 @@ test('불러오는 동안 빈 무대에 「도형을 추가해…」가 깜빡
|
|
|
103
103
|
/* A new figure's start card says how to begin; an empty stage otherwise is one loading, or with every part hidden. */
|
|
104
104
|
assert.ok(canvas.includes('return html`<div stage></div>`'))
|
|
105
105
|
assert.ok(!canvas.includes('add-a-shape-to-start') && !editor.includes('startShown'))
|
|
106
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms'])
|
|
106
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es'])
|
|
107
107
|
assert.equal((JSON.parse(read(`../translations/${lang}.json`)) as Record<string, string>)['figure.text.add-a-shape-to-start'], undefined, lang)
|
|
108
108
|
assert.ok(editor.includes("if (!foldSaid()) notify({ level: 'info', message: i18next.t('figure.text.parts-panel-folded-narrow') })"))
|
|
109
109
|
assert.ok(/function foldSaid\(\): boolean \{\s*try \{\s*if \(localStorage\.getItem\('figure-fold-said'\)\) return true\s*localStorage\.setItem\('figure-fold-said', '1'\)\s*\} catch/.test(editor), 'storage refused: said, as before')
|
package/test/links.test.ts
CHANGED
|
@@ -98,7 +98,7 @@ test('떼면 지금 크기로 고정한다 — 따르던 값을 잃지 않는다
|
|
|
98
98
|
test('세 칸의 말은 다섯 말 모두에 있다', () => {
|
|
99
99
|
const links = read('../client/modeller/figure-links.ts')
|
|
100
100
|
const keys = [...links.matchAll(/'(figure\.[a-z.-]*[a-z])'/g)].map(m => m[1])
|
|
101
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
101
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
102
102
|
const table = JSON.parse(read(`../translations/${lang}.json`))
|
|
103
103
|
for (const key of [...keys, 'figure.text.follow-would-loop', 'figure.text.fasten-would-loop', 'figure.label.motion-slide', 'figure.label.motion-turn', 'figure.label.place-grip', 'figure.label.place-seat'])
|
|
104
104
|
assert.ok(table[key], `${lang}: ${key}`)
|
package/test/maturity.test.ts
CHANGED
|
@@ -229,7 +229,7 @@ test('화면이 키를 이어 붙여 찾는 말도 다섯 말 모두에 있다',
|
|
|
229
229
|
'figure.text.bound-by-triangles',
|
|
230
230
|
...[...maturity.matchAll(/'(figure\.[a-z.-]*[a-z])'/g)].map(m => m[1]!)
|
|
231
231
|
]
|
|
232
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
232
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
233
233
|
const table = JSON.parse(read(`../translations/${lang}.json`))
|
|
234
234
|
for (const key of keys) assert.ok(table[key], `${lang}: ${key}`)
|
|
235
235
|
}
|
|
@@ -40,7 +40,7 @@ test('지울 수 없는 부품은 사람의 말로 거절하고, 지우며 풀
|
|
|
40
40
|
/* Since 2026-09-25 the reason is told apart (partRefusalOf, tested in refusals); the editor hands the refusal to it. */
|
|
41
41
|
assert.match(editor, /private removePart[\s\S]*?partRefusalOf\(error, id,/)
|
|
42
42
|
assert.match(editor, /private removePart[\s\S]*?this\.notice = removalNoticeOf\(view\.removal,/, '지운 뒤 풀어 준 것을 알린다')
|
|
43
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
43
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
44
44
|
const table = JSON.parse(read(`../translations/${lang}.json`))
|
|
45
45
|
for (const key of ['part-is-referenced', 'part-in-declared-space', 'part-row-along', 'part-row-carries'])
|
|
46
46
|
assert.ok(table[`figure.text.${key}`]?.includes('{part}'), `${lang} ${key}`)
|
|
@@ -63,7 +63,7 @@ test('팔레트는 시작 모양만 낸다 — 둥근 상자와 통은 상자의
|
|
|
63
63
|
assert.deepEqual(kinds, ['box', 'cylinder', 'sphere', 'frustum', 'polygon', 'pyramid', 'plate', 'wall', 'portal'])
|
|
64
64
|
/* A part made so is still called by what it is: its icon in the parts list, its name over the properties (links.ts). */
|
|
65
65
|
assert.match(panel, /'rounded-box': 'crop_square'/)
|
|
66
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms'])
|
|
66
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es'])
|
|
67
67
|
assert.ok(JSON.parse(read(`../translations/${lang}.json`))['figure.label.primitive-rounded-box'], lang)
|
|
68
68
|
})
|
|
69
69
|
|
|
@@ -71,7 +71,7 @@ test('부품 이름은 figure-model 의 rename-part 로 바꾸고, 거절은 까
|
|
|
71
71
|
assert.match(editor, /this\.session!\.edit\(\{ kind: 'rename-part', target: from, name \}\)/)
|
|
72
72
|
assert.match(editor, /this\.selected = name/, '바꾼 이름의 부품이 그대로 골라져 있다')
|
|
73
73
|
assert.match(editor, /'figure\.text\.part-name-taken' : 'figure\.text\.part-name-invalid'/)
|
|
74
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
74
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
75
75
|
const table = JSON.parse(read(`../translations/${lang}.json`))
|
|
76
76
|
for (const key of ['figure.text.part-name-taken', 'figure.text.part-name-invalid']) assert.ok(table[key]?.includes('{name}'), `${lang}: ${key}`)
|
|
77
77
|
}
|
package/test/properties.test.ts
CHANGED
|
@@ -89,7 +89,7 @@ test('속성 탭의 말은 다섯 말 모두에 있다', () => {
|
|
|
89
89
|
const keys = [...tab.matchAll(/'(figure\.[a-z.-]*[a-z])'/g)].map(m => m[1])
|
|
90
90
|
for (const face of ['left', 'right', 'bottom', 'top', 'back', 'front']) keys.push(`figure.label.face-${face}`)
|
|
91
91
|
for (const one of ['floor', 'ceiling', 'center']) keys.push(`figure.label.placement-${one}`)
|
|
92
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
92
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
93
93
|
const table = JSON.parse(read(`../translations/${lang}.json`))
|
|
94
94
|
for (const key of keys) assert.ok(table[key], `${lang}: ${key}`)
|
|
95
95
|
}
|
|
@@ -122,5 +122,5 @@ test('the dock checks the result from the server: saved is accepted and named, n
|
|
|
122
122
|
assert.equal(proposalOutcomeFrom([], sayKo), undefined, 'not saved is not known, never refused')
|
|
123
123
|
const page = read('../client/pages/figure-modeller-page.ts')
|
|
124
124
|
assert.ok(page.includes('proposalOutcomeFrom(await fetchProposalOutcome(proposalKey)'), 'the surface asks the server')
|
|
125
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) assert.ok(JSON.parse(read(`../translations/${lang}.json`))['figure.text.proposal-saved-into'], lang)
|
|
125
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) assert.ok(JSON.parse(read(`../translations/${lang}.json`))['figure.text.proposal-saved-into'], lang)
|
|
126
126
|
})
|