Wizard DXF: anteprima immagine, anteprima DXF e Prova usano <tmf-viewer>
Refs #122 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
/**
|
||||
* #113: zoom, Adatta e pan sulle anteprime del wizard (immagine e DXF), con plain node.
|
||||
* #122: lo zoom delle tre viste del wizard (immagine, DXF, Prova) e' nel componente <tmf-viewer>.
|
||||
* Qui si prova cio' che resta nel wizard (dimensioni dal detail di viewer-load, anteprima DXF)
|
||||
* e che le funzioni di zoom/pan/fit/overlay non ci siano piu'.
|
||||
* (Il nome del file resta quello di #113 per non rompere i riferimenti.)
|
||||
*
|
||||
* Run from src/frontend/flask_app: node tests/js/test_dxf_zoom.js
|
||||
*/
|
||||
@@ -18,109 +21,35 @@ function check(label, cond, extra) {
|
||||
if (cond) console.log('ok - ' + label);
|
||||
else { failures++; console.log('FAIL - ' + label + (extra ? ' :: ' + extra : '')); }
|
||||
}
|
||||
const near = (a, b) => Math.abs(a - b) < 1e-9 * Math.max(1, Math.abs(b));
|
||||
|
||||
// Wizard con viewport finti e $nextTick sincrono
|
||||
function make(cw) {
|
||||
const w = dxfRecipeWizard(1, () => 'tok', {});
|
||||
const vp = () => ({ clientWidth: cw, clientHeight: 238, scrollLeft: 5, scrollTop: 5 });
|
||||
w.$refs = { vpImg: vp(), vpDxf: vp() };
|
||||
w.$nextTick = fn => fn();
|
||||
return w;
|
||||
}
|
||||
const w = dxfRecipeWizard(1, () => 'tok', {});
|
||||
w.$nextTick = fn => fn();
|
||||
|
||||
// img: viewport 400 x 238 utili, immagine 2856 x 2848
|
||||
let w = make(400);
|
||||
w.vSetContent('img', 2856, 2848);
|
||||
check('#113 img: Adatta = clientHeight/2848', near(w.views.img.zoom, 238 / 2848) && near(w.views.img.fit, 238 / 2848), String(w.views.img.zoom));
|
||||
check('#113 img: scroll azzerato dopo Adatta', w.$refs.vpImg.scrollLeft === 0 && w.$refs.vpImg.scrollTop === 0);
|
||||
check('#113 img: percentuale = zoom*100', w.vPct('img') === Math.round(238 / 2848 * 100) + ' %', w.vPct('img'));
|
||||
const z0 = w.views.img.zoom;
|
||||
w.vZoomBy('img', 1.25);
|
||||
check('#113 img: zoomBy moltiplica', near(w.views.img.zoom, z0 * 1.25));
|
||||
check('#113 img: dopo zoom si puo trascinare', w.vCanPan('img') === true && w.vCursor('img') === 'grab');
|
||||
w.vZoomBy('img', 1e6);
|
||||
check('#113 img: limite alto 8', w.views.img.zoom === 8);
|
||||
w.vZoomBy('img', 1e-9);
|
||||
check('#113 img: limite basso 0.05', w.views.img.zoom === 0.05);
|
||||
w.vFit('img');
|
||||
check('#113 img: Adatta torna al fit', near(w.views.img.zoom, z0) && w.vCanPan('img') === false);
|
||||
// clientHeight 0 (non misurabile): ripiego 240
|
||||
w = make(400); w.$refs.vpImg.clientHeight = 0; w.vSetContent('img', 2856, 2848);
|
||||
check('#113 img: clientHeight 0 usa 240', near(w.views.img.fit, 240 / 2848));
|
||||
w = make(400); w.vSetContent('img', 2856, 2848);
|
||||
check('#113 img: l\'altra vista non si muove', w.views.dxf.w === 0 && w.views.dxf.zoom === 1);
|
||||
check('#113 img: wrapper w*zoom', near(w.vW('img'), 2856 * z0) && near(w.vH('img'), 2848 * z0));
|
||||
// #122: via tutto lo zoom/pan/fit/overlay del wizard
|
||||
const gone = ['views', 'viewPanKey', 'zoom', 'fitZoom', 'panning', 'resPx', 'vZoomBy', 'vFit', 'vWheel', 'vPanDown',
|
||||
'vPanMove', 'vPanEnd', 'vCursor', 'vPct', 'vW', 'vH', 'vReset', 'vSetContent', 'zoomBy', 'fit', 'onWheel',
|
||||
'onPanDown', 'onPanMove', 'onPanEnd', 'onResultImgLoad', 'drawOverlay', 'zoomPct', 'zoomW', 'zoomH', 'vpHeight'];
|
||||
for (const k of gone) check('#122 tolto dal wizard: ' + k, !(k in w));
|
||||
check('#122 sorgente senza $refs di zoom', !/\$refs\./.test(src));
|
||||
|
||||
// dxf: viewBox 20 x 10 mm, viewport 400 x 238 -> fit = min(20, 23.8) = 20
|
||||
w = make(400);
|
||||
w.vSetContent('dxf', 20, 10);
|
||||
check('#113 dxf: fit = min(vpW/w, clientHeight/h)', near(w.views.dxf.fit, 20), String(w.views.dxf.fit));
|
||||
check('#113 dxf: dopo Adatta 100 %', w.vPct('dxf') === '100 %', w.vPct('dxf'));
|
||||
w.vZoomBy('dxf', 2);
|
||||
check('#113 dxf: zoom 2x = 200 %', w.vPct('dxf') === '200 %', w.vPct('dxf'));
|
||||
w.vZoomBy('dxf', 1e9);
|
||||
check('#113 dxf: si ferma a fit*50', near(w.views.dxf.zoom, 20 * 50) && w.vPct('dxf') === '5000 %', String(w.views.dxf.zoom));
|
||||
w.vZoomBy('dxf', 1e-9);
|
||||
check('#113 dxf: si ferma a fit*0.5', near(w.views.dxf.zoom, 10) && w.vPct('dxf') === '50 %', String(w.views.dxf.zoom));
|
||||
|
||||
// vFit con viewport largo 0 (nascosto): stato invariato
|
||||
w = make(0);
|
||||
w.vSetContent('img', 1000, 500);
|
||||
check('#113 clientWidth 0: nessun fit, stato invariato', w.views.img.zoom === 1 && w.views.img.fit === 1 && w.views.img.w === 1000);
|
||||
w.$refs.vpImg.clientWidth = 500;
|
||||
w.vFit('img');
|
||||
check('#113 viewport visibile: il fit parte', near(w.views.img.fit, 238 / 500), String(w.views.img.fit));
|
||||
|
||||
// Reset: nuova immagine / DXF tolto
|
||||
w.vReset('img');
|
||||
check('#113 reset: contenuto e zoom azzerati', w.views.img.w === 0 && w.views.img.zoom === 1 && w.vCanPan('img') === false);
|
||||
w.vZoomBy('img', 2);
|
||||
check('#113 zoomBy senza contenuto: ignorato', w.views.img.zoom === 1);
|
||||
|
||||
// Integrazione: onImagePreviewLoad e _setImage
|
||||
// onImagePreviewLoad riceve il detail di viewer-load e alimenta il riepilogo
|
||||
global.URL.createObjectURL = () => 'blob:x';
|
||||
global.URL.revokeObjectURL = () => {};
|
||||
w = make(400);
|
||||
w._setImage({ name: 'a.bmp' }, 'file');
|
||||
w.onImagePreviewLoad({ naturalWidth: 800, naturalHeight: 240 });
|
||||
check('#113 onImagePreviewLoad: Adatta all\'arrivo', near(w.views.img.zoom, 0.5) && w.views.img.w === 800, String(w.views.img.zoom));
|
||||
w._setImage({ name: 'b.bmp' }, 'file');
|
||||
check('#113 _setImage: vista azzerata', w.views.img.w === 0 && w.views.img.zoom === 1);
|
||||
w.onImagePreviewLoad({ naturalWidth: 800, naturalHeight: 240 });
|
||||
w.onImagePreviewLoad({ width: 800, height: 240 });
|
||||
check('#122 viewer-load: dimensioni nel riepilogo', w.imagePx.w === 800 && w.imagePx.h === 240 && w.imageBroken === false);
|
||||
check('#122 riepilogo «W × H px»', /800.*240 px/.test(w.imagePreviewSummary), w.imagePreviewSummary);
|
||||
w.onImagePreviewLoad({ width: 0, height: 0 });
|
||||
check('#122 viewer-load senza dimensioni: imagePx null', w.imagePx === null);
|
||||
w.onImagePreviewLoad({ width: 800, height: 240 });
|
||||
w.onImagePreviewError();
|
||||
check('#113 errore <img>: vista azzerata', w.views.img.w === 0);
|
||||
check('#122 errore immagine: avviso e dimensioni azzerate', w.imageBroken === true && w.imagePx === null);
|
||||
w._setImage({ name: 'b.bmp' }, 'file');
|
||||
check('#122 nuova immagine: avviso tolto', w.imageBroken === false && w.imagePx === null);
|
||||
w._setImage(null);
|
||||
check('#122 immagine tolta: src vuoto', w.imageSrc === '' && w.imagePreviewUrl === '');
|
||||
|
||||
// Wheel: solo con Ctrl
|
||||
w = make(400);
|
||||
w.vSetContent('dxf', 20, 10);
|
||||
let prevented = false;
|
||||
w.vWheel('dxf', { ctrlKey: false, deltaY: -1, preventDefault() { prevented = true; } });
|
||||
check('#113 rotella senza Ctrl: niente zoom', w.views.dxf.zoom === 20 && !prevented);
|
||||
w.vWheel('dxf', { ctrlKey: true, deltaY: -1, preventDefault() { prevented = true; } });
|
||||
check('#113 Ctrl+rotella: zoom avanti', near(w.views.dxf.zoom, 25) && prevented);
|
||||
|
||||
// Pan
|
||||
w = make(400);
|
||||
w.vSetContent('dxf', 20, 10);
|
||||
w.vZoomBy('dxf', 2);
|
||||
const vp = w.$refs.vpDxf;
|
||||
vp.scrollLeft = 100; vp.scrollTop = 50; vp.setPointerCapture = () => {};
|
||||
w.vPanDown('dxf', { pointerType: 'mouse', button: 0, clientX: 200, clientY: 100, pointerId: 1 });
|
||||
w.vPanMove('dxf', { clientX: 150, clientY: 90 });
|
||||
check('#113 pan: scroll di quanto si muove il puntatore', vp.scrollLeft === 150 && vp.scrollTop === 60, vp.scrollLeft + ',' + vp.scrollTop);
|
||||
check('#113 pan: cursore grabbing', w.vCursor('dxf') === 'grabbing');
|
||||
w.vPanEnd();
|
||||
check('#113 pan finito', w.viewPanKey === '' && w._viewPan === null);
|
||||
|
||||
// #113: Adatta da ingrandito, con barre di scorrimento presenti: area = offset* meno i bordi, non client*
|
||||
global.getComputedStyle = () => ({ borderLeftWidth: '1px', borderRightWidth: '1px', borderTopWidth: '1px', borderBottomWidth: '1px' });
|
||||
w = make(432); w.$refs.vpImg.clientHeight = 228; w.$refs.vpImg.offsetWidth = 444; w.$refs.vpImg.offsetHeight = 240;
|
||||
w.vSetContent('img', 2464, 2056);
|
||||
check('#113 Adatta con barre: area senza barre (238/2056)', near(w.views.img.fit, 238 / 2056), String(w.views.img.fit));
|
||||
delete global.getComputedStyle;
|
||||
|
||||
// Integrazione DXF: loadDxfPreview imposta la misura del viewBox e fa Adatta; file tolto azzera
|
||||
// Anteprima DXF: il viewBox resta quello per il <tmf-viewer> (width/height del template)
|
||||
(async () => {
|
||||
const PREVIEW = {
|
||||
contorni: [[[0, 0], [20, 0], [20, 10]]],
|
||||
@@ -129,54 +58,17 @@ delete global.getComputedStyle;
|
||||
};
|
||||
global.FormData = class { append() {} };
|
||||
global.fetch = async () => ({ ok: true, json: async () => PREVIEW });
|
||||
const wd = make(400);
|
||||
const wd = dxfRecipeWizard(1, () => 'tok', {});
|
||||
wd.$nextTick = fn => fn();
|
||||
wd.dxfFile = { name: 'a.dxf' };
|
||||
await wd.loadDxfPreview();
|
||||
const vb = wd.dxfPreviewViewBox().split(' ').map(Number);
|
||||
check('#113 loadDxfPreview: misura = viewBox', near(wd.views.dxf.w, vb[2]) && near(wd.views.dxf.h, vb[3]), JSON.stringify(wd.views.dxf));
|
||||
check('#113 loadDxfPreview: Adatta = 100 %', wd.vPct('dxf') === '100 %' && wd.views.dxf.fit > 1);
|
||||
check('#122 DXF: viewBox con margine 5 % (w 22 x h 12)', vb.length === 4 && Math.abs(vb[2] - 22) < 1e-6 && Math.abs(vb[3] - 12) < 1e-6, vb.join(' '));
|
||||
check('#122 DXF: anteprima caricata', wd.dxfPreview && wd.dxfPreviewD().startsWith('M 0 0'));
|
||||
wd.dxfFile = null;
|
||||
await wd.loadDxfPreview();
|
||||
check('#113 DXF tolto: vista azzerata', wd.views.dxf.w === 0 && wd.views.dxf.zoom === 1);
|
||||
|
||||
// #121: overlay SVG al posto del canvas; ridisegno a ogni cambio di zoom (zoomBy, fit)
|
||||
{
|
||||
const calls = [];
|
||||
global.window = { VisionOverlay: { svg: (shapes, o) => { calls.push({ n: shapes.length, zoom: o.zoom }); return '<path d="M0 0"/>'; } } };
|
||||
const attrs = {};
|
||||
const sv = { innerHTML: '', setAttribute: (k, v) => { attrs[k] = v; } };
|
||||
const mk = () => {
|
||||
const x = make(400);
|
||||
x.$refs = { viewport: { clientWidth: 400, clientHeight: 300, scrollLeft: 0, scrollTop: 0 },
|
||||
img: { naturalWidth: 800, naturalHeight: 600 }, overlay: sv };
|
||||
x.result = { result: { overlay: [{ type: 'contour', role: 'found', rows: [1], cols: [2] }] } };
|
||||
return x;
|
||||
};
|
||||
const wo = mk();
|
||||
wo.drawOverlay();
|
||||
check('#121 viewBox = dimensioni naturali', attrs.viewBox === '0 0 800 600', attrs.viewBox);
|
||||
check('#121 innerHTML = VisionOverlay.svg', sv.innerHTML === '<path d="M0 0"/>' && calls[0].n === 1);
|
||||
wo.zoomBy(2);
|
||||
check('#121 zoomBy ridisegna col nuovo zoom', calls[calls.length - 1].zoom === wo.zoom && wo.zoom === 2, JSON.stringify(calls));
|
||||
wo.fit();
|
||||
check('#121 fit ridisegna col nuovo zoom', calls[calls.length - 1].zoom === wo.zoom, JSON.stringify(calls));
|
||||
// senza modulo o senza svg: vuoto, nessun errore
|
||||
global.window = {};
|
||||
sv.innerHTML = 'x';
|
||||
let err = null;
|
||||
try { mk().drawOverlay(); } catch (e) { err = e; }
|
||||
check('#121 senza VisionOverlay: vuoto senza errori', err === null && sv.innerHTML === '', String(err));
|
||||
global.window = { VisionOverlay: {} };
|
||||
sv.innerHTML = 'x';
|
||||
try { mk().drawOverlay(); } catch (e) { err = e; }
|
||||
check('#121 senza svg(): vuoto senza errori', err === null && sv.innerHTML === '', String(err));
|
||||
// immagine non ancora caricata: non tocca nulla
|
||||
const wn = mk(); wn.$refs.img.naturalWidth = 0; sv.innerHTML = 'y';
|
||||
wn.drawOverlay();
|
||||
check('#121 immagine non caricata: nessun disegno', sv.innerHTML === 'y');
|
||||
delete global.window;
|
||||
}
|
||||
check('#122 DXF tolto: anteprima azzerata', wd.dxfPreview === null && wd.dxfPreviewViewBox() === '0 0 1 1');
|
||||
|
||||
if (failures) { console.log(failures + ' test falliti'); process.exit(1); }
|
||||
console.log('tutti i test #113 ok');
|
||||
console.log('tutti i test #122 ok');
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user