@things-factory/figure-ui 10.1.204 → 10.1.205
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/canvas.ts +91 -9
- package/client/modeller/editor-styles.ts +7 -22
- package/client/modeller/editor.ts +176 -145
- package/client/modeller/fasten-form.ts +85 -6
- package/client/modeller/figure-fasten-dialog.ts +42 -9
- package/client/modeller/figure-fasten-drawing.ts +233 -12
- package/client/modeller/figure-section-editor.ts +6 -6
- package/client/modeller/figure-slot-layout-panel.ts +1 -1
- package/client/modeller/gizmo.ts +12 -1
- package/client/modeller/links.ts +48 -6
- package/client/modeller/section-drawing.ts +7 -7
- package/client/modeller/size-drag.ts +112 -0
- package/client/modeller/slot-layout-host.ts +3 -3
- package/client/modeller/stage-model.ts +6 -9
- package/client/pages/figure-modeller-page.ts +1 -4
- package/dist-client/modeller/canvas.d.ts +24 -3
- package/dist-client/modeller/canvas.js +96 -11
- package/dist-client/modeller/canvas.js.map +1 -1
- package/dist-client/modeller/editor-styles.js +7 -22
- package/dist-client/modeller/editor-styles.js.map +1 -1
- package/dist-client/modeller/editor.d.ts +28 -32
- package/dist-client/modeller/editor.js +176 -153
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/fasten-form.d.ts +36 -3
- package/dist-client/modeller/fasten-form.js +80 -5
- package/dist-client/modeller/fasten-form.js.map +1 -1
- package/dist-client/modeller/figure-fasten-dialog.d.ts +17 -3
- package/dist-client/modeller/figure-fasten-dialog.js +47 -10
- package/dist-client/modeller/figure-fasten-dialog.js.map +1 -1
- package/dist-client/modeller/figure-fasten-drawing.d.ts +33 -1
- package/dist-client/modeller/figure-fasten-drawing.js +235 -11
- package/dist-client/modeller/figure-fasten-drawing.js.map +1 -1
- package/dist-client/modeller/figure-section-editor.d.ts +3 -3
- package/dist-client/modeller/figure-section-editor.js +4 -4
- package/dist-client/modeller/figure-section-editor.js.map +1 -1
- package/dist-client/modeller/figure-slot-layout-panel.js +1 -1
- package/dist-client/modeller/figure-slot-layout-panel.js.map +1 -1
- package/dist-client/modeller/gizmo.d.ts +12 -1
- package/dist-client/modeller/gizmo.js +8 -0
- package/dist-client/modeller/gizmo.js.map +1 -1
- package/dist-client/modeller/links.d.ts +14 -2
- package/dist-client/modeller/links.js +42 -4
- package/dist-client/modeller/links.js.map +1 -1
- package/dist-client/modeller/section-drawing.d.ts +3 -4
- package/dist-client/modeller/section-drawing.js +10 -9
- package/dist-client/modeller/section-drawing.js.map +1 -1
- package/dist-client/modeller/size-drag.d.ts +38 -0
- package/dist-client/modeller/size-drag.js +85 -0
- package/dist-client/modeller/size-drag.js.map +1 -0
- package/dist-client/modeller/slot-layout-host.d.ts +1 -3
- package/dist-client/modeller/slot-layout-host.js +3 -3
- package/dist-client/modeller/slot-layout-host.js.map +1 -1
- package/dist-client/modeller/stage-model.d.ts +5 -7
- package/dist-client/modeller/stage-model.js +6 -6
- package/dist-client/modeller/stage-model.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +0 -2
- package/dist-client/pages/figure-modeller-page.js +1 -6
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/test/align.test.ts +7 -11
- package/test/browser/acceptance-page.js +10 -0
- package/test/browser/editing.check.mjs +17 -13
- package/test/browser/links-page.js +2 -2
- package/test/browser/links.check.mjs +94 -56
- package/test/browser/modeller-page.js +30 -18
- package/test/browser/modeller-revision.check.mjs +39 -0
- package/test/browser/modeller.check.mjs +32 -10
- package/test/browser/screen-page.js +0 -2
- package/test/browser/screen.check.mjs +11 -13
- package/test/browser/section.check.mjs +2 -2
- package/test/fasten-form.test.ts +62 -1
- package/test/gizmo.test.ts +13 -15
- package/test/links.test.ts +8 -3
- package/test/resize-with-placement.test.ts +14 -13
- package/test/revision.test.ts +3 -2
- package/test/section-editor.test.ts +9 -10
- package/test/size-drag.test.ts +114 -0
- package/test/slot-layout-host.test.ts +13 -8
- package/translations/en.json +25 -22
- package/translations/es.json +25 -22
- package/translations/ja.json +25 -22
- package/translations/ko.json +25 -22
- package/translations/ms.json +25 -22
- package/translations/zh.json +25 -22
|
@@ -93,7 +93,7 @@ const tab = name => read(l => probe.tab(l), `figure.label.tab-short-${name}`).th
|
|
|
93
93
|
/** A fastening the part has is said in one line; 「바꾸기」 opens the fastening window on it. */
|
|
94
94
|
async function changeFastening() {
|
|
95
95
|
await until(() => look.all('button[change-fastening]').length === 1, [], 'the fastening said in one line')
|
|
96
|
-
await (
|
|
96
|
+
await press('button[change-fastening]')
|
|
97
97
|
await until(() => !!probe.fastenTargets(), [], 'the fastening window open')
|
|
98
98
|
}
|
|
99
99
|
/** The window's list: each row's part and words, whether chosen, whether it can be. Null while the window is closed. */
|
|
@@ -388,17 +388,15 @@ describe('a new fastening for a door leaf: chosen in the window, seen before it
|
|
|
388
388
|
})
|
|
389
389
|
const ghosts = () => read(() => look.drawn().filter(n => n.startsWith('__preview')))
|
|
390
390
|
|
|
391
|
-
test('started, the window opens
|
|
391
|
+
test('started, the window opens in the standard popup, asking what to fasten to, nothing chosen', async () => {
|
|
392
392
|
await pick('leaf')
|
|
393
393
|
await press('button[fasten-start]')
|
|
394
394
|
const list = await until(() => probe.fastenTargets(), [], 'the fastening window')
|
|
395
395
|
assert.deepEqual(list.filter(t => t.selected), [], 'nothing chosen')
|
|
396
396
|
assert.ok(list.some(t => t.target === 'frame' && !t.disabled), 'the frame offered')
|
|
397
397
|
assert.deepEqual((await all('[apply]')).map(b => b.disabled), [true], 'nothing to apply yet')
|
|
398
|
-
/*
|
|
399
|
-
|
|
400
|
-
assert.deepEqual({ ...panel, overCanvas: undefined }, { title: 'figure.label.fastening', role: 'dialog', popup: false, overCanvas: undefined })
|
|
401
|
-
assert.ok(panel.overCanvas <= 0.1, `the canvas stays in sight: ${panel.overCanvas} of its width covered`)
|
|
398
|
+
/* The standard popup, moved by its head, with no backdrop: the canvas can be pressed while it is open (ADR-0140 stage 4). */
|
|
399
|
+
assert.deepEqual(await read(() => probe.fastenPopup()), { title: 'figure.label.fastening', movable: true, backdrop: false, role: 'dialog' })
|
|
402
400
|
})
|
|
403
401
|
|
|
404
402
|
test('the frame chosen: the editor is told the command, and the canvas draws where the leaf would go and the face it meets', async () => {
|
|
@@ -424,15 +422,15 @@ describe('a new fastening for a door leaf: chosen in the window, seen before it
|
|
|
424
422
|
assert.deepEqual(await ghosts(), [], 'nothing of the earlier result drawn')
|
|
425
423
|
})
|
|
426
424
|
|
|
427
|
-
test('another
|
|
425
|
+
test('another tab pressed closes the window with nothing written, and nothing it tried is drawn', async () => {
|
|
428
426
|
const start = await state()
|
|
429
|
-
/* No backdrop
|
|
430
|
-
|
|
431
|
-
await pick('frame')
|
|
427
|
+
/* No backdrop: the press reaches the tab, and the window, which is the properties tab's, closes. */
|
|
428
|
+
await click(await read(l => probe.tab(l), 'figure.label.tab-short-motion'), 'the motion tab')
|
|
432
429
|
await until(() => !probe.fastenTargets(), [], 'the window closed')
|
|
433
430
|
await until(() => !look.drawn().some(n => n.startsWith('__preview')), [], 'nothing it tried drawn')
|
|
431
|
+
assert.equal(await read(() => look.tab()), 'figure.label.tab-short-motion', 'the press reached the tab')
|
|
434
432
|
assert.equal((await state()).revision, start.revision, 'nothing written')
|
|
435
|
-
await
|
|
433
|
+
await tab('properties')
|
|
436
434
|
})
|
|
437
435
|
|
|
438
436
|
test('"fasten it first" from the motion tab opens this part\'s window, on what it has, on the properties tab', async () => {
|
|
@@ -490,13 +488,13 @@ async function pressFixAlignment() {
|
|
|
490
488
|
describe('past the edge of the face a part is fastened to', () => {
|
|
491
489
|
before(async () => {
|
|
492
490
|
let a = fasten(box(createFigureAsset('Overhangs'), 'frame', [1000, 100, 400], [0, 50, 0]), 'frame', 'plane')
|
|
493
|
-
/* Pushed 30 mm past the frame's right edge; a mesh turned on it, its box past the edge; on a drum's round end; wider than the frame, off to one side. */
|
|
491
|
+
/* Pushed 30 mm past the frame's right edge; a mesh turned on it, its box past the edge; on a drum's round end, within its square and past its rim; wider than the frame, off to one side. */
|
|
494
492
|
a = fasten(box(a, 'out', [100, 100, 100]), 'out', 'frame', { align: { x: { mm: 480 } } })
|
|
495
493
|
const mesh = { asset: 'a'.repeat(64), format: 'glb', unit: 'mm', bounds: { min: { x: -50, y: -50, z: -50 }, max: { x: 50, y: 50, z: 50 } }, triangles: 12, primitives: 1 }
|
|
496
494
|
a = addFigurePrimitive(a, { id: 'tilted', kind: 'mesh', dimensions: { size: 100 }, mesh, position: { x: 0, y: 0, z: 0 } })
|
|
497
495
|
a = applyFigureAuthoring(a, { kind: 'set-joint', joint: { id: 'tilted', type: 'fixed', body0: { part: 'frame', face: 'top' }, body1: { part: 'tilted' }, origin: { align: { x: { mm: 460 } }, childRotate: { y: 30 } } } })
|
|
498
496
|
a = fasten(addFigurePrimitive(a, { id: 'drum', kind: 'cylinder', dimensions: { radius: 100, length: 200 }, position: { x: 0, y: 0, z: 0 } }), 'drum', 'frame')
|
|
499
|
-
a = fasten(box(a, 'cap', [50, 50, 50]), 'cap', 'drum')
|
|
497
|
+
a = fasten(box(a, 'cap', [50, 50, 50]), 'cap', 'drum', { align: { x: { mm: 70 }, z: { mm: 70 } } })
|
|
500
498
|
await mount('overhangs', fasten(box(a, 'plate', [1600, 20, 200]), 'plate', 'frame', { align: { x: { mm: 200 } } }))
|
|
501
499
|
})
|
|
502
500
|
const overhang = () => read(() => look.all('div[overhang] p').map(p => p.text))
|
|
@@ -524,13 +522,13 @@ describe('past the edge of the face a part is fastened to', () => {
|
|
|
524
522
|
await until(() => look.all('div[overhang] p', 'figure.text.overhang-possible').length === 1, [], 'said it may be')
|
|
525
523
|
})
|
|
526
524
|
|
|
527
|
-
test('on a round face
|
|
525
|
+
test('on a round face, measured on the disc itself: past its rim though within its square, and "fix alignment" brings it in (ADR-0140 3.9)', async () => {
|
|
528
526
|
await pick('cap')
|
|
529
|
-
await until(() => look.all('div[overhang] p', 'figure.text.overhang-
|
|
527
|
+
await until(() => look.all('div[overhang] p', 'figure.text.overhang-out').length === 1, [], 'said past the rim')
|
|
530
528
|
const start = await state()
|
|
531
529
|
await pressFixAlignment()
|
|
532
|
-
await until(
|
|
533
|
-
|
|
530
|
+
await until(r => probe.state().revision === r + 1, [start.revision], 'one step')
|
|
531
|
+
await until(() => look.all('div[overhang] p', 'figure.text.overhang-out').length === 0, [], 'within the rim')
|
|
534
532
|
})
|
|
535
533
|
|
|
536
534
|
test('wider than the face: said in the relation with which side, not as a warning', async () => {
|
|
@@ -626,41 +624,32 @@ function twistedFollowingSize() {
|
|
|
626
624
|
return applyFigureAuthoring(twisted(), { kind: 'follow-size', part: 'blade' })
|
|
627
625
|
}
|
|
628
626
|
|
|
629
|
-
|
|
630
|
-
|
|
627
|
+
/*
|
|
628
|
+
ADR-0140 4.1 · 4.4: a part fastened at any angle is sized by a drag too -- its size alone is written, and its fastening
|
|
629
|
+
keeps it. Before, the drag was withheld and a notice sent the author to the size fields.
|
|
630
|
+
*/
|
|
631
|
+
describe('a part fastened twisted is sized by a drag: its size alone, its fastening keeps it', () => {
|
|
632
|
+
test('the resize tool is on, and a size drag writes its size in one step, the fastening as it was, with nothing said', async () => {
|
|
631
633
|
await mount('twisted', twisted())
|
|
632
634
|
await pick('blade')
|
|
633
|
-
assert.equal((await at('button[tool]', 'figure.button.gizmo-scale'))?.disabled,
|
|
635
|
+
assert.equal((await at('button[tool]', 'figure.button.gizmo-scale'))?.disabled, false, 'the resize tool is on')
|
|
634
636
|
await read(() => probe.told())
|
|
635
637
|
const start = await state()
|
|
636
|
-
await read(() =>
|
|
637
|
-
await
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
assert.deepEqual(await read(() => probe.
|
|
641
|
-
assert.
|
|
642
|
-
})
|
|
643
|
-
|
|
644
|
-
test('the R key says why, and the notice\'s button puts the cursor in the part\'s size field', async () => {
|
|
645
|
-
await mount('twisted', twisted())
|
|
646
|
-
await pick('blade')
|
|
647
|
-
await read(() => probe.told())
|
|
648
|
-
await page().keyboard.press('KeyR')
|
|
649
|
-
await until(() => probe.told().some(n => n.message === 'figure.text.gizmo-size-turned-fastening' && n.action === 'figure.button.to-size-fields'), [], 'the reason said with its way on')
|
|
638
|
+
const joint = await read(() => probe.joint('blade'))
|
|
639
|
+
await read(() => look.transformed('blade', { scale: [1.5, 1, 1], grab: { x: 1, y: 0, z: 0 } }))
|
|
640
|
+
await until(r => probe.state().revision === r + 1, [start.revision], 'one step')
|
|
641
|
+
assert.equal(await read(() => probe.value('blade.size.x')), 450, 'its size')
|
|
642
|
+
assert.deepEqual(await read(() => probe.joint('blade')), joint, 'its fastening as it was')
|
|
643
|
+
assert.deepEqual(await read(() => probe.told()), [], 'nothing to say')
|
|
650
644
|
assert.equal((await state()).error, '', 'no error')
|
|
651
|
-
/* From another tab: the button brings the properties tab and the size field. */
|
|
652
|
-
await tab('motion')
|
|
653
|
-
assert.equal(await read(() => probe.pressNoticeButton()), true)
|
|
654
|
-
await until(() => probe.focused()?.label?.startsWith('blade.size.'), [], 'the blade\'s size field focused')
|
|
655
|
-
assert.equal(await read(() => look.tab()), 'figure.label.tab-short-properties')
|
|
656
645
|
})
|
|
657
646
|
|
|
658
647
|
/*
|
|
659
|
-
:3300, 2026-10-19 (the fan's blade-2): a part whose size is not its own to type
|
|
660
|
-
|
|
648
|
+
:3300, 2026-10-19 (the fan's blade-2): a part whose size is not its own to type has the resize tool off; R says the
|
|
649
|
+
tool's own words, and puts no gizmo on it.
|
|
661
650
|
*/
|
|
662
651
|
for (const [label, make] of [['another part', 'twistedFollowing'], ['the figure\'s size', 'twistedFollowingSize']])
|
|
663
|
-
test(`a part whose size follows ${label} is not
|
|
652
|
+
test(`a part whose size follows ${label} is not sent to size fields it cannot type in`, async () => {
|
|
664
653
|
await mount(`twisted-${make}`, make === 'twistedFollowing' ? twistedFollowing() : twistedFollowingSize())
|
|
665
654
|
await pick('blade')
|
|
666
655
|
await read(() => probe.told())
|
|
@@ -668,12 +657,10 @@ describe('a part fastened twisted is not sized by a drag', () => {
|
|
|
668
657
|
await sleep(400)
|
|
669
658
|
const told = await read(() => probe.told())
|
|
670
659
|
assert.deepEqual(told.filter(n => n.action), [], `no way to size fields: ${JSON.stringify(told)}`)
|
|
671
|
-
assert.ok(!told.some(n => n.message === 'figure.text.gizmo-size-turned-fastening'), 'not said to be attached turned')
|
|
672
660
|
/* R does what the bar's resize tool does, off for this part: no gizmo it cannot use, and the tool's own words (:3300, 10.1.196). */
|
|
673
661
|
assert.equal((await state()).attached, null, 'no gizmo on it')
|
|
674
662
|
assert.deepEqual(told, [{ level: 'info', message: 'figure.text.gizmo-not-own', action: null }])
|
|
675
663
|
})
|
|
676
|
-
|
|
677
664
|
})
|
|
678
665
|
|
|
679
666
|
describe('a box made to follow the figure size on the floor', () => {
|
|
@@ -684,14 +671,14 @@ describe('a box made to follow the figure size on the floor', () => {
|
|
|
684
671
|
await pick('box')
|
|
685
672
|
})
|
|
686
673
|
|
|
687
|
-
test('its height is the room\'s only, shown apart; its row shows the number the floor gives; a size drag
|
|
674
|
+
test('its height is the room\'s only, shown apart; its row shows the number the floor gives; a size drag is drawn by the modeller', async () => {
|
|
688
675
|
const roomOnly = await all('input[aria-label="box.position.y"]')
|
|
689
676
|
assert.equal(roomOnly.length, 1, 'one field for it, apart from the position row')
|
|
690
677
|
assert.equal(roomOnly[0].disabled, true, 'not typed into')
|
|
691
678
|
assert.equal((await all('section[room-only] input[aria-label="box.position.y"]')).length, 1, 'in the room\'s section')
|
|
692
679
|
const held = (await all('div[vector-row] input[readonly]')).filter(i => i.label?.startsWith('position.y'))
|
|
693
680
|
assert.deepEqual(held.map(i => i.value), ['50'], 'the height the floor gives, read-only')
|
|
694
|
-
assert.
|
|
681
|
+
assert.equal(await read(() => look.scalePlacement('box')), 'owner', 'its place in a size drag is the modeller\'s (ADR-0140 4.4)')
|
|
695
682
|
})
|
|
696
683
|
|
|
697
684
|
test('the room is declared again only when the author confirms', async () => {
|
|
@@ -798,7 +785,7 @@ describe('the fastening window: tried freely, written once', () => {
|
|
|
798
785
|
await until(w => JSON.stringify(JSON.parse(document.querySelector('figure-editor').session.source()).joints.find(j => j.body1.part === 'lamp')) === w, [JSON.stringify(was)], 'one undo: the fastening as it was')
|
|
799
786
|
})
|
|
800
787
|
|
|
801
|
-
test('the
|
|
788
|
+
test('moved by the popup\'s head out of the way of the canvas: nothing written, still open', async () => {
|
|
802
789
|
await pick('lamp')
|
|
803
790
|
await changeFastening()
|
|
804
791
|
const rect = () => read(() => {
|
|
@@ -806,21 +793,21 @@ describe('the fastening window: tried freely, written once', () => {
|
|
|
806
793
|
return r ? { x: Math.round(r.left), y: Math.round(r.top) } : null
|
|
807
794
|
})
|
|
808
795
|
const was = await rect()
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
return { x: r.left + 40, y: r.top + r.height / 2 }
|
|
796
|
+
const head = await read(() => {
|
|
797
|
+
const h = window.fastenDialog().closest('ox-floating-overlay').shadowRoot.querySelector('[header] h1').getBoundingClientRect()
|
|
798
|
+
return { x: h.left + h.width / 2, y: h.top + h.height / 2 }
|
|
813
799
|
})
|
|
814
800
|
const start = await state()
|
|
815
|
-
await page().mouse.move(
|
|
801
|
+
await page().mouse.move(head.x, head.y)
|
|
816
802
|
await page().mouse.down()
|
|
817
|
-
for (let i = 1; i <= 6; i++) await page().mouse.move(
|
|
803
|
+
for (let i = 1; i <= 6; i++) await page().mouse.move(head.x - 30 * i, head.y + 20 * i)
|
|
818
804
|
await page().mouse.up()
|
|
819
|
-
await
|
|
805
|
+
const moved = await rect()
|
|
806
|
+
assert.deepEqual([moved.x - was.x, moved.y - was.y], [-180, 120], `moved with the pointer: ${JSON.stringify([was, moved])}`)
|
|
820
807
|
assert.equal(await windowOpen(), true, 'still open')
|
|
821
|
-
assert.deepEqual(await rect(), was, 'the window where it was')
|
|
822
808
|
assert.equal((await state()).revision, start.revision, 'nothing written')
|
|
823
|
-
|
|
809
|
+
/* Moved down, its foot may be past the page's: closed by Esc, as the window takes it. */
|
|
810
|
+
await page().keyboard.press('Escape')
|
|
824
811
|
await until(() => !probe.fastenTargets(), [], 'the window closed')
|
|
825
812
|
})
|
|
826
813
|
|
|
@@ -857,6 +844,57 @@ describe('a motion on the motion tab', () => {
|
|
|
857
844
|
})
|
|
858
845
|
})
|
|
859
846
|
|
|
847
|
+
describe('pressed on the canvas while the fastening window is open (ADR-0140 stage 4)', () => {
|
|
848
|
+
before(async () => {
|
|
849
|
+
/* A drum standing at (0, 600, 0), radius 100; a tag on the floor, its fastening to change. */
|
|
850
|
+
let a = addFigurePrimitive(createFigureAsset('Pressed'), { id: 'drum', kind: 'cylinder', dimensions: { radius: 100, length: 400 }, position: { x: 0, y: 600, z: 0 } })
|
|
851
|
+
a = fasten(box(a, 'tag', [40, 40, 40], [600, 20, 600]), 'tag', 'plane')
|
|
852
|
+
await mount('pressed', a)
|
|
853
|
+
})
|
|
854
|
+
|
|
855
|
+
test("the drum's side pressed: the window aims at the point pressed, and the tag stays chosen", async () => {
|
|
856
|
+
await pick('tag')
|
|
857
|
+
await changeFastening()
|
|
858
|
+
/* The scene's click, as a press on the drum's side facing +Z, 50 mm above its middle, tells it (`pickedAt`). */
|
|
859
|
+
const said = await read(async () => {
|
|
860
|
+
const deep = (s, root = document) => [...root.querySelectorAll(s)].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(s, e.shadowRoot)))
|
|
861
|
+
const canvas = deep('figure-canvas')[0]
|
|
862
|
+
const layer = canvas.scene.root
|
|
863
|
+
const drum = layer.components.find(c => c.state.name === 'drum')
|
|
864
|
+
drum.realObject.pickedAt = { point: [0, 650, 100], normal: [0, 0, 1] }
|
|
865
|
+
/* As the scene's click does: told on the component, it comes up to the board with the component as its origin. */
|
|
866
|
+
drum.trigger('click', new MouseEvent('click'))
|
|
867
|
+
await new Promise(done => setTimeout(done, 300))
|
|
868
|
+
const dialog = deep('figure-fasten-dialog')[0]
|
|
869
|
+
await dialog.updateComplete
|
|
870
|
+
return { target: dialog.form.target, surface: dialog.form.surface, selected: deep('figure-editor')[0].selected }
|
|
871
|
+
})
|
|
872
|
+
assert.deepEqual(said, { target: 'drum', surface: { around: 270, along: { mm: 50 } }, selected: 'tag' })
|
|
873
|
+
})
|
|
874
|
+
|
|
875
|
+
test('the window open, the drum pressed with the mouse on the canvas: aimed at, the window still open', async () => {
|
|
876
|
+
await press('[cancel]')
|
|
877
|
+
await until(() => !probe.fastenTargets(), [], 'the window closed')
|
|
878
|
+
await changeFastening()
|
|
879
|
+
/* Moved by its head off the drum, as a hand does, then the drum pressed where it is drawn. */
|
|
880
|
+
const head = await read(() => {
|
|
881
|
+
const h = window.fastenDialog().closest('ox-floating-overlay').shadowRoot.querySelector('[header] h1').getBoundingClientRect()
|
|
882
|
+
return { x: h.left + h.width / 2, y: h.top + h.height / 2 }
|
|
883
|
+
})
|
|
884
|
+
await page().mouse.move(head.x, head.y)
|
|
885
|
+
await page().mouse.down()
|
|
886
|
+
for (let i = 1; i <= 10; i++) await page().mouse.move(head.x + 35 * i, head.y)
|
|
887
|
+
await page().mouse.up()
|
|
888
|
+
await settled()
|
|
889
|
+
const drum = await read(() => probe.partOnPage('drum'))
|
|
890
|
+
assert.ok(await read(p => { const r = window.fastenDialog().getBoundingClientRect(); return p.x < r.left || p.x > r.right || p.y < r.top || p.y > r.bottom }, drum), 'the drum clear of the window')
|
|
891
|
+
await click(drum, 'the drum on the canvas')
|
|
892
|
+
await until(() => probe.fastenTargets()?.some(t => t.target === 'drum' && t.selected), [], 'the drum aimed at in the window')
|
|
893
|
+
assert.equal(await windowOpen(), true, 'still open: no backdrop took the press')
|
|
894
|
+
assert.equal((await state()).selected, 'tag', 'the tag still chosen')
|
|
895
|
+
})
|
|
896
|
+
})
|
|
897
|
+
|
|
860
898
|
test('the page raised no error', () => {
|
|
861
899
|
assert.deepEqual(opened.errors, [])
|
|
862
900
|
})
|
|
@@ -62,6 +62,15 @@ function sourceOf(kind, text) {
|
|
|
62
62
|
asset.palette = { ...asset.palette, ...PALETTE }
|
|
63
63
|
return serializeFigureAsset(addFigureBox(asset, { id: 'room', size: { x: 4000, y: 3000, z: 6000 }, position: { x: 0, y: 1500, z: 0 } }))
|
|
64
64
|
}
|
|
65
|
+
if (kind === 'shelf') {
|
|
66
|
+
/* A bed on the floor and a slat on its top: a part that can be made a row (the fold checks). */
|
|
67
|
+
let asset = createFigureAsset('Shelf')
|
|
68
|
+
asset.palette = { ...asset.palette, ...PALETTE }
|
|
69
|
+
asset = addFigureBox(asset, { id: 'bed', size: { x: 2000, y: 400, z: 600 }, position: { x: 0, y: 200, z: 0 } })
|
|
70
|
+
asset = applyFigureAuthoring(asset, { kind: 'attach', part: 'bed', to: { plane: 'mounting-plane' } })
|
|
71
|
+
asset = addFigureBox(asset, { id: 'slat', size: { x: 60, y: 20, z: 600 }, position: { x: 0, y: 410, z: 0 } })
|
|
72
|
+
return serializeFigureAsset(applyFigureAuthoring(asset, { kind: 'attach', part: 'slat', to: { part: 'bed', face: 'top' } }))
|
|
73
|
+
}
|
|
65
74
|
if (kind === 'rollers') {
|
|
66
75
|
/* A roller row whose rollers spin in place, each carrying a mark (ADR-0093, 2026-09-26). */
|
|
67
76
|
let asset = createFigureAsset('Rollers')
|
|
@@ -71,7 +80,8 @@ function sourceOf(kind, text) {
|
|
|
71
80
|
asset = addFigurePrimitive(asset, { id: 'roller', kind: 'cylinder', dimensions: { radius: 40, length: 560 }, position: { x: 0, y: 0, z: 0 }, rotation: { x: 90, y: 0, z: 0 } })
|
|
72
81
|
asset = applyFigureAuthoring(asset, { kind: 'attach', part: 'roller', facing: 'centre', to: { part: 'bed', face: 'top' }, gap: 40 })
|
|
73
82
|
asset = addFigureBox(asset, { id: 'mark', size: { x: 30, y: 30, z: 560 }, position: { x: 0, y: 0, z: 0 } })
|
|
74
|
-
|
|
83
|
+
/* On the roller's side at 0° round its own Y -- its own +X (ADR-0140 3.9). */
|
|
84
|
+
asset = applyFigureAuthoring(asset, { kind: 'attach', part: 'mark', to: { part: 'roller', surface: { around: 0, along: 'centre' } } })
|
|
75
85
|
const j = asset.joints.find(o => o.body1.part === 'roller')
|
|
76
86
|
asset = applyFigureAuthoring(asset, { kind: 'set-joint', joint: { ...j, type: 'revolute', axis: 'Z', lowerLimit: -360, upperLimit: 360, state: { id: 'spin' } } })
|
|
77
87
|
return serializeFigureAsset(applyFigureAuthoring(asset, { kind: 'make-repeat', part: 'roller', axis: 'x', pitch: 200 }))
|
|
@@ -232,23 +242,10 @@ window.probe = {
|
|
|
232
242
|
const chip = deep('figure-fasten-dialog')[0]?.shadowRoot.querySelector('ox-input-select-buttons')?.shadowRoot?.querySelector(`md-filter-chip[data-value="${CSS.escape(part)}"]`)
|
|
233
243
|
return chip ? centreOf(chip) : null
|
|
234
244
|
},
|
|
235
|
-
/**
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
fastenPanel() {
|
|
240
|
-
const dialog = deep('figure-fasten-dialog')[0]
|
|
241
|
-
const panel = dialog?.closest('[fasten-panel]')
|
|
242
|
-
if (!panel) return null
|
|
243
|
-
const p = panel.getBoundingClientRect(),
|
|
244
|
-
c = canvas().getBoundingClientRect()
|
|
245
|
-
const across = Math.max(0, Math.min(p.right, c.right) - Math.max(p.left, c.left))
|
|
246
|
-
return {
|
|
247
|
-
title: panel.querySelector('h3')?.textContent.trim(),
|
|
248
|
-
role: dialog.getAttribute('role'),
|
|
249
|
-
popup: deep('ox-floating-overlay').length > 0,
|
|
250
|
-
overCanvas: Math.round((across / c.width) * 100) / 100
|
|
251
|
-
}
|
|
245
|
+
/** The fastening window's popup, as the standard overlay holds it: its title, whether its head moves it, its backdrop. */
|
|
246
|
+
fastenPopup() {
|
|
247
|
+
const overlay = deep('figure-fasten-dialog')[0]?.closest('ox-floating-overlay')
|
|
248
|
+
return overlay ? { title: overlay.title, movable: overlay.movable, backdrop: !!overlay.backdrop, role: overlay.querySelector('figure-fasten-dialog').getAttribute('role') } : null
|
|
252
249
|
},
|
|
253
250
|
/** What the notification surface was handed since the last call: its level, its words, its button's label. */
|
|
254
251
|
told() {
|
|
@@ -380,6 +377,21 @@ window.probe = {
|
|
|
380
377
|
const b = deep('button[mode]').find(b => b.title === `figure.text.mode-${mode}-explained`)
|
|
381
378
|
return b ? centreOf(b) : null
|
|
382
379
|
},
|
|
380
|
+
/**
|
|
381
|
+
* A size drag as the scene tells it (ADR-0140 4.4): a frame of it -- the part grown by `scale` along its own axes, the
|
|
382
|
+
* handle `grab` held -- or its release. The scene's own gizmo is not driven: what the canvas tells the editor is.
|
|
383
|
+
*/
|
|
384
|
+
sizeFrame(id, scale, grab) {
|
|
385
|
+
canvas().dispatchEvent(new CustomEvent('part-sizing', { detail: { id, result: { scale, grab } }, bubbles: true, composed: true }))
|
|
386
|
+
},
|
|
387
|
+
sizeRelease(id, scale, grab) {
|
|
388
|
+
canvas().dispatchEvent(new CustomEvent('part-transformed', { detail: { id, result: { scale, grab } }, bubbles: true, composed: true }))
|
|
389
|
+
},
|
|
390
|
+
/** Where the canvas is told to draw a part now: its pose as given to the canvas. */
|
|
391
|
+
drawnPose(id) {
|
|
392
|
+
const part = canvas().parts.find(p => p.id === id)
|
|
393
|
+
return part ? { t: [...part.pose.t], r: part.pose.r.map(row => [...row]) } : null
|
|
394
|
+
},
|
|
383
395
|
/** The drawn position of a part's component, as the canvas shows it now. */
|
|
384
396
|
drawn(id) {
|
|
385
397
|
const c = canvas().scene.root.components.find(c => c.state.name === id)
|
|
@@ -109,6 +109,45 @@ describe('work counted on the robot arm', () => {
|
|
|
109
109
|
})
|
|
110
110
|
})
|
|
111
111
|
|
|
112
|
+
/*
|
|
113
|
+
ADR-0140 4.4: a size drag is drawn from figure-model frame by frame -- the figure compiled once when the drag begins, the
|
|
114
|
+
instance evaluator stepped each frame -- and the release writes what the last frame drew: no position differs, and the
|
|
115
|
+
release compiles no more than a release with no frames before it.
|
|
116
|
+
*/
|
|
117
|
+
describe('a size drag drawn frame by frame on the robot arm', () => {
|
|
118
|
+
const grab = { x: 0, y: 1, z: 0 }
|
|
119
|
+
const far = (a, b) => Math.max(...a.t.map((v, i) => Math.abs(v - b.t[i])), ...a.r.flat().map((v, i) => Math.abs(v - b.r.flat()[i])))
|
|
120
|
+
|
|
121
|
+
test('each frame compiles no figure, and moves what hangs from the part', async () => {
|
|
122
|
+
await mount('arm')
|
|
123
|
+
await click(await read(id => probe.partOnPage(id), 'upper-arm'))
|
|
124
|
+
await settled()
|
|
125
|
+
const still = await read(() => probe.drawnPose('forearm'))
|
|
126
|
+
await read(() => probe.sizeFrame('upper-arm', [1, 1.1, 1], { x: 0, y: 1, z: 0 }))
|
|
127
|
+
await drawn()
|
|
128
|
+
for (const s of [1.2, 1.3]) {
|
|
129
|
+
const { spent } = await counted(() => read(s => probe.sizeFrame('upper-arm', [1, s, 1], { x: 0, y: 1, z: 0 }), s))
|
|
130
|
+
assert.equal(spent.compiles, 0, `${s}: ${JSON.stringify(spent)}`)
|
|
131
|
+
}
|
|
132
|
+
assert.ok((await read(() => probe.drawnPose('forearm'))).t[1] > still.t[1] + 1, 'the forearm rides up with it')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test('the release writes what the last frame drew: no position differs', async () => {
|
|
136
|
+
const before = await read(() => probe.drawnPose('forearm'))
|
|
137
|
+
const revision = (await read(() => probe.state())).revision
|
|
138
|
+
const { spent } = await counted(() => read(g => probe.sizeRelease('upper-arm', [1, 1.3, 1], g), grab))
|
|
139
|
+
assert.equal((await read(() => probe.state())).revision, revision + 1, 'one step')
|
|
140
|
+
const written = await read(() => probe.body('forearm'))
|
|
141
|
+
assert.ok(far(before, written) < 1e-6, `the forearm where the last frame drew it: ${far(before, written)}`)
|
|
142
|
+
/* Released with no frames before it, on the same figure: the release costs the same or more. */
|
|
143
|
+
await mount('arm')
|
|
144
|
+
await click(await read(id => probe.partOnPage(id), 'upper-arm'))
|
|
145
|
+
await settled()
|
|
146
|
+
const bare = await counted(() => read(g => probe.sizeRelease('upper-arm', [1, 1.3, 1], g), grab))
|
|
147
|
+
assert.ok(spent.compiles <= bare.spent.compiles, `released after frames ${JSON.stringify(spent)}, without ${JSON.stringify(bare.spent)}`)
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
112
151
|
describe('the edit canvas and the preview stay standing', () => {
|
|
113
152
|
before(() => mount('box'))
|
|
114
153
|
|
|
@@ -388,6 +388,18 @@ describe('a figure made in the modeller is given its size from its parts (user 2
|
|
|
388
388
|
await tab('response')
|
|
389
389
|
})
|
|
390
390
|
|
|
391
|
+
/* In words a person reads (user 2026-10-20): what the figure does on a drawing, not the range behind it. */
|
|
392
|
+
const sizeGroup = () =>
|
|
393
|
+
page().evaluate(() => {
|
|
394
|
+
const deep = (root = document) => [...root.querySelectorAll('section[board-size]')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
395
|
+
const s = deep()[0]
|
|
396
|
+
return s ? { title: [...(s.querySelector('h3')?.childNodes ?? [])].filter(n => n.nodeType === 3).map(n => n.textContent).join('').trim(), fixed: s.querySelector('p[fixed-size]')?.textContent.trim() ?? null, followers: s.querySelector('p[followers]')?.textContent.trim() ?? null, dashes: s.querySelectorAll('div[pair][range] > span[dash]').length } : null
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
test('fixed: the size it is always drawn at, in one sentence, and the way to let a drawing resize it', async () => {
|
|
400
|
+
assert.deepEqual(await sizeGroup(), { title: 'figure.label.board-size', fixed: 'figure.text.board-size-fixed', followers: null, dashes: 0 })
|
|
401
|
+
})
|
|
402
|
+
|
|
391
403
|
test('with nothing picked, the figure offers to declare its size; clicked, its size fields appear', async () => {
|
|
392
404
|
const button = await page().evaluateHandle(() => {
|
|
393
405
|
const deep = (root = document) => [...root.querySelectorAll('button')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
@@ -407,6 +419,10 @@ describe('a figure made in the modeller is given its size from its parts (user 2
|
|
|
407
419
|
assert.deepEqual(await read(() => probe.fields('size.')), { 'size.x': 400, 'size.y': 400, 'size.z': 400 }, 'the size it is authored at: its parts now')
|
|
408
420
|
})
|
|
409
421
|
|
|
422
|
+
test('resizable: each axis reads smallest – largest, and which parts change with it (none on a lone box)', async () => {
|
|
423
|
+
assert.deepEqual(await sizeGroup(), { title: 'figure.label.board-size', fixed: null, followers: 'figure.text.board-size-no-followers', dashes: 3 })
|
|
424
|
+
})
|
|
425
|
+
|
|
410
426
|
test('the sizes it may be placed at are typed: the largest width, one step, and a smallest past its size refused', async () => {
|
|
411
427
|
const field = id =>
|
|
412
428
|
page().evaluateHandle(id => {
|
|
@@ -615,9 +631,15 @@ describe('the modeller made easier to find and to keep (2026-09-26 baseline)', (
|
|
|
615
631
|
assert.equal(await resize.evaluate(b => b.getAttribute('aria-pressed')), 'false')
|
|
616
632
|
})
|
|
617
633
|
|
|
618
|
-
test('a height typed into its field keeps the
|
|
634
|
+
test('a height typed into its field keeps the part\'s middle, and the size group says so (ADR-0140 4.1)', async () => {
|
|
619
635
|
await mount('box')
|
|
620
636
|
await click(await read(() => probe.partOnPage('box')))
|
|
637
|
+
const was = await body('box')
|
|
638
|
+
const middleOf = b => b.t[1] + b.dimensions.start + b.dimensions.length / 2
|
|
639
|
+
assert.ok(await read(() => {
|
|
640
|
+
const deep = (root = document) => [...root.querySelectorAll('p[size-keeps]')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
641
|
+
return deep().some(p => p.textContent.trim() === 'figure.text.size-keeps-centre')
|
|
642
|
+
}), 'said beside the size fields')
|
|
621
643
|
const height = await find('input', 'box.size.y')
|
|
622
644
|
assert.ok(await height.evaluate(i => !!i), 'the height field')
|
|
623
645
|
const at = await centre(height)
|
|
@@ -626,8 +648,8 @@ describe('the modeller made easier to find and to keep (2026-09-26 baseline)', (
|
|
|
626
648
|
await page().keyboard.press('Enter')
|
|
627
649
|
await sleep(600)
|
|
628
650
|
const now = await body('box')
|
|
629
|
-
/* The box is drawn
|
|
630
|
-
assert.equal(Math.round(now
|
|
651
|
+
/* The box is drawn along y from its start: its middle is its origin plus the start plus half the length. */
|
|
652
|
+
assert.equal(Math.round(middleOf(now)), Math.round(middleOf(was)), `its middle where it was: ${middleOf(was)} -> ${middleOf(now)}`)
|
|
631
653
|
assert.equal(now.dimensions.length, 1000)
|
|
632
654
|
})
|
|
633
655
|
|
|
@@ -895,14 +917,14 @@ describe('a row that carries a part (ADR-0093, 2026-09-26)', () => {
|
|
|
895
917
|
|
|
896
918
|
describe('the row section opens on one click', () => {
|
|
897
919
|
test('a part that can be made a row: one click on the folded heading opens it, a second folds it', async () => {
|
|
898
|
-
await mount('
|
|
899
|
-
await click(await read(() => probe.row('
|
|
920
|
+
await mount('shelf')
|
|
921
|
+
await click(await read(() => probe.row('slat')))
|
|
900
922
|
await tab('response')
|
|
901
923
|
const summary = await page().evaluateHandle(() => {
|
|
902
924
|
const deep = (root = document) => [...root.querySelectorAll('details[row-fold] > summary')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
|
|
903
925
|
return deep()[0] ?? null
|
|
904
926
|
})
|
|
905
|
-
assert.ok(await summary.evaluate(s => !!s && !!s.parentElement.querySelector('button[act]')), 'the
|
|
927
|
+
assert.ok(await summary.evaluate(s => !!s && !!s.parentElement.querySelector('button[act]')), 'the slat is offered a row to make')
|
|
906
928
|
const at = await summary.evaluate(s => {
|
|
907
929
|
s.scrollIntoView({ block: 'center' })
|
|
908
930
|
const r = s.getBoundingClientRect()
|
|
@@ -915,9 +937,9 @@ describe('the row section opens on one click', () => {
|
|
|
915
937
|
})
|
|
916
938
|
|
|
917
939
|
test('a part picked again: a fold opened at once stays open -- the folds kept for the part are put back first', async () => {
|
|
918
|
-
await mount('
|
|
919
|
-
await click(await read(() => probe.row('
|
|
920
|
-
await click(await read(() => probe.row('
|
|
940
|
+
await mount('shelf')
|
|
941
|
+
await click(await read(() => probe.row('slat')))
|
|
942
|
+
await click(await read(() => probe.row('bed')))
|
|
921
943
|
const open = await read(async () => {
|
|
922
944
|
const deep = (selector, root = document) => [...root.querySelectorAll(selector)].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(selector, e.shadowRoot)))
|
|
923
945
|
const editor = deep('figure-editor')[0]
|
|
@@ -925,7 +947,7 @@ describe('the row section opens on one click', () => {
|
|
|
925
947
|
The panel is drawn for the part once the editor and its links have updated; a click can come any time after
|
|
926
948
|
that, before a frame is drawn. Here it comes right then.
|
|
927
949
|
*/
|
|
928
|
-
editor.selected = '
|
|
950
|
+
editor.selected = 'slat'
|
|
929
951
|
await editor.updateComplete
|
|
930
952
|
await Promise.all(deep('figure-links').map(links => links.updateComplete))
|
|
931
953
|
await null
|
|
@@ -8,7 +8,6 @@ import './modeller-page.js'
|
|
|
8
8
|
import { captureFigureThumbnail } from 'figure-ui-client/modeller/thumbnail.js'
|
|
9
9
|
import { drawableFigureParts } from 'figure-ui-client/modeller/renderer.js'
|
|
10
10
|
import { previewTypeOf } from '@things-factory/figure-viewer/dist-client/preview-model.js'
|
|
11
|
-
import { figureReportOf } from 'figure-ui-client/modeller/maturity.js'
|
|
12
11
|
import { hasFigure } from '@hatiolab/things-scene'
|
|
13
12
|
import {
|
|
14
13
|
addFigureBox,
|
|
@@ -118,7 +117,6 @@ window.screenCheck = {
|
|
|
118
117
|
registered: type => hasFigure(type),
|
|
119
118
|
previewTypeOf,
|
|
120
119
|
/** The report the verdict tab measures, for the figure the editor holds. */
|
|
121
|
-
capacityNow: () => figureReportOf(window.screenCheck.editor().session.revisionNow().asset)?.capacity.instances,
|
|
122
120
|
/** The card's picture of a figure (saved text), as a save takes it. */
|
|
123
121
|
async picture(text) {
|
|
124
122
|
const asset = JSON.parse(text)
|
|
@@ -223,9 +223,9 @@ describe('the frame: parts · canvas · one side of seven tabs, and the bar over
|
|
|
223
223
|
await pressTab(TAB('properties'))
|
|
224
224
|
})
|
|
225
225
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
await
|
|
226
|
+
/* The count once drawn on the verdict tab covered its icon and was not something to act on (2026-10-20): none. */
|
|
227
|
+
test('the verdict tab carries no count over its icon', async () => {
|
|
228
|
+
assert.equal(await read(() => screenCheck.editor().shadowRoot.querySelectorAll('button[tab] span[badge]').length), 0)
|
|
229
229
|
})
|
|
230
230
|
|
|
231
231
|
test('nothing chosen: the properties panel opens on the figure\'s own settings, with no part head', async () => {
|
|
@@ -240,8 +240,11 @@ describe('the frame: parts · canvas · one side of seven tabs, and the bar over
|
|
|
240
240
|
assert.equal(await read(() => screenCheck.editor().shadowRoot.querySelector('ox-canvas-bar').pane === screenCheck.editor().shadowRoot.querySelector('ox-three-pane')), true, 'the bar folds the editor\'s own frame')
|
|
241
241
|
const titles = await read(() => ['undo', 'redo'].map(a => screenCheck.editor().shadowRoot.querySelector('ox-canvas-bar').shadowRoot.querySelector(`button[data-action=${a}]`).title))
|
|
242
242
|
assert.ok(titles[0].endsWith('(Ctrl+Z)') && titles[1].endsWith('(Ctrl+Y)'), `the keys in the titles: ${titles}`)
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
/* Saved or not is the save button's: pressable with something to save, dimmed without -- no words beside it. */
|
|
244
|
+
const status = () => read(() => screenCheck.editor().shadowRoot.querySelector('ox-canvas-bar [role=status]')?.textContent.trim() ?? null)
|
|
245
|
+
const saveOn = () => read(() => !screenCheck.editor().shadowRoot.querySelector('ox-canvas-bar button[save]').disabled)
|
|
246
|
+
assert.equal(await status(), null, 'nothing said while nothing is under way')
|
|
247
|
+
assert.equal(await saveOn(), false, 'saved: the save button is dimmed')
|
|
245
248
|
|
|
246
249
|
await pressRow('box')
|
|
247
250
|
const height = await element('the height field', () => screenCheck.deep('input').find(i => i.getAttribute('aria-label') === 'box.size.y'))
|
|
@@ -249,14 +252,15 @@ describe('the frame: parts · canvas · one side of seven tabs, and the bar over
|
|
|
249
252
|
await page().keyboard.type('600')
|
|
250
253
|
await page().keyboard.press('Enter')
|
|
251
254
|
await until('the height is 600', () => probe.value('box.size.y') === 600)
|
|
252
|
-
await until('
|
|
255
|
+
await until('something to save', () => !screenCheck.editor().shadowRoot.querySelector('ox-canvas-bar button[save]').disabled)
|
|
256
|
+
assert.equal(await status(), null, 'and still no words beside it')
|
|
253
257
|
|
|
254
258
|
await pressBar('undo')
|
|
255
259
|
await until('undone', () => probe.value('box.size.y') === 400)
|
|
256
260
|
await pressBar('redo')
|
|
257
261
|
await until('redone', () => probe.value('box.size.y') === 600)
|
|
258
262
|
await press('save', () => screenCheck.editor().shadowRoot.querySelector('ox-canvas-bar button[save]'))
|
|
259
|
-
await until('
|
|
263
|
+
await until('saved: dimmed again', () => screenCheck.editor().shadowRoot.querySelector('ox-canvas-bar button[save]').disabled)
|
|
260
264
|
|
|
261
265
|
const width = () => read(() => Math.round(screenCheck.deep('figure-canvas')[0].getBoundingClientRect().width))
|
|
262
266
|
const was = await width()
|
|
@@ -1077,12 +1081,6 @@ describe('the modeller page: the editor with the page\'s figure panel, verdict a
|
|
|
1077
1081
|
await press('the release button', () => screenCheck.editor().shadowRoot.querySelector('ox-canvas-bar button[publish]'))
|
|
1078
1082
|
})
|
|
1079
1083
|
|
|
1080
|
-
test('the verdict tab\'s count is the report\'s, measured by the verdict', async () => {
|
|
1081
|
-
const expected = await read(() => screenCheck.capacityNow())
|
|
1082
|
-
assert.ok(expected > 0)
|
|
1083
|
-
await until('the count on the tab', expected => screenCheck.editor().shadowRoot.querySelector('button[tab][aria-label="figure.label.tab-short-verdict"] span[badge]')?.textContent.trim() === expected.toLocaleString(), expected)
|
|
1084
|
-
})
|
|
1085
|
-
|
|
1086
1084
|
test('a new type asks the verdict again', async () => {
|
|
1087
1085
|
await pressTab(TAB('properties'))
|
|
1088
1086
|
await pressRow('left')
|
|
@@ -291,11 +291,11 @@ describe('the section editor on a box: what follows, what is kept, and a figure
|
|
|
291
291
|
await openEditor()
|
|
292
292
|
})
|
|
293
293
|
|
|
294
|
-
test('the length says
|
|
294
|
+
test('the length says what a length typed keeps: the part\'s middle (ADR-0140 4.1)', async () => {
|
|
295
295
|
const now = await editorNow()
|
|
296
296
|
assert.equal(now.length.label, 'figure.label.section-editor-length')
|
|
297
297
|
assert.equal(now.length.disabled, false)
|
|
298
|
-
assert.equal(now.length.note, 'figure.text.
|
|
298
|
+
assert.equal(now.length.note, 'figure.text.size-keeps-centre')
|
|
299
299
|
})
|
|
300
300
|
|
|
301
301
|
test('the figure changed while the editor is open: apply is refused, and closing does not offer it', async () => {
|