Skip to content

Commit

Permalink
test (stackSegmantation): circular scissor, threshold and dynamic thr…
Browse files Browse the repository at this point in the history
…eshold tools (#1401)

* test (stackSegmantation): circular scissor, threshold and dynamic threshold tools

* test (stackSegmantation): circular scissor, threshold and dynamic threshold tools (screenshots)
  • Loading branch information
lscoder committed Jul 23, 2024
1 parent d8f12e4 commit 1371998
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 53 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
187 changes: 170 additions & 17 deletions tests/stackSegmentation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ import {
} from './utils/index';
import pause from './utils/pause';
import locatorToPageCoord from './utils/locatorToPageCoord';
import simulateDrawRect from './utils/simulateDrawRect';

const SEG1_OUTERCIRCLE_POINT = [210, 235];

const segmentPoints1 = [
const rightArmBoneContour = [
[100, 197],
[98, 221],
[115, 233],
Expand All @@ -20,12 +19,20 @@ const segmentPoints1 = [
[118, 194],
];

const segmentPoints2 = [
[397, 142],
[384, 171],
[391, 202],
[411, 181],
[414, 152],
const leftArmContour = [
[433, 167],
[432, 172],
[439, 219],
[424, 258],
[376, 263],
[348, 252],
[342, 223],
[365, 200],
[363, 181],
[352, 164],
[356, 144],
[371, 116],
[390, 93],
];

test.beforeEach(async ({ page }) => {
Expand Down Expand Up @@ -55,7 +62,7 @@ test.describe('Stack Segmentation', async () => {
test('should draw a new segment', async ({ page }) => {
const canvas = await page.locator('canvas');

await simulateDrawPath(page, canvas, segmentPoints1, {
await simulateDrawPath(page, canvas, rightArmBoneContour, {
interpolateSteps: true,
closePath: true,
});
Expand All @@ -73,7 +80,7 @@ test.describe('Stack Segmentation', async () => {
await page.getByRole('combobox').first().selectOption('CircularEraser');
});

test.describe('and it is on a segmentation 1 that has segments', async () => {
test.describe('and segmentation 1 that has segments is active', async () => {
test('should erase the pixels from both circular segments', async ({
page,
}) => {
Expand Down Expand Up @@ -108,12 +115,99 @@ test.describe('Stack Segmentation', async () => {
});
});

test.describe('when threshold brush tool is selected', async () => {
test.beforeEach(async ({ page }) => {
await page.getByRole('combobox').first().selectOption('ThresholdBrush');
await page.getByRole('slider').fill('25');
});

test('should paint the expected pixels based on the initial pixel value', async ({
page,
}) => {
const canvas = await page.locator('canvas');

await simulateDrawPath(page, canvas, leftArmContour, {
interpolateSteps: true,
closePath: true,
});

await checkForScreenshot(
page,
canvas,
screenShotPaths.stackSegmentation.thresholdBrushSegment1
);
});
});

test.describe('when dynamic threshold tool is selected', async () => {
test.beforeEach(async ({ page }) => {
await page.getByRole('combobox').first().selectOption('DynamicThreshold');
await page.getByRole('slider').fill('25');
});

test.describe('and the mouse stays at the same location for a few ms', async () => {
test('should highlight some pixels based on the pixel values at the mouse cursor', async ({
page,
}) => {
const canvas = await page.locator('canvas');
const canvasPoint = leftArmContour[0];
const pagePoint = await locatorToPageCoord(canvas, canvasPoint);

await page.mouse.move(pagePoint[0], pagePoint[1]);
await pause(1000);

await checkForScreenshot(
page,
canvas,
screenShotPaths.stackSegmentation
.dynamicThresholdInitialHighlightedPixels
);
});
});

test.describe('and the mouse is moved around with left button held down', async () => {
test.beforeEach(async ({ page }) => {
const canvas = await page.locator('canvas');

await simulateDrawPath(page, canvas, leftArmContour, {
interpolateSteps: true,
});
});

test('should highlight all pixels that are within the threshold', async ({
page,
}) => {
const canvas = await page.locator('canvas');

await checkForScreenshot(
page,
canvas,
screenShotPaths.stackSegmentation.dynamicThresholdHighlightedContour
);
});

test.describe('and the <ENTER> key is pressed', async () => {
test('should accept the pixels selected', async ({ page }) => {
const canvas = await page.locator('canvas');

page.keyboard.press('Enter');

await checkForScreenshot(
page,
canvas,
screenShotPaths.stackSegmentation.dynamicThresholdConfirmedContour
);
});
});
});
});

test.describe('when rectangle scissor tool is selected', async () => {
test.beforeEach(async ({ page }) => {
await page.getByRole('combobox').first().selectOption('RectangleScissor');
});

test.describe('and it is on a segmentation 1 that has segments', async () => {
test.describe('and segmentation 1 that has segments is active', async () => {
test('should fill the pixels within the rectangular region selected on segmentation 1', async ({
page,
}) => {
Expand All @@ -129,7 +223,7 @@ test.describe('Stack Segmentation', async () => {
});

test.describe('and it is on a segmentation 2 that has no segments', async () => {
test('should fill the pixels within the rectangular region selected on segmentation 2', async ({
test('should fill the pixels within the rectangular region selected on segmentation 2 preserving the segments on segmentation 1', async ({
page,
}) => {
const canvas = await page.locator('canvas');
Expand All @@ -148,12 +242,52 @@ test.describe('Stack Segmentation', async () => {
});
});

test.describe('when circular scissor tool is selected', async () => {
test.beforeEach(async ({ page }) => {
await page.getByRole('combobox').first().selectOption('CircleScissor');
});

test.describe('and segmentation 1 that has segments is active', async () => {
test('should fill the pixels within the circular region selected on segmentation 1', async ({
page,
}) => {
const canvas = await page.locator('canvas');

await drawCircleScissor(page, canvas);
await checkForScreenshot(
page,
canvas,
screenShotPaths.stackSegmentation.circularScissorSegmentation1
);
});
});

test.describe('and it is on a segmentation 2 that has no segments', async () => {
test('should fill the pixels within the circular region selected on segmentation 2 preserving the segments on segmentation 1', async ({
page,
}) => {
const canvas = await page.locator('canvas');

await page
.getByRole('button', { name: 'Create New Segmentation on' })
.click();

await drawCircleScissor(page, canvas);
await checkForScreenshot(
page,
canvas,
screenShotPaths.stackSegmentation.circularScissorSegmentation2
);
});
});
});

test.describe('when paint fill tool is selected', async () => {
test.beforeEach(async ({ page }) => {
await page.getByRole('combobox').first().selectOption('PaintFill');
});

test.describe('and clicking on the outer circle', async () => {
test.describe('and user clicks on the outer circle', async () => {
test('should fill the outer circle', async ({ page }) => {
const canvas = await page.locator('canvas');

Expand All @@ -166,8 +300,8 @@ test.describe('Stack Segmentation', async () => {
});
});

test.describe('and creating a new segmentation', async () => {
test.describe('and clicking on the outer circle', async () => {
test.describe('and a new segmentation is created', async () => {
test.describe('and user clicks on the outer circle', async () => {
test('should paint the entire image over the previous segmetantion', async ({
page,
}) => {
Expand All @@ -190,7 +324,7 @@ test.describe('Stack Segmentation', async () => {
});

async function runPaintFill(page, canvas, clickPoint: number[]) {
const pageCoord = await locatorToPageCoord(page, canvas, clickPoint);
const pageCoord = await locatorToPageCoord(canvas, clickPoint);
const toolsDropdown = await page.getByRole('combobox').first();
const selectedToolOption = await toolsDropdown.inputValue();

Expand Down Expand Up @@ -226,6 +360,24 @@ async function eraseVerticalLine(page, canvas) {
await toolsDropdown.selectOption(selectedToolOption);
}

async function drawCircleScissor(page, canvas) {
const toolsDropdown = await page.getByRole('combobox').first();
const selectedToolOption = await toolsDropdown.inputValue();

await toolsDropdown.selectOption('CircleScissor');

const width = Number(await canvas.getAttribute('width'));
const height = Number(await canvas.getAttribute('height'));
const circleCenterPoint = [Math.round(width / 2), Math.round(height / 3)];
const circleBorderPoint = [circleCenterPoint[0] + 60, circleCenterPoint[1]];
const circlePoints = [circleCenterPoint, circleBorderPoint];

await simulateDrawPath(page, canvas, circlePoints);

// Restore it to its previous selected value
await toolsDropdown.selectOption(selectedToolOption);
}

async function drawRectangleScissor(page, canvas) {
const toolsDropdown = await page.getByRole('combobox').first();
const selectedToolOption = await toolsDropdown.inputValue();
Expand All @@ -239,8 +391,9 @@ async function drawRectangleScissor(page, canvas) {
2 * Math.round(width / 3),
Math.round(height / 2),
];
const rectPoints = [rectTopLeftPoint, rectBottomRightPoint];

await simulateDrawRect(page, canvas, rectTopLeftPoint, rectBottomRightPoint);
await simulateDrawPath(page, canvas, rectPoints);

// Restore it to its previous selected value
await toolsDropdown.selectOption(selectedToolOption);
Expand Down
12 changes: 5 additions & 7 deletions tests/utils/locatorToPageCoord.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
export default async function locatorToPageCoord(
page,
locator,
point: number[]
) {
const bbox = await locator.boundingBox();
import locatorToPageCoords from './locatorToPageCoords';

return [bbox.x + point[0], bbox.y + point[1]];
export default async function locatorToPageCoord(locator, point: number[]) {
const pagePoints = await locatorToPageCoords(locator, [point]);

return pagePoints[0];
}
17 changes: 17 additions & 0 deletions tests/utils/locatorToPageCoords.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Convert a point from locator space (eg: canvas) to page space.
* These points are used frequently by `page.mouse` methods.
*
* Do not call `locatorToPageCoord` that converts one point at a time when
* converting multiple points because `locator.boundingBox()` add an extra ~10ms
* to each call.
*
* @param locator - An element returned by `page.locator()`
* @param points - Array of poins in locator space to be converted to page space
* @returns Array of points in page space
*/
export default async function locatorToPageCoords(locator, points: number[][]) {
const bbox = await locator.boundingBox();

return points.map((point) => [bbox.x + point[0], bbox.y + point[1]]);
}
8 changes: 8 additions & 0 deletions tests/utils/screenShotPaths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,18 @@ const screenShotPaths = {
stackSegmentation: {
defaultSegmentation: 'defaultSegmentation.png',
circularBrushSegment1: 'circularBrushSegment1.png',
thresholdBrushSegment1: 'thresholdBrushSegment1.png',
dynamicThresholdInitialHighlightedPixels:
'dynamicThresholdInitialHighlightedPixels.png',
dynamicThresholdHighlightedContour:
'dynamicThresholdHighlightedContour.png',
dynamicThresholdConfirmedContour: 'dynamicThresholdConfirmedContour.png',
circularEraserSegmentation1: 'circularEraserSegmentation1.png',
circularEraserSegmentation2: 'circularEraserSegmentation2.png',
rectangleScissorSegmentation1: 'rectangleScissorSegmentation1.png',
rectangleScissorSegmentation2: 'rectangleScissorSegmentation2.png',
circularScissorSegmentation1: 'circularScissorSegmentation1.png',
circularScissorSegmentation2: 'circularScissorSegmentation2.png',
paintFillSeg1OuterCircle: 'paintFillSeg1OuterCircle.png',
paintFillSegmentation2: 'paintFillSegmentation2.png',
},
Expand Down
21 changes: 15 additions & 6 deletions tests/utils/simulateDrawPath.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import locatorToPageCoords from './locatorToPageCoords';

/**
*
* @param page - The page to simulate the drag on
* @param locator - The locator of the element to perform the drag on
* @param locatorPoints - Array with at least two points
* @param options - Options object that may contain one or more of the following options
* - button: button that should be used to trigger mouse down/up events
* - steps: number of steps between each pair of points
* - closePath: it closes the path moving the mouse back to the start point
* when this is set to true
* - interpolateSteps: interpolates the mouse movement by `steps` steps or by
* the distance multiplied by `stepsResolution` steps.
* - stepsResolution: determines how many steps will be used when `interpolateSteps`
* is set to `true` but `steps` is `undefined`. The number of steps will be
* equal to the distance between the two points when `stepsResolution` is set
* to 1 but that may make the test run very slow. Default: 1/4.
*/

async function simulateDrawPath(
page,
locator,
Expand All @@ -20,11 +33,7 @@ async function simulateDrawPath(
throw new Error('locatorPoints must have at least two points');
}

const bbox = await locator.boundingBox();
const pagePoints = locatorPoints.map((point) => [
point[0] + bbox.x,
point[1] + bbox.y,
]);
const pagePoints = await locatorToPageCoords(locator, locatorPoints);

if (options?.closePath) {
pagePoints.push(pagePoints[0]);
Expand Down
23 changes: 0 additions & 23 deletions tests/utils/simulateDrawRect.ts

This file was deleted.

0 comments on commit 1371998

Please sign in to comment.