Skip to content

Commit c2dae5e

Browse files
author
DavidQ
committed
Restore Object Preview grid step and scale object drawing to coordinate grid counts - PR_26133_004-object-preview-object-scale-grid-step-restore
1 parent 4ee1038 commit c2dae5e

4 files changed

Lines changed: 154 additions & 74 deletions

File tree

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# PR_26133_003 Playwright V8 Coverage Report
1+
# PR_26133_004 Playwright V8 Coverage Report
22

33
Coverage source: `docs/dev/reports/playwright_v8_coverage_report.txt`, refreshed by the final `npm run test:workspace-v2` run.
44

@@ -7,12 +7,13 @@ Coverage source: `docs/dev/reports/playwright_v8_coverage_report.txt`, refreshed
77
- Coverage is advisory only; no thresholds are enforced.
88
- Workspace Manager V2 entry point: 91%.
99
- Object Vector Studio V2 runtime coverage entries from the generated report:
10-
- `tools/object-vector-studio-v2/js/ToolStarterApp.js`: 90%, executed lines 3270/3270, executed functions 342/378.
10+
- `tools/object-vector-studio-v2/js/ToolStarterApp.js`: 91%, executed lines 3304/3304, executed functions 346/381.
1111
- `tools/object-vector-studio-v2/js/bootstrap.js`: 80%, executed lines 99/99, executed functions 4/5.
12-
- The generated report lists no low-coverage changed runtime JS files.
12+
- The generated report lists `tools/object-vector-studio-v2/js/ToolStarterApp.js` as a changed JS file with browser V8 coverage.
1313

1414
## Validation Context
1515

1616
- Main command: `npm run test:workspace-v2`.
1717
- Result: 46 passed.
1818
- Focused Object Vector Studio V2 layout and preview-coordinate scenarios also passed.
19+
- Manual Object Preview probe confirmed restored 10-unit grid spacing, 10x object drawing scale, zoom/pan/reset behavior, and no console/runtime errors.
Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
1-
# PR_26133_003 Workspace V2 Results
1+
# PR_26133_004 Workspace V2 Results
22

33
## Command Results
44

55
- `node --check tools/object-vector-studio-v2/js/ToolStarterApp.js`: passed.
6-
- `node --check tools/object-vector-studio-v2/js/bootstrap.js`: passed.
76
- `node --check tests/playwright/tools/WorkspaceManagerV2.spec.mjs`: passed.
87
- `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --grep "Object Vector Studio V2 (layout shell|preview coordinates)"`: 2 passed.
9-
- `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --grep "blocks Workspace Manager V2 Save when the toolState file fails schema validation"`: 1 passed.
10-
- Final `npm run test:workspace-v2`: 46 passed.
11-
- `git diff --check`: passed.
8+
- `npm run test:workspace-v2`: 46 passed.
9+
- `git diff --check`: passed with the existing LF-to-CRLF working-copy warning for `tests/playwright/tools/WorkspaceManagerV2.spec.mjs`.
1210

1311
## Targeted Object Preview Verification
1412

15-
- Standalone Playwright manual pass reported `consoleErrors: []` and `pageErrors: []`.
16-
- Object Preview canvas filled the available work-area width at 1280px and after resizing to 1040px.
17-
- Render surface aspect ratio stayed aligned with the SVG viewBox: measured `1.4545` before and after resize.
18-
- Visible grid lines now map to coordinate units 1:1.
19-
- Asteroids ship points `0,-18`, `14,16`, `0,8`, `-14,16` aligned directly on visible grid lines.
20-
- Asteroids ship vertical span measured 18 visible grid lines above origin and 16 below origin.
21-
- Existing zoom viewBox behavior remained covered by the Object Vector Studio V2 layout scenario.
13+
- Standalone Playwright manual probe reported `consoleErrors: []` and `pageErrors: []`.
14+
- Object Preview grid rendered with 10-unit visible spacing.
15+
- Object Preview shape drawing used 10x preview scale while preserving persisted object coordinates.
16+
- Asteroids ship source points `0,-18`, `14,16`, `0,8`, `-14,16` rendered as `0,-180`, `140,160`, `0,80`, `-140,160`.
17+
- At 25% zoom, the Asteroids ship measured 18 visible grid lines above origin and 16 visible grid lines below origin.
18+
- Object Preview canvas filled available horizontal work-area width and kept the SVG viewBox aspect ratio stable.
19+
- Zoom, pan, and reset view remained functional: panned viewBox `-620 -440 1280 880`, reset viewBox `-160 -110 320 220`.
2220

2321
## Contract Checks
2422

23+
- `const GRID_STEP = 10;` is restored in Object Preview.
24+
- Canvas side-to-side fit and aspect-ratio behavior from the prior PR are preserved.
2525
- No sample JSON files were modified.
2626
- No unrelated tool/runtime files were changed.
2727
- No fallback behavior was added.

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,22 +1592,27 @@ test.describe("Workspace Manager V2 bootstrap", () => {
15921592
return {
15931593
aspectRatioMatchesViewBox: Math.abs((surfaceRect.width / surfaceRect.height) - (viewBox[2] / viewBox[3])) < 0.02,
15941594
canvasFillsAvailableWidth: Math.abs(surfaceRect.width - availableWidth) <= 2,
1595+
gridStepRestored: Math.min(...verticalLines.filter((x) => x > 0)) === 10 && Math.min(...horizontalLines.filter((y) => y > 0)) === 10,
15951596
rectangle: {
15961597
height: Number(rectangle.getAttribute("height")),
15971598
width: Number(rectangle.getAttribute("width")),
15981599
x: Number(rectangle.getAttribute("x")),
15991600
y: Number(rectangle.getAttribute("y"))
16001601
},
1601-
rectangleSpansGridLines: verticalLines.includes(-80) && verticalLines.includes(0) && horizontalLines.includes(-30) && horizontalLines.includes(30),
1602-
unitGridSpacing: verticalLines.includes(-80) && verticalLines.includes(-79) && horizontalLines.includes(-30) && horizontalLines.includes(-29)
1602+
rectangleUsesPreviewDrawingScale: Number(rectangle.getAttribute("x")) === -800
1603+
&& Number(rectangle.getAttribute("y")) === -300
1604+
&& Number(rectangle.getAttribute("width")) === 800
1605+
&& Number(rectangle.getAttribute("height")) === 600,
1606+
unitGridSpacingRemoved: !verticalLines.includes(-79) && !horizontalLines.includes(-29)
16031607
};
16041608
});
16051609
expect(gridObjectScale).toEqual({
16061610
aspectRatioMatchesViewBox: true,
16071611
canvasFillsAvailableWidth: true,
1608-
rectangle: { height: 60, width: 80, x: -80, y: -30 },
1609-
rectangleSpansGridLines: true,
1610-
unitGridSpacing: true
1612+
gridStepRestored: true,
1613+
rectangle: { height: 600, width: 800, x: -800, y: -300 },
1614+
rectangleUsesPreviewDrawingScale: true,
1615+
unitGridSpacingRemoved: true
16111616
});
16121617
await expect(page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1'] [data-object-control='visibility']")).toHaveText("");
16131618
await expect(page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1'] [data-object-control='lock']")).toHaveText("");
@@ -1707,13 +1712,21 @@ test.describe("Workspace Manager V2 bootstrap", () => {
17071712
});
17081713
expect(selectionChrome).toEqual({ handleHeight: 3, handleWidth: 3, selectionStrokeWidth: "0.75px" });
17091714

1710-
await page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='circle-2']").click({ modifiers: ["Shift"] });
1715+
const clickPreviewShape = async (shapeId, eventInit = {}) => {
1716+
await page.locator(`#objectVectorStudioV2RenderSurface [data-shape-id='${shapeId}']`).dispatchEvent("click", {
1717+
bubbles: true,
1718+
cancelable: true,
1719+
...eventInit
1720+
});
1721+
};
1722+
1723+
await clickPreviewShape("circle-2", { shiftKey: true });
17111724
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='rectangle-1']")).toHaveClass(/is-selected/);
17121725
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='circle-2']")).toHaveClass(/is-selected/);
17131726
await expect(page.locator("[data-palette-color='#ffffff']")).toHaveClass(/is-selected/);
17141727
await expect(page.locator("#statusLog")).toHaveValue(/Multi-select count: 2/);
17151728
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"selectedShapeIds"');
1716-
await page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='rectangle-1']").click();
1729+
await clickPreviewShape("rectangle-1");
17171730
await expect(page.locator("[data-palette-color='#6fd3ff']")).toHaveClass(/is-selected/);
17181731
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Rectangle Geometry");
17191732
const reviewLayoutState = await page.evaluate(() => {
@@ -1798,24 +1811,24 @@ test.describe("Workspace Manager V2 bootstrap", () => {
17981811

17991812
await page.keyboard.press("F");
18001813
await page.locator("[data-palette-color='#6fd3ff']").click();
1801-
await page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='circle-2']").click();
1814+
await clickPreviewShape("circle-2");
18021815
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"fill": "#6fd3ff"');
18031816
await expect(page.locator("#statusLog")).toHaveValue(/OK Applied palette color #6fd3ff from cyan to shape circle-2 by render surface click\. Target: paint\./);
18041817
await page.keyboard.press("S");
1805-
await page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='circle-2']").click();
1818+
await clickPreviewShape("circle-2");
18061819
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"stroke": "#ffffff"');
18071820
await expect(page.locator("#statusLog")).toHaveValue(/OK Applied palette color #ffffff from white to shape circle-2 by render surface click\. Target: stroke width 2\./);
18081821
await page.evaluate(() => {
18091822
window.__objectVectorStudioV2App.selectedStrokeColor = "#123456";
18101823
window.__objectVectorStudioV2App.selectedStrokeLabel = "manual rogue";
18111824
window.__objectVectorStudioV2App.activeTool = "stroke";
18121825
});
1813-
await page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='circle-2']").click();
1826+
await clickPreviewShape("circle-2");
18141827
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Palette color application rejected: #123456 is not in the loaded palette\./);
18151828
await expect(page.locator("#objectVectorStudioV2JsonDetails")).not.toContainText("#123456");
18161829
await page.locator("[data-palette-color='#ffffff']").click();
18171830
await page.keyboard.press("I");
1818-
await page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='circle-2']").click();
1831+
await clickPreviewShape("circle-2");
18191832
await expect(page.locator("#statusLog")).toHaveValue(/OK Sampled stroke color #ffffff from shape circle-2\./);
18201833
await page.keyboard.press("X");
18211834
await expect(page.locator("#statusLog")).toHaveValue(/OK Swapped fill and stroke colors/);
@@ -2109,6 +2122,12 @@ test.describe("Workspace Manager V2 bootstrap", () => {
21092122
name: "asteroids-ship-grid.object-vector.json"
21102123
});
21112124
await expect(page.locator("#statusLog")).toHaveValue(/OK Render mode svg-work-surface: rendered Asteroids Ship Grid Check with 1 visible shapes; capture mode none\./);
2125+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-160 -110 320 220");
2126+
for (let index = 0; index < 8; index += 1) {
2127+
await page.locator("#objectVectorStudioV2ZoomOutButton").click();
2128+
}
2129+
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toContainText("Zoom 25%");
2130+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-640 -440 1280 880");
21122131

21132132
const readPreviewScale = async () => page.locator("#objectVectorStudioV2RenderSurface").evaluate((surface) => {
21142133
const workArea = document.querySelector(".object-vector-studio-v2__work-area");
@@ -2129,29 +2148,38 @@ test.describe("Workspace Manager V2 bootstrap", () => {
21292148
const transformed = point.matrixTransform(surface.getScreenCTM());
21302149
return { x: transformed.x, y: transformed.y };
21312150
};
2132-
const shipPoints = [
2151+
const sourceShipPoints = [
21332152
{ x: 0, y: -18 },
21342153
{ x: 14, y: 16 },
21352154
{ x: 0, y: 8 },
21362155
{ x: -14, y: 16 }
21372156
];
2157+
const shipPoints = sourceShipPoints.map((point) => ({ x: point.x * 10, y: point.y * 10 }));
2158+
const drawnPoints = surface.querySelector("[data-shape-id='asteroids-ship-outline']").getAttribute("points")
2159+
.split(/\s+/)
2160+
.map((entry) => {
2161+
const [x, y] = entry.split(",").map(Number);
2162+
return { x, y };
2163+
});
21382164
const pointScreens = shipPoints.map((point) => screenPoint(point.x, point.y));
21392165
const lineScreens = {
2140-
bottom: screenPoint(0, 16).y,
2166+
bottom: screenPoint(0, 160).y,
21412167
origin: screenPoint(0, 0).y,
2142-
top: screenPoint(0, -18).y
2168+
top: screenPoint(0, -180).y
21432169
};
21442170
return {
21452171
aspectRatioStable: Math.abs((surfaceRect.width / surfaceRect.height) - (viewBox[2] / viewBox[3])) < 0.02,
21462172
canvasFillsAvailableWidth: Math.abs(surfaceRect.width - availableWidth) <= 2,
2147-
gridLinesAboveOrigin: horizontalLines.filter((y) => y < 0 && y >= -18).length,
2148-
gridLinesBelowOrigin: horizontalLines.filter((y) => y > 0 && y <= 16).length,
2173+
drawnPoints,
2174+
gridLinesAboveOrigin: horizontalLines.filter((y) => y < 0 && y >= -180).length,
2175+
gridLinesBelowOrigin: horizontalLines.filter((y) => y > 0 && y <= 160).length,
2176+
gridStepRestored: Math.min(...verticalLines.filter((x) => x > 0)) === 10 && Math.min(...horizontalLines.filter((y) => y > 0)) === 10,
21492177
originCentered: Math.abs(lineScreens.origin - (surfaceRect.top + surfaceRect.height / 2)) <= 1,
21502178
pointsOnVisibleGridLines: shipPoints.every((point) => horizontalLines.includes(point.y) && verticalLines.includes(point.x)),
21512179
pointScreensMatchGrid: Math.abs(pointScreens[0].y - lineScreens.top) <= 0.01
21522180
&& Math.abs(pointScreens[1].y - lineScreens.bottom) <= 0.01
21532181
&& Math.abs(pointScreens[3].y - lineScreens.bottom) <= 0.01,
2154-
unitGridSpacing: horizontalLines.includes(-18) && horizontalLines.includes(-17) && horizontalLines.includes(15) && horizontalLines.includes(16),
2182+
unitGridSpacingRemoved: !horizontalLines.includes(-18) && !horizontalLines.includes(-17) && !horizontalLines.includes(15) && !horizontalLines.includes(16),
21552183
viewBox: surface.getAttribute("viewBox")
21562184
};
21572185
});
@@ -2160,22 +2188,36 @@ test.describe("Workspace Manager V2 bootstrap", () => {
21602188
expect(initialPreviewScale).toEqual({
21612189
aspectRatioStable: true,
21622190
canvasFillsAvailableWidth: true,
2191+
drawnPoints: [
2192+
{ x: 0, y: -180 },
2193+
{ x: 140, y: 160 },
2194+
{ x: 0, y: 80 },
2195+
{ x: -140, y: 160 }
2196+
],
21632197
gridLinesAboveOrigin: 18,
21642198
gridLinesBelowOrigin: 16,
2199+
gridStepRestored: true,
21652200
originCentered: true,
21662201
pointsOnVisibleGridLines: true,
21672202
pointScreensMatchGrid: true,
2168-
unitGridSpacing: true,
2169-
viewBox: "-160 -110 320 220"
2203+
unitGridSpacingRemoved: true,
2204+
viewBox: "-640 -440 1280 880"
21702205
});
21712206

2207+
await page.locator("#objectVectorStudioV2PanRightButton").click();
2208+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-620 -440 1280 880");
2209+
await page.locator("#objectVectorStudioV2PanLeftButton").click();
2210+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-640 -440 1280 880");
21722211
await page.setViewportSize({ width: 1040, height: 720 });
21732212
const resizedPreviewScale = await readPreviewScale();
21742213
expect(resizedPreviewScale.aspectRatioStable).toBe(true);
21752214
expect(resizedPreviewScale.canvasFillsAvailableWidth).toBe(true);
21762215
expect(resizedPreviewScale.gridLinesAboveOrigin).toBe(18);
21772216
expect(resizedPreviewScale.gridLinesBelowOrigin).toBe(16);
2217+
expect(resizedPreviewScale.gridStepRestored).toBe(true);
21782218
expect(resizedPreviewScale.pointsOnVisibleGridLines).toBe(true);
2219+
await page.locator("#objectVectorStudioV2ResetViewButton").click();
2220+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-160 -110 320 220");
21792221

21802222
expect(pageErrors).toEqual([]);
21812223
expect(consoleErrors).toEqual([]);

0 commit comments

Comments
 (0)