diff --git a/e2e/examples/image-keypoints.js b/e2e/examples/image-keypoints.js
index b5b813705c..ed7d1cd0b9 100644
--- a/e2e/examples/image-keypoints.js
+++ b/e2e/examples/image-keypoints.js
@@ -9,7 +9,7 @@ const config = `
`;
const data = {
- image: 'https://user.fm/files/v2-901310d5cb3fa90e0616ca10590bacb3/spacexmoon-800x501.jpg',
+ image: 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg',
};
const result = [
@@ -18,14 +18,14 @@ const result = [
from_name: 'tag',
to_name: 'img',
image_rotation: 0,
- original_height: 501,
- original_width: 800,
+ original_height: 576,
+ original_width: 768,
type: 'keypointlabels',
origin: 'manual',
value: {
x: 49.60000000000001,
y: 52.34042553191488,
- width: 0.6471078324314267,
+ width: 0.6120428759942558,
keypointlabels: ['Hello'],
},
},
@@ -34,14 +34,14 @@ const result = [
from_name: 'tag',
to_name: 'img',
image_rotation: 0,
- original_height: 501,
- original_width: 800,
+ original_height: 576,
+ original_width: 768,
type: 'keypointlabels',
origin: 'manual',
value: {
x: 47.73333333333334,
y: 52.765957446808514,
- width: 0.6666666666666666,
+ width: 0.6305418719211823,
keypointlabels: ['World'],
},
},
diff --git a/e2e/fragments/AtImageView.js b/e2e/fragments/AtImageView.js
index ad2ccf152e..ff02528e08 100644
--- a/e2e/fragments/AtImageView.js
+++ b/e2e/fragments/AtImageView.js
@@ -79,6 +79,12 @@ module.exports = {
I.waitForVisible('canvas', 5);
},
+ async getNaturalSize() {
+ const sizes = await I.executeScript(Helpers.getNaturalSize);
+
+ return sizes;
+ },
+
async getCanvasSize() {
const sizes = await I.executeScript(Helpers.getCanvasSize);
diff --git a/e2e/tests/helpers.js b/e2e/tests/helpers.js
index f879027676..7110e91def 100644
--- a/e2e/tests/helpers.js
+++ b/e2e/tests/helpers.js
@@ -234,7 +234,7 @@ const convertToFixed = (data, fractionDigits = 2) => {
if (['string', 'number'].includes(typeof data)) {
const n = Number(data);
- return Number.isNaN(n) ? data : Number.isInteger(n) ? n : +Number(n).toFixed(fractionDigits);
+ return Number.isNaN(n) ? data : Number.isInteger(n) ? n : +n.toFixed(fractionDigits);
}
if (Array.isArray(data)) {
return data.map(n => convertToFixed(n, fractionDigits));
@@ -533,6 +533,14 @@ async function generateImageUrl({ width, height }) {
return canvas.toDataURL();
}
+const getNaturalSize = () => {
+ const imageObject = window.Htx.annotationStore.selected.objects.find(o => o.type === 'image');
+
+ return {
+ width: imageObject.naturalWidth,
+ height: imageObject.naturalHeight,
+ };
+};
const getCanvasSize = () => {
const imageObject = window.Htx.annotationStore.selected.objects.find(o => o.type === 'image');
@@ -859,6 +867,7 @@ module.exports = {
areEqualRGB,
hasKonvaPixelColorAtPoint,
getKonvaPixelColorFromPoint,
+ getNaturalSize,
getCanvasSize,
getImageSize,
getImageFrameSize,
diff --git a/e2e/tests/history.test.js b/e2e/tests/history.test.js
index cc0a5f1996..27a90e0b0d 100644
--- a/e2e/tests/history.test.js
+++ b/e2e/tests/history.test.js
@@ -1,6 +1,6 @@
Feature('Time traveling');
-const IMAGE = 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
Scenario('Travel through history with the selected brush region', async function({ I, LabelStudio, AtImageView, AtSidebar }) {
I.amOnPage('/');
diff --git a/e2e/tests/image-labels.test.js b/e2e/tests/image-labels.test.js
index c1924c8b8b..eac9dd22c7 100644
--- a/e2e/tests/image-labels.test.js
+++ b/e2e/tests/image-labels.test.js
@@ -3,8 +3,7 @@ const { toKebabCase } = require('strman');
Feature('Images\' labels type matching');
-const IMAGE =
- 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
const createConfig = ({ shapes = ['Rectangle'], props } = {}) => {
return `
diff --git a/e2e/tests/image.gestures.test.js b/e2e/tests/image.gestures.test.js
index bea2c3c614..4a723a6796 100644
--- a/e2e/tests/image.gestures.test.js
+++ b/e2e/tests/image.gestures.test.js
@@ -10,8 +10,7 @@ const DEFAULT_DIMENSIONS = {
Feature('Creating regions with gesture');
-const IMAGE =
- 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
const BLUEVIOLET = {
color: '#8A2BE2',
@@ -36,7 +35,7 @@ const createShape = {
byMultipleClicks(x, y, radius, opts = {}) {
const points = [];
- for (let i = 5; i--; ) {
+ for (let i = 5; i--;) {
points.push([x + Math.sin(((2 * Math.PI) / 5) * i) * radius, y - Math.cos(((2 * Math.PI) / 5) * i) * radius]);
points.push([
x + (Math.sin(((2 * Math.PI) / 5) * (i - 0.5)) * radius) / 3,
@@ -209,7 +208,7 @@ Scenario('Creating regions by various gestures', async function({ I, AtImageView
for (const [idx, region] of Object.entries(regions)) {
I.pressKey(region.hotKey);
AtImageView[region.action](...region.params);
- AtSidebar.seeRegions(+idx+1);
+ AtSidebar.seeRegions(+idx + 1);
}
const result = await I.executeScript(serialize);
diff --git a/e2e/tests/image.magic-wand.test.js b/e2e/tests/image.magic-wand.test.js
index a012f383a6..e6afdb7bc5 100644
--- a/e2e/tests/image.magic-wand.test.js
+++ b/e2e/tests/image.magic-wand.test.js
@@ -3,7 +3,6 @@ const {
hasKonvaPixelColorAtPoint,
setKonvaLayersOpacity,
serialize,
- waitForImage,
} = require('./helpers');
const assert = require('assert');
diff --git a/e2e/tests/image.shapes.test.js b/e2e/tests/image.shapes.test.js
index e699108856..ded9414699 100644
--- a/e2e/tests/image.shapes.test.js
+++ b/e2e/tests/image.shapes.test.js
@@ -19,8 +19,7 @@ const getConfigWithShape = (shape, props = '') => `
<${shape} ${props} name="tag" toName="img" />
`;
-const IMAGE =
- 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
// precalculated image size on the screen; may change because of different reasons
const WIDTH = 706;
diff --git a/e2e/tests/image.test.js b/e2e/tests/image.test.js
index 6ece71a08e..e054cc3ba6 100644
--- a/e2e/tests/image.test.js
+++ b/e2e/tests/image.test.js
@@ -62,8 +62,7 @@ const annotationWithPerRegion = {
result: [annotationMoonwalker.result[0], createRegion('answer', 'textarea', { text: ['blah'] })],
};
-const image =
- 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
+const image = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
Scenario('Check Rect region for Image', async function({ I, AtImageView, AtSidebar }) {
const params = {
@@ -148,10 +147,10 @@ Scenario('Image with perRegion tags', async function({ I, AtImageView, AtSidebar
assert.deepStrictEqual(result[0].value.rectanglelabels, ['Moonwalker']);
});
-const outOfBoundsFFs = new DataTable(['FF_DEV_3793'])
+const outOfBoundsFFs = new DataTable(['FF_DEV_3793']);
outOfBoundsFFs.add([true]);
-outOfBoundsFFs.add([false])
+outOfBoundsFFs.add([false]);
Data(outOfBoundsFFs)
.Scenario('Can\'t create rectangles outside of canvas', async ({
diff --git a/e2e/tests/image.transformer.test.js b/e2e/tests/image.transformer.test.js
index 43955d2ffa..b8ee2e1c18 100644
--- a/e2e/tests/image.transformer.test.js
+++ b/e2e/tests/image.transformer.test.js
@@ -1,1270 +1,1269 @@
-const assert = require('assert');
-const Asserts = require('../utils/asserts');
-const Helpers = require('./helpers');
-
-Feature('Image transformer');
-
-const IMAGE =
- 'https://user.fm/files/v2-901310d5cb3fa90e0616ca10590bacb3/spacexmoon-800x501.jpg';
-
-const annotationEmpty = {
- id: '1000',
- result: [],
-};
-
-const getParamsWithShape = (shape, params = '') => ({
- config: `
-
-
- <${shape} ${params} name="tag" toName="img" />
- `,
- data: { image: IMAGE },
- annotations: [annotationEmpty],
-});
-
-const getParamsWithLabels = (shape) => ({
- config: `
-
-
- <${shape}Labels name="tag" toName="img">
-
- ${shape}Labels>
- `,
- data: { image: IMAGE },
- annotations: [annotationEmpty],
-});
-
-const shapes = {
- Rectangle: {
- drawAction: 'drawByDrag',
- hasTransformer: true,
- hasRotator: true,
- hasMoveToolTransformer: true,
- hasMultiSelectionTransformer: true,
- hasMultiSelectionRotator: true,
- hotKey: 'r',
- byBBox(x, y, width, height) {
- return {
- params: [x, y, width, height],
- result: { width, height, rotation: 0, x, y },
- };
- },
- },
- Ellipse: {
- drawAction: 'drawByDrag',
- hasTransformer: true,
- hasRotator: true,
- hasMoveToolTransformer: true,
- hasMultiSelectionTransformer: true,
- hasMultiSelectionRotator: true,
- hotKey: 'o',
- byBBox(x, y, width, height) {
- return {
- params: [x + width / 2, y + height / 2, width / 2, height / 2],
- result: { radiusX: width / 2, radiusY: height / 2, rotation: 0, x: x + width / 2, y: y + height / 2 },
- };
- },
- },
- Polygon: {
- drawAction: 'drawByClickingPoints',
- hasTransformer: false,
- hasRotator: false,
- hasMoveToolTransformer: true,
- hasMultiSelectionTransformer: true,
- hasMultiSelectionRotator: false,
- hotKey: 'p',
- byBBox(x, y, width, height) {
- const points = [];
-
- points.push([x, y]);
- points.push([x + width, y]);
- points.push([x + width / 2, y + height / 2]);
- points.push([x + width, y + height]);
- points.push([x, y + height]);
- return {
- params: [[...points, points[0]]],
- result: {
- points,
- },
- };
- },
- },
- KeyPoint: {
- drawAction: 'clickAt',
- hasTransformer: false,
- hasRotator: false,
- hasMoveToolTransformer: false,
- hasMultiSelectionTransformer: true,
- hasMultiSelectionRotator: false,
- hotKey: 'k',
- params: 'strokeWidth="2"',
- byBBox(x, y, width, height) {
- return {
- params: [x + width / 2, y + height / 2],
- result: {
- x: x + width / 2,
- y: y + height / 2,
- width: 2,
- },
- };
- },
- },
-};
-
-function drawShapeByBbox(Shape, x, y, width, height, where) {
- where[Shape.drawAction](...Shape.byBBox(x, y, width, height).params);
-}
-
-const shapesTable = new DataTable(['shapeName']);
-
-for (const shapeName of Object.keys(shapes)) {
- shapesTable.add([shapeName]);
-}
-/*
-Data(shapesTable).Scenario('Check transformer existing for different shapes, their amount and modes.', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
- const { shapeName } = current;
- const Shape = shapes[shapeName];
-
- I.amOnPage('/');
- const bbox1 = {
- x: 100,
- y: 100,
- width: 200,
- height: 200,
- };
- const bbox2 = {
- x: 400,
- y: 100,
- width: 200,
- height: 200,
- };
- const getCenter = bbox => [bbox.x + bbox.width / 2, bbox.y + bbox.height / 2];
- let isTransformerExist;
-
- LabelStudio.init(getParamsWithLabels(shapeName));
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
-
- // Draw two regions
- I.pressKey('1');
- drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView);
- AtSidebar.seeRegions(1);
- I.pressKey('1');
- drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView);
- AtSidebar.seeRegions(2);
-
- // Check that it wasn't a cause to show a transformer
- isTransformerExist = await AtImageView.isTransformerExist();
- assert.strictEqual(isTransformerExist, false);
-
- // Select the first region
- AtImageView.clickAt(...getCenter(bbox1));
- AtSidebar.seeSelectedRegion();
-
- // Match if transformer exist with expectations in single selected mode
- isTransformerExist = await AtImageView.isTransformerExist();
- assert.strictEqual(isTransformerExist, Shape.hasTransformer);
-
- // Match if rotator at transformer exist with expectations in single selected mode
- isTransformerExist = await AtImageView.isRotaterExist();
- assert.strictEqual(isTransformerExist, Shape.hasRotator);
-
- // Switch to move tool
- I.pressKey('v');
-
- // Match if rotator at transformer exist with expectations in single selected mode with move tool chosen
- isTransformerExist = await AtImageView.isTransformerExist();
- assert.strictEqual(isTransformerExist, Shape.hasMoveToolTransformer);
-
- // Deselect the previous selected region
- I.pressKey(['u']);
-
- // Select 2 regions
- AtImageView.drawThroughPoints([
- [bbox1.x - 5, bbox1.y - 5],
- [bbox2.x + bbox2.width + 5, bbox2.y + bbox2.height + 5],
- ], 'steps', 10);
-
- // Match if transformer exist with expectations in multiple selected mode
- isTransformerExist = await AtImageView.isTransformerExist();
- assert.strictEqual(isTransformerExist, Shape.hasMultiSelectionTransformer);
-
- // Match if rotator exist with expectations in multiple selected mode
- isTransformerExist = await AtImageView.isRotaterExist();
- assert.strictEqual(isTransformerExist, Shape.hasMultiSelectionRotator);
-});
-
-Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMoveToolTransformer))
- .Scenario('Resizing a single region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
- const { shapeName } = current;
- const Shape = shapes[shapeName];
-
- I.amOnPage('/');
- LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
- const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height);
-
- // Draw a region in bbox {x1:50,y1:50,x2:150,y2:150}
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, 50, 50, 100, 100, AtImageView);
- AtSidebar.seeRegions(1);
-
- // Select the shape
- AtImageView.clickAt(100, 100);
- AtSidebar.seeSelectedRegion();
-
- // Switch to move tool to force appearance of transformer
- I.pressKey('v');
- const isTransformerExist = await AtImageView.isTransformerExist();
-
- assert.strictEqual(isTransformerExist, true);
-
-
- // Transform the shape
- // Move the top anchor up for 50px (limited by image border) => {x1:50,y1:0,x2:150,y2:150}
- AtImageView.drawByDrag(100, 50, 0, -100);
- // Move the left anchor left for 50px (limited by image border) => {x1:0,y1:0,x2:150,y2:150}
- AtImageView.drawByDrag(50, 75, -300, -100);
- // Move the right anchor left for 50px => {x1:0,y1:0,x2:100,y2:150}
- AtImageView.drawByDrag(150, 75, -50, 0);
- // Move the bottom anchor down for 100px => {x1:0,y1:0,x2:100,y2:250}
- AtImageView.drawByDrag(50, 150, 10, 100);
- // Move the right-bottom anchor right for 200px and down for 50px => {x1:0,y1:0,x2:300,y2:300}
- AtImageView.drawByDrag(100, 250, 200, 50);
- // Check resulting sizes
- const rectangleResult = await LabelStudio.serialize();
- const exceptedResult = Shape.byBBox(0, 0, 300, 300).result;
-
- Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult));
- });
-
-Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMoveToolTransformer))
- .Scenario('Resizing a single region with zoom', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
- const { shapeName } = current;
- const Shape = shapes[shapeName];
-
- I.amOnPage('/');
-
- LabelStudio.setFeatureFlags({
- 'ff_front_dev_2394_zoomed_transforms_260522_short': true,
- });
-
- LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
- const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height);
-
- // Draw a region in bbox {x1:50,y1:50,x2:150,y2:150}
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, 50, 50, 300, 300, AtImageView);
- AtSidebar.seeRegions(1);
-
- // Select the shape
- AtImageView.clickAt(100, 100);
- AtSidebar.seeSelectedRegion();
-
- // Switch to move tool to force appearance of transformer
- I.pressKey('v');
- const isTransformerExist = await AtImageView.isTransformerExist();
-
- assert.strictEqual(isTransformerExist, true);
-
- AtImageView.setZoom(3, 0, 0);
-
- // Transform the shape
- AtImageView.drawByDrag(150, 150, -150, -150);
- I.wait(1);
-
- AtImageView.drawByDrag(0, 0, -300, -100);
- I.wait(1);
-
- AtImageView.drawByDrag(0, 0, 150, 150);
- I.wait(1);
-
- // Check resulting sizes
- const rectangleResult = await LabelStudio.serialize();
-
- I.wait(10);
-
- const exceptedResult = Shape.byBBox(50, 50, 300, 300).result;
-
- Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult), 0);
- });
-
-Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
- .Scenario('Simple rotating', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
- const { shapeName } = current;
- const Shape = shapes[shapeName];
-
- I.amOnPage('/');
- LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
-
- // Draw a region in bbox {x1:40%,y1:40%,x2:60%,y2:60%}
- const rectangle = {
- x: canvasSize.width * .4,
- y: canvasSize.height * .4,
- width: canvasSize.width * .2,
- height: canvasSize.height * .2,
- };
- const rectangleCenter = {
- x: rectangle.x + rectangle.width / 2,
- y: rectangle.y + rectangle.height / 2,
- };
-
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView);
- AtSidebar.seeRegions(1);
-
-
- // Select the shape and check that transformer appears
- AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y);
- AtSidebar.seeSelectedRegion();
-
- // Switch to move tool to force appearance of transformer
- I.pressKey('v');
- const isTransformerExist = await AtImageView.isTransformerExist();
-
- assert.strictEqual(isTransformerExist, true);
-
- // The rotator anchor must be above top anchor by 50 pixels
- const rotatorPosition = {
- x: rectangleCenter.x,
- y: rectangle.y - 50,
- };
-
- // Rotate for 45 degrees clockwise
- AtImageView.drawThroughPoints(
- [
- [rotatorPosition.x, rotatorPosition.y],
- [rectangleCenter.x + 500, rectangleCenter.y - 500],
- ], 'steps', 5);
-
- // Check resulting rotation
- const rectangleResult = await LabelStudio.serialize();
-
- Asserts.deepEqualWithTolerance(Math.round(rectangleResult[0].value.rotation), 45);
-
- });
-
-Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
- .Scenario('Rotating of unrotatable region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
- const { shapeName } = current;
- const Shape = shapes[shapeName];
-
- I.amOnPage('/');
- LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
-
- // Draw a region which we cannot rotate 'cause of position near the image's border {x1:0,y1:20%,x2:20%,y2:50%}
- const rectangle = {
- x: 0,
- y: canvasSize.height * .2,
- width: canvasSize.width * .2,
- height: canvasSize.height * .3,
- };
- const rectangleCenter = {
- x: rectangle.x + rectangle.width / 2,
- y: rectangle.y + rectangle.height / 2,
- };
-
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView);
- AtSidebar.seeRegions(1);
-
- // Select the shape and check that transformer appears
- AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y);
- AtSidebar.seeSelectedRegion();
-
- // Switch to move tool to force appearance of transformer
- I.pressKey('v');
- const isTransformerExist = await AtImageView.isTransformerExist();
-
- assert.strictEqual(isTransformerExist, true);
-
- // The rotator anchor must be above top anchor by 50 pixels
- const rotatorPosition = {
- x: rectangleCenter.x,
- y: rectangle.y - 50,
- };
-
- // Rotate for 45 degrees clockwise
- AtImageView.drawByDrag(rotatorPosition.x, rotatorPosition.y, rectangleCenter.y - rotatorPosition.y + 100, -100);
-
- // Check the region hasn't been rotated
- const rectangleResult = await LabelStudio.serialize();
-
- Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 0);
- });
-
-Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
- .Scenario('Broke the limits with rotation', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
- const { shapeName } = current;
- const Shape = shapes[shapeName];
-
- I.amOnPage('/');
- LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
-
- {
- // Draw a region which have limitation at rotating by bbox {x1:5,y1:100,x2:305,y2:350}
- const rectangle = {
- x: 5,
- y: 100,
- width: 300,
- height: 300,
- };
- const rectangleCenter = {
- x: rectangle.x + rectangle.width / 2,
- y: rectangle.y + rectangle.height / 2,
- };
-
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView);
- AtSidebar.seeRegions(1);
-
- // Select the shape and check that transformer appears
- AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y);
- AtSidebar.seeSelectedRegion();
-
- // Switch to move tool to force appearance of transformer
- I.pressKey('v');
- const isTransformerExist = await AtImageView.isTransformerExist();
-
- assert.strictEqual(isTransformerExist, true);
-
- // The rotator anchor must be above top anchor by 50 pixels
- const rotatorPosition = {
- x: rectangleCenter.x,
- y: rectangle.y - 50,
- };
-
- // Rotate for 45 degrees clockwise
- AtImageView.drawThroughPoints([
- [rotatorPosition.x, rotatorPosition.y],
- [rectangleCenter.x + 500, rectangleCenter.y - 500],
- ], 'steps', 200);
-
- // Check that we cannot rotate it like this
- let rectangleResult = await LabelStudio.serialize();
-
- assert.notStrictEqual(
- Math.round(rectangleResult[0].value.rotation),
- 0,
- 'Region must be rotated',
- );
- assert.notStrictEqual(
- Math.round(rectangleResult[0].value.rotation),
- 45,
- 'Angle must not be 45 degrees',
- );
-
- // Undo changes
- I.pressKey(['CommandOrControl', 'z']);
-
- // Rotate for 90 degrees clockwise instead
- AtImageView.drawThroughPoints([
- [rotatorPosition.x, rotatorPosition.y],
- [rectangle.x + rectangle.width + 100, rectangleCenter.y],
- [rectangle.x + rectangle.width + 200, rectangleCenter.y],
- ], 'steps', 200);
-
- // Check the resulted rotation
- rectangleResult = await LabelStudio.serialize();
-
- Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 90, 'Angle must be 90 degrees');
- // remove region
- I.pressKey('Backspace');
- }
-
- I.say('Check that it works same way with right border');
-
- {
- // Draw a region which have limitation at rotating by bbox {x1:100% - 305,y1:100,x2:100% - 5,y2:350}
- const rectangle = {
- x: canvasSize.width - 305,
- y: 100,
- width: 300,
- height: 300,
- };
- const rectangleCenter = {
- x: rectangle.x + rectangle.width / 2,
- y: rectangle.y + rectangle.height / 2,
- };
-
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView);
- AtSidebar.seeRegions(1);
-
- // Select the shape and check that transformer appears
- AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y);
- AtSidebar.seeSelectedRegion();
-
- // Switch to move tool to force appearance of transformer
- I.pressKey('v');
- const isTransformerExist = await AtImageView.isTransformerExist();
-
- assert.strictEqual(isTransformerExist, true);
-
- // The rotator anchor must be above top anchor by 50 pixels
- const rotatorPosition = {
- x: rectangleCenter.x,
- y: rectangle.y - 50,
- };
-
- // Rotate for 45 degrees clockwise
- AtImageView.drawThroughPoints([
- [rotatorPosition.x, rotatorPosition.y],
- [rectangleCenter.x + 500, rectangleCenter.y - 500],
- ], 'steps', 200);
-
- // Check the resulted rotation
- let rectangleResult = await LabelStudio.serialize();
-
- assert.notStrictEqual(Math.round(rectangleResult[0].value.rotation), 0);
- assert.notStrictEqual(Math.round(rectangleResult[0].value.rotation), 45);
-
- // Undo changes
- I.pressKey(['CommandOrControl', 'z']);
-
- // Rotate for 90 degrees clockwise instead
- AtImageView.drawThroughPoints([
- [rotatorPosition.x, rotatorPosition.y],
- [rectangle.x + rectangle.width + 100, rectangleCenter.y],
- [rectangle.x + rectangle.width + 200, rectangleCenter.y],
- ], 'steps', 200);
-
- // Check that we cannot rotate it like this
- rectangleResult = await LabelStudio.serialize();
-
- Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 90);
- }
-
- });
-
-Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
- .Scenario('Check the initial rotation of transformer for the single region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
- const { shapeName } = current;
- const Shape = shapes[shapeName];
-
- I.amOnPage('/');
- LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
-
- const bbox = {
- x: 100,
- y: 100,
- width: 100,
- height: 100,
- };
- const bboxCenter = {
- x: bbox.x + bbox.width / 2,
- y: bbox.y + bbox.height / 2,
- };
-
- // Draw a region
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, bbox.x, bbox.y, bbox.width, bbox.height, AtImageView);
- AtSidebar.seeRegions(1);
-
- // Select it
- AtImageView.clickAt(bboxCenter.x, bboxCenter.y);
- AtSidebar.seeSelectedRegion();
-
- // Switch to move tool to force appearance of transformer
- I.pressKey('v');
- const isTransformerExist = await AtImageView.isTransformerExist();
-
- assert.strictEqual(isTransformerExist, true);
-
- // The rotator anchor must be above top anchor by 50 pixels
- let rotatorPosition = {
- x: bboxCenter.x,
- y: bbox.y - 50,
- };
-
- // Rotate for 90 degrees clockwise
- AtImageView.drawThroughPoints([
- [rotatorPosition.x, rotatorPosition.y],
- [bbox.x + bbox.width + 100, bboxCenter.y],
- [bbox.x + bbox.width + 200, bboxCenter.y],
- ], 'steps', 10);
-
- // Unselect current region
- I.pressKey('u');
- AtSidebar.dontSeeSelectedRegion();
-
- // Select it again
- AtImageView.clickAt(bboxCenter.x, bboxCenter.y);
- AtSidebar.seeSelectedRegion();
-
- // The trick is that we turn it further, based on the assumption that transformer appears in rotated state on region selection
- // So let's try to rotate it
- // The rotator anchor must be to the right of the right anchor by 50 pixels
-
- rotatorPosition = {
- x: bbox.x + bbox.width + 50,
- y: bboxCenter.y,
- };
-
- // Rotate for 90 degrees clockwise once again
- AtImageView.drawThroughPoints([
- [rotatorPosition.x, rotatorPosition.y],
- [bboxCenter.x, bbox.y + bbox.height + 100],
- [bboxCenter.x, bbox.y + bbox.height + 200],
- ], 'steps', 10);
-
- // Check that region has been rotated for 180 degrees
- const rectangleResult = await LabelStudio.serialize();
-
- Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 180);
- });
-
-Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
- .Scenario('Check the initial rotation of transformer for the couple of regions', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
- const { shapeName } = current;
- const Shape = shapes[shapeName];
-
- I.amOnPage('/');
- LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
-
- const bbox1 = {
- x: 100,
- y: 100,
- width: 40,
- height: 40,
- };
-
- const bbox2 = {
- x: 160,
- y: 160,
- width: 40,
- height: 40,
- };
-
- const transformerBbox = {
- x: bbox1.x,
- y: bbox1.y,
- width: bbox2.x + bbox2.width - bbox1.x,
- height: bbox2.y + bbox2.height - bbox1.y,
- };
- const transformerBboxCenter = {
- x: transformerBbox.x + transformerBbox.width / 2,
- y: transformerBbox.y + transformerBbox.height / 2,
- };
-
- // Draw the first region
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView);
- AtSidebar.seeRegions(1);
-
- // Draw the second region
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView);
- AtSidebar.seeRegions(2);
-
- // Switch to move tool and select them
- I.pressKey('v');
- AtImageView.drawThroughPoints([
- [transformerBbox.x - 20, transformerBbox.y - 20],
- [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20],
- ]);
- AtSidebar.seeSelectedRegion();
-
- // The rotator anchor must be above top anchor by 50 pixels
- const rotatorPosition = {
- x: transformerBboxCenter.x,
- y: transformerBbox.y - 50,
- };
-
- // Rotate for 180 degrees clockwise
- AtImageView.drawThroughPoints([
- [rotatorPosition.x, rotatorPosition.y],
- [transformerBboxCenter.x + 100, transformerBboxCenter.y + 100],
- [transformerBboxCenter.x, transformerBboxCenter.y + 100],
- [transformerBboxCenter.x, transformerBboxCenter.y + 200],
- ], 'steps', 10);
-
- // Unselect current regions
- I.pressKey('u');
- AtSidebar.dontSeeSelectedRegion();
-
- // Select them again
- AtImageView.drawThroughPoints([
- [transformerBbox.x - 20, transformerBbox.y - 20],
- [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20],
- ]);
- AtSidebar.seeSelectedRegion();
-
- // So we have couple of rotated regions, let's check if rotates still appears above the top anchor
-
- // Rotate for 90 degrees clockwise
- AtImageView.drawThroughPoints([
- [rotatorPosition.x, rotatorPosition.y],
- [transformerBboxCenter.x + 100, transformerBboxCenter.y],
- [transformerBboxCenter.x + 200, transformerBboxCenter.y],
- ], 'steps', 10);
-
- // Check that region has been rotated for (180 + 90) degrees
- const rectangleResult = await LabelStudio.serialize();
-
- Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 180 + 90);
- });
-
-// KeyPoints are transformed unpredictable so for now just skip them
-Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionTransformer && shapeName !== 'KeyPoint'))
- .Scenario('Transforming of multiple regions', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
- const { shapeName } = current;
- const Shape = shapes[shapeName];
-
- I.amOnPage('/');
- LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
- const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height);
-
- const bbox1 = {
- x: 100,
- y: 100,
- width: 50,
- height: 50,
- };
-
- const bbox2 = {
- x: 150,
- y: 150,
- width: 50,
- height: 50,
- };
-
- const transformerBbox = {
- x: bbox1.x,
- y: bbox1.y,
- width: bbox2.x + bbox2.width - bbox1.x,
- height: bbox2.y + bbox2.height - bbox1.y,
- };
- const transformerBboxCenter = {
- get x() {
- return transformerBbox.x + transformerBbox.width / 2;
- },
- get y() {
- return transformerBbox.y + transformerBbox.height / 2;
- },
- };
-
- // Draw the first region
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView);
- AtSidebar.seeRegions(1);
-
- // Draw the second region
- I.pressKey(Shape.hotKey);
- I.pressKeyDown('Control');
- drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView);
- I.pressKeyUp('Control');
- AtSidebar.seeRegions(2);
-
- // Switch to move tool and select them
- I.pressKey('v');
- AtImageView.drawThroughPoints([
- [transformerBbox.x - 20, transformerBbox.y - 20],
- [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20],
- ]);
- AtSidebar.seeSelectedRegion();
- // Scale the shapes vertically
- AtImageView.drawByDrag(transformerBboxCenter.x, transformerBbox.y + transformerBbox.height, 0, 50);
- transformerBbox.height += 50;
- AtSidebar.seeSelectedRegion();
- // Scale the shapes horizontally
- AtImageView.drawByDrag(transformerBbox.x + transformerBbox.width, transformerBboxCenter.y, 50, 0);
- transformerBbox.width += 50;
- AtSidebar.seeSelectedRegion();
- // Scale the shapes in both directions
- AtImageView.drawByDrag(transformerBbox.x + transformerBbox.width, transformerBbox.y + transformerBbox.height, 50, 50);
- transformerBbox.height += 50;
- transformerBbox.width += 50;
- AtSidebar.seeSelectedRegion();
-
- // Check resulting sizes
- const rectangleResult = await LabelStudio.serialize();
- const exceptedResult1 = Shape.byBBox(bbox1.x, bbox1.y, bbox1.width + 50, bbox1.height + 50).result;
- const exceptedResult2 = Shape.byBBox(bbox2.x + 50, bbox2.y + 50, bbox2.width + 50, bbox2.height + 50).result;
-
- Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult1));
- Asserts.deepEqualWithTolerance(rectangleResult[1].value, convertToImageSize(exceptedResult2));
- });
-
-Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionTransformer))
- .Scenario('Move regions by drag', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
- const { shapeName } = current;
- const Shape = shapes[shapeName];
-
- I.amOnPage('/');
- LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
- const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height);
-
- const bbox1 = {
- x: 100,
- y: 100,
- width: 20,
- height: 20,
- };
- const bbox1Center = {
- x: bbox1.x + bbox1.width / 2,
- y: bbox1.y + bbox1.height / 2,
- };
-
- const bbox2 = {
- x: 140,
- y: 140,
- width: 20,
- height: 20,
- };
- const bbox2Center = {
- x: bbox2.x + bbox2.width / 2,
- y: bbox2.y + bbox2.height / 2,
- };
-
- const transformerBbox = {
- x: bbox1.x,
- y: bbox1.y,
- width: bbox2.x + bbox2.width - bbox1.x,
- height: bbox2.y + bbox2.height - bbox1.y,
- };
- const transformerBboxCenter = {
- x: transformerBbox.x + transformerBbox.width / 2,
- y: transformerBbox.y + transformerBbox.height / 2,
- };
-
- // Draw the first region
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView);
- AtSidebar.seeRegions(1);
-
- // Draw the second region
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView);
- AtSidebar.seeRegions(2);
-
- if (shapeName === 'KeyPoint') {
- // Draw more points to get more space in transformer
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, bbox1.x, bbox1.y, 0, 0, AtImageView);
- AtSidebar.seeRegions(3);
-
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, bbox2.x + bbox2.width, bbox2.y + bbox2.height, 0, 0, AtImageView);
- AtSidebar.seeRegions(4);
- }
-
- // Switch to move tool and select them
- I.pressKey('v');
- AtImageView.drawThroughPoints([
- [transformerBbox.x - 20, transformerBbox.y - 20],
- [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20],
- ]);
- AtSidebar.seeSelectedRegion();
-
- const dragShapes = (startPoint, shift, rememberShift = true) => {
- AtImageView.drawThroughPoints([
- [startPoint.x, startPoint.y],
- [startPoint.x + shift.x, startPoint.y + shift.y],
- [startPoint.x + shift.x, startPoint.y + shift.y],
- ], 'steps', 10);
- AtSidebar.seeSelectedRegion();
-
- if (rememberShift) {
- bbox1Center.x += shift.x;
- bbox1Center.y += shift.y;
- bbox2Center.x += shift.x;
- bbox2Center.y += shift.y;
- transformerBboxCenter.x += shift.x;
- transformerBboxCenter.y += shift.y;
- }
- };
-
- // Drag shapes by holding onto the first region
- dragShapes(bbox1Center, { x: 100, y: 0 });
- // Drag shapes by holding onto the second region
- dragShapes(bbox2Center, { x: 0, y: 100 });
- // Drag shapes by holding onto the transformer background
- dragShapes(transformerBboxCenter, { x: 150, y: 150 }, false);
- // Move back throught history to check that transformer's background moving with it
- I.pressKey(['Control', 'z']);
- // Drag shapes by holding onto the transformer background again
- dragShapes(transformerBboxCenter, { x: 100, y: 100 }, false);
-
-
- // Check that dragging was successful
- const rectangleResult = await LabelStudio.serialize();
- const exceptedResult1 = Shape.byBBox(bbox1.x + 200, bbox1.y + 200, bbox1.width, bbox1.height).result;
- const exceptedResult2 = Shape.byBBox(bbox2.x + 200, bbox2.y + 200, bbox2.width, bbox2.height).result;
-
- Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult1));
- Asserts.deepEqualWithTolerance(rectangleResult[1].value, convertToImageSize(exceptedResult2));
- });
-
-Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
- .Scenario('Limitation of dragging a single rotated region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
- const { shapeName } = current;
- const Shape = shapes[shapeName];
-
- I.amOnPage('/');
- LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
-
- const bbox = {
- x: canvasSize.width / 2 - 50,
- y: canvasSize.height / 2 - 50,
- width: 100,
- height: 100,
- };
- const bboxCenter = {
- x: bbox.x + bbox.width / 2,
- y: bbox.y + bbox.height / 2,
- };
-
- // Draw a region
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, bbox.x, bbox.y, bbox.width, bbox.height, AtImageView);
- AtSidebar.seeRegions(1);
-
- // Select it
- AtImageView.clickAt(bboxCenter.x, bboxCenter.y);
- AtSidebar.seeSelectedRegion();
-
- // Switch to move tool to force appearance of transformer
- I.pressKey('v');
- const isTransformerExist = await AtImageView.isTransformerExist();
-
- assert.strictEqual(isTransformerExist, true);
-
- // The rotator anchor must be above top anchor by 50 pixels
- const rotatorPosition = {
- x: bboxCenter.x,
- y: bbox.y - 50,
- };
-
- // Rotate for 180 degrees clockwise
- AtImageView.drawThroughPoints([
- [rotatorPosition.x, rotatorPosition.y],
- [bboxCenter.x + 100, bboxCenter.y],
- [bboxCenter.x, bboxCenter.y + 100],
- [bboxCenter.x, bboxCenter.y + 200],
- ], 'steps', 10);
-
- // When we have the rotated region, we need to check its behavior when we drag it across the borders of the image
- let rectangleResult;
-
- I.say('Drag the region over the left border');
- AtImageView.drawThroughPoints([
- [bboxCenter.x, bboxCenter.y],
- [-500, bboxCenter.y],
- ], 'steps', 20);
- AtSidebar.seeSelectedRegion();
- // moving of the region should be constrained by borders
- rectangleResult = await LabelStudio.serialize();
- Asserts.deepEqualWithTolerance(
- rectangleResult[0].value.x * canvasSize.width / 100,
- Shape.byBBox(bbox.width, bbox.y + bbox.height, -bbox.width, -bbox.height).result.x,
- );
- // reset position by undo
- I.pressKey(['Control', 'z']);
-
- I.say('Drag the region over the top border');
- AtImageView.drawThroughPoints([
- [bboxCenter.x, bboxCenter.y],
- [bboxCenter.x, -500],
- ], 'steps', 20);
- AtSidebar.seeSelectedRegion();
- // moving of the region should be constrained by borders
- rectangleResult = await LabelStudio.serialize();
- Asserts.deepEqualWithTolerance(
- rectangleResult[0].value.y * canvasSize.height / 100,
- Shape.byBBox(bbox.x + bbox.width, bbox.height, -bbox.width, -bbox.height).result.y,
- );
- // reset position by undo
- I.pressKey(['Control', 'z']);
-
- I.say('Drag the region over the right border');
- AtImageView.drawThroughPoints([
- [bboxCenter.x, bboxCenter.y],
- [canvasSize.width + 500, bboxCenter.y],
- ], 'steps', 20);
- AtSidebar.seeSelectedRegion();
- // moving of the region should be constrained by borders
- rectangleResult = await LabelStudio.serialize();
-
- Asserts.deepEqualWithTolerance(
- rectangleResult[0].value.x * canvasSize.width / 100,
- Shape.byBBox(canvasSize.width, bbox.y + bbox.height, -bbox.width, -bbox.height).result.x,
- );
- // reset position by undo
- I.pressKey(['Control', 'z']);
-
- I.say('Drag the region over the bottom border');
- AtImageView.drawThroughPoints([
- [bboxCenter.x, bboxCenter.y],
- [bboxCenter.x, canvasSize.height + 500],
- ], 'steps', 20);
- AtSidebar.seeSelectedRegion();
- // moving of the region should be constrained by borders
- rectangleResult = await LabelStudio.serialize();
- Asserts.deepEqualWithTolerance(
- rectangleResult[0].value.y * canvasSize.height / 100,
- Shape.byBBox(bbox.x + bbox.width, canvasSize.height, -bbox.width, -bbox.height).result.y,
- );
- });
-
-Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
- .Scenario('Limitation of dragging a couple of rotated regions', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
- const { shapeName } = current;
- const Shape = shapes[shapeName];
-
- I.amOnPage('/');
- LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
-
- const bbox1 = {
- x: canvasSize.width / 2 - 50,
- y: canvasSize.height / 2 - 50,
- width: 50,
- height: 50,
- };
-
- const bbox2 = {
- x: canvasSize.width / 2,
- y: canvasSize.height / 2,
- width: 50,
- height: 50,
- };
-
- const transformerBbox = {
- x: bbox1.x,
- y: bbox1.y,
- width: bbox2.x + bbox2.width - bbox1.x,
- height: bbox2.y + bbox2.height - bbox1.y,
- };
- const transformerBboxCenter = {
- get x() {
- return transformerBbox.x + transformerBbox.width / 2;
- },
- get y() {
- return transformerBbox.y + transformerBbox.height / 2;
- },
- };
-
- // Draw the first region
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView);
- AtSidebar.seeRegions(1);
-
- // Draw the second region
- I.pressKey(Shape.hotKey);
- I.pressKeyDown('Control');
- drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView);
- I.pressKeyUp('Control');
- AtSidebar.seeRegions(2);
-
- // Select them by move tool
- I.pressKey('v');
- AtImageView.drawThroughPoints(
- [
- [transformerBbox.x - 50, transformerBbox.y - 50],
- [transformerBbox.x + transformerBbox.width + 50, transformerBbox.y + transformerBbox.height + 50],
- ], 'steps', 10,
- );
- AtSidebar.seeSelectedRegion();
-
- // The rotator anchor must be above top anchor by 50 pixels
- const rotatorPosition = {
- x: transformerBboxCenter.x,
- y: transformerBbox.y - 50,
- };
-
- // Rotate for 180 degrees clockwise
- AtImageView.drawThroughPoints([
- [rotatorPosition.x, rotatorPosition.y],
- [transformerBboxCenter.x + 100, transformerBboxCenter.y],
- [transformerBboxCenter.x, transformerBboxCenter.y + 100],
- [transformerBboxCenter.x, transformerBboxCenter.y + 200],
- ], 'steps', 10);
-
- // When we have the rotated region, we need to check its behavior when we drag it across the borders of the image
- let rectangleResult;
-
- I.say('Drag the region over the left border');
- AtImageView.drawThroughPoints([
- [transformerBboxCenter.x, transformerBboxCenter.y],
- [-500, transformerBboxCenter.y],
- ], 'steps', 20);
- AtSidebar.seeSelectedRegion();
- // moving of the region should be constrained by borders
- rectangleResult = await LabelStudio.serialize();
- Asserts.deepEqualWithTolerance(
- rectangleResult[0].value.x * canvasSize.width / 100,
- Shape.byBBox(transformerBbox.width, transformerBbox.y + transformerBbox.height, -bbox1.width, -bbox1.height).result.x,
- );
- Asserts.deepEqualWithTolerance(
- rectangleResult[1].value.x * canvasSize.width / 100,
- Shape.byBBox(bbox2.width, transformerBbox.y + bbox2.height, -bbox2.width, -bbox2.height).result.x,
- );
- // reset position by undo
- I.pressKey(['Control', 'z']);
-
- I.say('Drag the region over the top border');
- AtImageView.drawThroughPoints([
- [transformerBboxCenter.x, transformerBboxCenter.y],
- [transformerBboxCenter.x, -500],
- ], 'steps', 20);
- AtSidebar.seeSelectedRegion();
- // moving of the region should be constrained by borders
- rectangleResult = await LabelStudio.serialize();
- Asserts.deepEqualWithTolerance(
- rectangleResult[0].value.y * canvasSize.height / 100,
- Shape.byBBox(transformerBbox.x + transformerBbox.width, transformerBbox.height, -bbox1.width, -bbox1.height).result.y,
- );
- Asserts.deepEqualWithTolerance(
- rectangleResult[1].value.y * canvasSize.height / 100,
- Shape.byBBox(transformerBbox.x + bbox2.width, bbox2.height, -bbox2.width, -bbox2.height).result.y,
- );
- // reset position by undo
- I.pressKey(['Control', 'z']);
-
- I.say('Drag the region over the right border');
- AtImageView.drawThroughPoints([
- [transformerBboxCenter.x, transformerBboxCenter.y],
- [canvasSize.width + 500, transformerBboxCenter.y],
- ], 'steps', 20);
- AtSidebar.seeSelectedRegion();
- // moving of the region should be constrained by borders
- rectangleResult = await LabelStudio.serialize();
-
- Asserts.deepEqualWithTolerance(
- rectangleResult[0].value.x * canvasSize.width / 100,
- Shape.byBBox(canvasSize.width, transformerBbox.y + transformerBbox.height, -bbox1.width, -bbox1.height).result.x,
- );
- Asserts.deepEqualWithTolerance(
- rectangleResult[1].value.x * canvasSize.width / 100,
- Shape.byBBox(canvasSize.width - transformerBbox.width + bbox2.width, transformerBbox.y + bbox2.height, -bbox2.width, -bbox2.height).result.x,
- );
- // reset position by undo
- I.pressKey(['Control', 'z']);
-
- I.say('Drag the region over the bottom border');
- AtImageView.drawThroughPoints([
- [transformerBboxCenter.x, transformerBboxCenter.y],
- [transformerBboxCenter.x, canvasSize.height + 500],
- ], 'steps', 20);
- AtSidebar.seeSelectedRegion();
- // moving of the region should be constrained by borders
- rectangleResult = await LabelStudio.serialize();
- Asserts.deepEqualWithTolerance(
- rectangleResult[0].value.y * canvasSize.height / 100,
- Shape.byBBox(transformerBbox.x + transformerBbox.width, canvasSize.height, -bbox1.width, -bbox1.height).result.y,
- );
- Asserts.deepEqualWithTolerance(
- rectangleResult[1].value.y * canvasSize.height / 100,
- Shape.byBBox(transformerBbox.x + bbox2.width, canvasSize.height - transformerBbox.height + bbox2.height, -bbox2.width, -bbox2.height).result.y,
- );
- });
-
-Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasRotator))
- .Scenario('Rotating the region near the border', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
- const { shapeName } = current;
- const Shape = shapes[shapeName];
-
- I.amOnPage('/');
- LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
-
- const bbox = {
- x: canvasSize.width - Math.ceil(Math.sqrt(100 ** 2 + 100 ** 2)) / 2 - 50,
- y: canvasSize.height - Math.ceil(Math.sqrt(100 ** 2 + 100 ** 2)) / 2 - 50,
- width: 100,
- height: 100,
- };
-
- const bboxCenter = {
- x: bbox.x + bbox.width / 2,
- y: bbox.y + bbox.height / 2,
- };
-
- // Draw the region
- I.pressKey(Shape.hotKey);
- drawShapeByBbox(Shape, bbox.x, bbox.y, bbox.width, bbox.height, AtImageView);
- AtSidebar.seeRegions(1);
-
- // Select it
- AtImageView.clickAt(bboxCenter.x, bboxCenter.y);
- AtSidebar.seeSelectedRegion();
-
- // The rotator anchor must be above top anchor by 50 pixels
- const rotatorPosition = {
- x: bboxCenter.x,
- y: bbox.y - 50,
- };
-
- // Check 7 different rotations
- const rotatorWayPoints = [[rotatorPosition.x, rotatorPosition.y]];
- const angle45 = Math.PI / 4;
-
- for (let i = 0; i < 8; i++) {
- const angle = angle45 * i;
-
- rotatorWayPoints.push([bboxCenter.x + Math.sin(angle) * 100, bboxCenter.y - Math.cos(angle) * 100]);
- rotatorWayPoints.push([bboxCenter.x + Math.sin(angle) * 1000, bboxCenter.y - Math.cos(angle) * 1000]);
-
- // Rotate clockwise by 45 * i degrees
- AtImageView.drawThroughPoints(rotatorWayPoints, 'steps', 10);
- AtSidebar.seeSelectedRegion();
- // Check that rotating was successful
- const rectangleResult = await LabelStudio.serialize();
-
- Asserts.deepEqualWithTolerance(
- Math.round(rectangleResult[0].value.rotation),
- 45 * i,
- );
-
- // undo rotation
- I.pressKey(['Control', 'z']);
- // clear unnecessary waypoints
- rotatorWayPoints.pop();
- }
- });
-*/
\ No newline at end of file
+const assert = require('assert');
+const Asserts = require('../utils/asserts');
+const Helpers = require('./helpers');
+
+Feature('Image transformer');
+
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
+
+const annotationEmpty = {
+ id: '1000',
+ result: [],
+};
+
+const getParamsWithShape = (shape, params = '') => ({
+ config: `
+
+
+ <${shape} ${params} name="tag" toName="img" />
+ `,
+ data: { image: IMAGE },
+ annotations: [annotationEmpty],
+});
+
+const getParamsWithLabels = (shape) => ({
+ config: `
+
+
+ <${shape}Labels name="tag" toName="img">
+
+ ${shape}Labels>
+ `,
+ data: { image: IMAGE },
+ annotations: [annotationEmpty],
+});
+
+const shapes = {
+ Rectangle: {
+ drawAction: 'drawByDrag',
+ hasTransformer: true,
+ hasRotator: true,
+ hasMoveToolTransformer: true,
+ hasMultiSelectionTransformer: true,
+ hasMultiSelectionRotator: true,
+ hotKey: 'r',
+ byBBox(x, y, width, height) {
+ return {
+ params: [x, y, width, height],
+ result: { width, height, rotation: 0, x, y },
+ };
+ },
+ },
+ Ellipse: {
+ drawAction: 'drawByDrag',
+ hasTransformer: true,
+ hasRotator: true,
+ hasMoveToolTransformer: true,
+ hasMultiSelectionTransformer: true,
+ hasMultiSelectionRotator: true,
+ hotKey: 'o',
+ byBBox(x, y, width, height) {
+ return {
+ params: [x + width / 2, y + height / 2, width / 2, height / 2],
+ result: { radiusX: width / 2, radiusY: height / 2, rotation: 0, x: x + width / 2, y: y + height / 2 },
+ };
+ },
+ },
+ Polygon: {
+ drawAction: 'drawByClickingPoints',
+ hasTransformer: false,
+ hasRotator: false,
+ hasMoveToolTransformer: true,
+ hasMultiSelectionTransformer: true,
+ hasMultiSelectionRotator: false,
+ hotKey: 'p',
+ byBBox(x, y, width, height) {
+ const points = [];
+
+ points.push([x, y]);
+ points.push([x + width, y]);
+ points.push([x + width / 2, y + height / 2]);
+ points.push([x + width, y + height]);
+ points.push([x, y + height]);
+ return {
+ params: [[...points, points[0]]],
+ result: {
+ points,
+ },
+ };
+ },
+ },
+ KeyPoint: {
+ drawAction: 'clickAt',
+ hasTransformer: false,
+ hasRotator: false,
+ hasMoveToolTransformer: false,
+ hasMultiSelectionTransformer: true,
+ hasMultiSelectionRotator: false,
+ hotKey: 'k',
+ params: 'strokeWidth="2"',
+ byBBox(x, y, width, height) {
+ return {
+ params: [x + width / 2, y + height / 2],
+ result: {
+ x: x + width / 2,
+ y: y + height / 2,
+ width: 2,
+ },
+ };
+ },
+ },
+};
+
+function drawShapeByBbox(Shape, x, y, width, height, where) {
+ where[Shape.drawAction](...Shape.byBBox(x, y, width, height).params);
+}
+
+const shapesTable = new DataTable(['shapeName']);
+
+for (const shapeName of Object.keys(shapes)) {
+ shapesTable.add([shapeName]);
+}
+
+Data(shapesTable).Scenario('Check transformer existing for different shapes, their amount and modes.', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
+ const { shapeName } = current;
+ const Shape = shapes[shapeName];
+
+ I.amOnPage('/');
+ const bbox1 = {
+ x: 100,
+ y: 100,
+ width: 200,
+ height: 200,
+ };
+ const bbox2 = {
+ x: 400,
+ y: 100,
+ width: 200,
+ height: 200,
+ };
+ const getCenter = bbox => [bbox.x + bbox.width / 2, bbox.y + bbox.height / 2];
+ let isTransformerExist;
+
+ LabelStudio.init(getParamsWithLabels(shapeName));
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+
+ // Draw two regions
+ I.pressKey('1');
+ drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView);
+ AtSidebar.seeRegions(1);
+ I.pressKey('1');
+ drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView);
+ AtSidebar.seeRegions(2);
+
+ // Check that it wasn't a cause to show a transformer
+ isTransformerExist = await AtImageView.isTransformerExist();
+ assert.strictEqual(isTransformerExist, false);
+
+ // Select the first region
+ AtImageView.clickAt(...getCenter(bbox1));
+ AtSidebar.seeSelectedRegion();
+
+ // Match if transformer exist with expectations in single selected mode
+ isTransformerExist = await AtImageView.isTransformerExist();
+ assert.strictEqual(isTransformerExist, Shape.hasTransformer);
+
+ // Match if rotator at transformer exist with expectations in single selected mode
+ isTransformerExist = await AtImageView.isRotaterExist();
+ assert.strictEqual(isTransformerExist, Shape.hasRotator);
+
+ // Switch to move tool
+ I.pressKey('v');
+
+ // Match if rotator at transformer exist with expectations in single selected mode with move tool chosen
+ isTransformerExist = await AtImageView.isTransformerExist();
+ assert.strictEqual(isTransformerExist, Shape.hasMoveToolTransformer);
+
+ // Deselect the previous selected region
+ I.pressKey(['u']);
+
+ // Select 2 regions
+ AtImageView.drawThroughPoints([
+ [bbox1.x - 5, bbox1.y - 5],
+ [bbox2.x + bbox2.width + 5, bbox2.y + bbox2.height + 5],
+ ], 'steps', 10);
+
+ // Match if transformer exist with expectations in multiple selected mode
+ isTransformerExist = await AtImageView.isTransformerExist();
+ assert.strictEqual(isTransformerExist, Shape.hasMultiSelectionTransformer);
+
+ // Match if rotator exist with expectations in multiple selected mode
+ isTransformerExist = await AtImageView.isRotaterExist();
+ assert.strictEqual(isTransformerExist, Shape.hasMultiSelectionRotator);
+});
+
+Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMoveToolTransformer))
+ .Scenario('Resizing a single region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
+ const { shapeName } = current;
+ const Shape = shapes[shapeName];
+
+ I.amOnPage('/');
+ LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+ const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height);
+
+ // Draw a region in bbox {x1:50,y1:50,x2:150,y2:150}
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, 50, 50, 100, 100, AtImageView);
+ AtSidebar.seeRegions(1);
+
+ // Select the shape
+ AtImageView.clickAt(100, 100);
+ AtSidebar.seeSelectedRegion();
+
+ // Switch to move tool to force appearance of transformer
+ I.pressKey('v');
+ const isTransformerExist = await AtImageView.isTransformerExist();
+
+ assert.strictEqual(isTransformerExist, true);
+
+
+ // Transform the shape
+ // Move the top anchor up for 50px (limited by image border) => {x1:50,y1:0,x2:150,y2:150}
+ AtImageView.drawByDrag(100, 50, 0, -100);
+ // Move the left anchor left for 50px (limited by image border) => {x1:0,y1:0,x2:150,y2:150}
+ AtImageView.drawByDrag(50, 75, -300, -100);
+ // Move the right anchor left for 50px => {x1:0,y1:0,x2:100,y2:150}
+ AtImageView.drawByDrag(150, 75, -50, 0);
+ // Move the bottom anchor down for 100px => {x1:0,y1:0,x2:100,y2:250}
+ AtImageView.drawByDrag(50, 150, 10, 100);
+ // Move the right-bottom anchor right for 200px and down for 50px => {x1:0,y1:0,x2:300,y2:300}
+ AtImageView.drawByDrag(100, 250, 200, 50);
+ // Check resulting sizes
+ const rectangleResult = await LabelStudio.serialize();
+ const exceptedResult = Shape.byBBox(0, 0, 300, 300).result;
+
+ Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult));
+ });
+
+Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMoveToolTransformer))
+ .Scenario('Resizing a single region with zoom', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
+ const { shapeName } = current;
+ const Shape = shapes[shapeName];
+
+ LabelStudio.setFeatureFlags({
+ 'ff_front_dev_2394_zoomed_transforms_260522_short': true,
+ 'fflag_fix_front_dev_3377_image_regions_shift_on_resize_280922_short': true,
+ 'fflag_fix_front_dev_3793_relative_coords_short': true,
+ });
+
+ I.amOnPage('/');
+
+ LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+ const naturalSize = await AtImageView.getNaturalSize();
+ const canvasSize = await AtImageView.getCanvasSize();
+ // region sizes are relative (0 to 100) so we have to convert sizes we use for them...
+ // ...relatively to displayed image size, which is canvas size when we open the page
+ const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height);
+
+ // Draw a region in bbox {x1:50,y1:50,x2:150,y2:150}
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, 50, 50, 300, 300, AtImageView);
+ AtSidebar.seeRegions(1);
+
+ // Select the shape
+ AtImageView.clickAt(100, 100);
+ AtSidebar.seeSelectedRegion();
+
+ // Switch to move tool to force appearance of transformer
+ I.pressKey('v');
+ const isTransformerExist = await AtImageView.isTransformerExist();
+
+ assert.strictEqual(isTransformerExist, true);
+
+ // we display an image to fit to canvas size on page load, so initial zoom is not 1;
+ // to do an x3 zoom we have to calculate current zoom and multiply it by 3
+ AtImageView.setZoom(3 * canvasSize.width / naturalSize.width, 0, 0);
+
+ // Transform the shape
+ AtImageView.drawByDrag(150, 150, -150, -150);
+
+ AtImageView.drawByDrag(0, 0, -300, -100);
+
+ AtImageView.drawByDrag(0, 0, 150, 150);
+
+ // Check resulting sizes
+ const rectangleResult = await LabelStudio.serialize();
+ const exceptedResult = Shape.byBBox(50, 50, 300, 300).result;
+
+ Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult), 2);
+ });
+
+Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
+ .Scenario('Simple rotating', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
+ const { shapeName } = current;
+ const Shape = shapes[shapeName];
+
+ I.amOnPage('/');
+ LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+
+ // Draw a region in bbox {x1:40%,y1:40%,x2:60%,y2:60%}
+ const rectangle = {
+ x: canvasSize.width * .4,
+ y: canvasSize.height * .4,
+ width: canvasSize.width * .2,
+ height: canvasSize.height * .2,
+ };
+ const rectangleCenter = {
+ x: rectangle.x + rectangle.width / 2,
+ y: rectangle.y + rectangle.height / 2,
+ };
+
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView);
+ AtSidebar.seeRegions(1);
+
+
+ // Select the shape and check that transformer appears
+ AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y);
+ AtSidebar.seeSelectedRegion();
+
+ // Switch to move tool to force appearance of transformer
+ I.pressKey('v');
+ const isTransformerExist = await AtImageView.isTransformerExist();
+
+ assert.strictEqual(isTransformerExist, true);
+
+ // The rotator anchor must be above top anchor by 50 pixels
+ const rotatorPosition = {
+ x: rectangleCenter.x,
+ y: rectangle.y - 50,
+ };
+
+ // Rotate for 45 degrees clockwise
+ AtImageView.drawThroughPoints(
+ [
+ [rotatorPosition.x, rotatorPosition.y],
+ [rectangleCenter.x + 500, rectangleCenter.y - 500],
+ ], 'steps', 5);
+
+ // Check resulting rotation
+ const rectangleResult = await LabelStudio.serialize();
+
+ Asserts.deepEqualWithTolerance(Math.round(rectangleResult[0].value.rotation), 45);
+
+ });
+
+Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
+ .Scenario('Rotating of unrotatable region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
+ const { shapeName } = current;
+ const Shape = shapes[shapeName];
+
+ I.amOnPage('/');
+ LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+
+ // Draw a region which we cannot rotate 'cause of position near the image's border {x1:0,y1:20%,x2:20%,y2:50%}
+ const rectangle = {
+ x: 0,
+ y: canvasSize.height * .2,
+ width: canvasSize.width * .2,
+ height: canvasSize.height * .3,
+ };
+ const rectangleCenter = {
+ x: rectangle.x + rectangle.width / 2,
+ y: rectangle.y + rectangle.height / 2,
+ };
+
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView);
+ AtSidebar.seeRegions(1);
+
+ // Select the shape and check that transformer appears
+ AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y);
+ AtSidebar.seeSelectedRegion();
+
+ // Switch to move tool to force appearance of transformer
+ I.pressKey('v');
+ const isTransformerExist = await AtImageView.isTransformerExist();
+
+ assert.strictEqual(isTransformerExist, true);
+
+ // The rotator anchor must be above top anchor by 50 pixels
+ const rotatorPosition = {
+ x: rectangleCenter.x,
+ y: rectangle.y - 50,
+ };
+
+ // Rotate for 45 degrees clockwise
+ AtImageView.drawByDrag(rotatorPosition.x, rotatorPosition.y, rectangleCenter.y - rotatorPosition.y + 100, -100);
+
+ // Check the region hasn't been rotated
+ const rectangleResult = await LabelStudio.serialize();
+
+ Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 0);
+ });
+
+Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
+ .Scenario('Broke the limits with rotation', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
+ const { shapeName } = current;
+ const Shape = shapes[shapeName];
+
+ I.amOnPage('/');
+ LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+
+ {
+ // Draw a region which have limitation at rotating by bbox {x1:5,y1:100,x2:305,y2:350}
+ const rectangle = {
+ x: 5,
+ y: 100,
+ width: 300,
+ height: 300,
+ };
+ const rectangleCenter = {
+ x: rectangle.x + rectangle.width / 2,
+ y: rectangle.y + rectangle.height / 2,
+ };
+
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView);
+ AtSidebar.seeRegions(1);
+
+ // Select the shape and check that transformer appears
+ AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y);
+ AtSidebar.seeSelectedRegion();
+
+ // Switch to move tool to force appearance of transformer
+ I.pressKey('v');
+ const isTransformerExist = await AtImageView.isTransformerExist();
+
+ assert.strictEqual(isTransformerExist, true);
+
+ // The rotator anchor must be above top anchor by 50 pixels
+ const rotatorPosition = {
+ x: rectangleCenter.x,
+ y: rectangle.y - 50,
+ };
+
+ // Rotate for 45 degrees clockwise
+ AtImageView.drawThroughPoints([
+ [rotatorPosition.x, rotatorPosition.y],
+ [rectangleCenter.x + 500, rectangleCenter.y - 500],
+ ], 'steps', 200);
+
+ // Check that we cannot rotate it like this
+ let rectangleResult = await LabelStudio.serialize();
+
+ assert.notStrictEqual(
+ Math.round(rectangleResult[0].value.rotation),
+ 0,
+ 'Region must be rotated',
+ );
+ assert.notStrictEqual(
+ Math.round(rectangleResult[0].value.rotation),
+ 45,
+ 'Angle must not be 45 degrees',
+ );
+
+ // Undo changes
+ I.pressKey(['CommandOrControl', 'z']);
+
+ // Rotate for 90 degrees clockwise instead
+ AtImageView.drawThroughPoints([
+ [rotatorPosition.x, rotatorPosition.y],
+ [rectangle.x + rectangle.width + 100, rectangleCenter.y],
+ [rectangle.x + rectangle.width + 200, rectangleCenter.y],
+ ], 'steps', 200);
+
+ // Check the resulted rotation
+ rectangleResult = await LabelStudio.serialize();
+
+ Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 90, 'Angle must be 90 degrees');
+ // remove region
+ I.pressKey('Backspace');
+ }
+
+ I.say('Check that it works same way with right border');
+
+ {
+ // Draw a region which have limitation at rotating by bbox {x1:100% - 305,y1:100,x2:100% - 5,y2:350}
+ const rectangle = {
+ x: canvasSize.width - 305,
+ y: 100,
+ width: 300,
+ height: 300,
+ };
+ const rectangleCenter = {
+ x: rectangle.x + rectangle.width / 2,
+ y: rectangle.y + rectangle.height / 2,
+ };
+
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView);
+ AtSidebar.seeRegions(1);
+
+ // Select the shape and check that transformer appears
+ AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y);
+ AtSidebar.seeSelectedRegion();
+
+ // Switch to move tool to force appearance of transformer
+ I.pressKey('v');
+ const isTransformerExist = await AtImageView.isTransformerExist();
+
+ assert.strictEqual(isTransformerExist, true);
+
+ // The rotator anchor must be above top anchor by 50 pixels
+ const rotatorPosition = {
+ x: rectangleCenter.x,
+ y: rectangle.y - 50,
+ };
+
+ // Rotate for 45 degrees clockwise
+ AtImageView.drawThroughPoints([
+ [rotatorPosition.x, rotatorPosition.y],
+ [rectangleCenter.x + 500, rectangleCenter.y - 500],
+ ], 'steps', 200);
+
+ // Check the resulted rotation
+ let rectangleResult = await LabelStudio.serialize();
+
+ assert.notStrictEqual(Math.round(rectangleResult[0].value.rotation), 0);
+ assert.notStrictEqual(Math.round(rectangleResult[0].value.rotation), 45);
+
+ // Undo changes
+ I.pressKey(['CommandOrControl', 'z']);
+
+ // Rotate for 90 degrees clockwise instead
+ AtImageView.drawThroughPoints([
+ [rotatorPosition.x, rotatorPosition.y],
+ [rectangle.x + rectangle.width + 100, rectangleCenter.y],
+ [rectangle.x + rectangle.width + 200, rectangleCenter.y],
+ ], 'steps', 200);
+
+ // Check that we cannot rotate it like this
+ rectangleResult = await LabelStudio.serialize();
+
+ Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 90);
+ }
+
+ });
+
+Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
+ .Scenario('Check the initial rotation of transformer for the single region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
+ const { shapeName } = current;
+ const Shape = shapes[shapeName];
+
+ I.amOnPage('/');
+ LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+
+ const bbox = {
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ };
+ const bboxCenter = {
+ x: bbox.x + bbox.width / 2,
+ y: bbox.y + bbox.height / 2,
+ };
+
+ // Draw a region
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, bbox.x, bbox.y, bbox.width, bbox.height, AtImageView);
+ AtSidebar.seeRegions(1);
+
+ // Select it
+ AtImageView.clickAt(bboxCenter.x, bboxCenter.y);
+ AtSidebar.seeSelectedRegion();
+
+ // Switch to move tool to force appearance of transformer
+ I.pressKey('v');
+ const isTransformerExist = await AtImageView.isTransformerExist();
+
+ assert.strictEqual(isTransformerExist, true);
+
+ // The rotator anchor must be above top anchor by 50 pixels
+ let rotatorPosition = {
+ x: bboxCenter.x,
+ y: bbox.y - 50,
+ };
+
+ // Rotate for 90 degrees clockwise
+ AtImageView.drawThroughPoints([
+ [rotatorPosition.x, rotatorPosition.y],
+ [bbox.x + bbox.width + 100, bboxCenter.y],
+ [bbox.x + bbox.width + 200, bboxCenter.y],
+ ], 'steps', 10);
+
+ // Unselect current region
+ I.pressKey('u');
+ AtSidebar.dontSeeSelectedRegion();
+
+ // Select it again
+ AtImageView.clickAt(bboxCenter.x, bboxCenter.y);
+ AtSidebar.seeSelectedRegion();
+
+ // The trick is that we turn it further, based on the assumption that transformer appears in rotated state on region selection
+ // So let's try to rotate it
+ // The rotator anchor must be to the right of the right anchor by 50 pixels
+
+ rotatorPosition = {
+ x: bbox.x + bbox.width + 50,
+ y: bboxCenter.y,
+ };
+
+ // Rotate for 90 degrees clockwise once again
+ AtImageView.drawThroughPoints([
+ [rotatorPosition.x, rotatorPosition.y],
+ [bboxCenter.x, bbox.y + bbox.height + 100],
+ [bboxCenter.x, bbox.y + bbox.height + 200],
+ ], 'steps', 10);
+
+ // Check that region has been rotated for 180 degrees
+ const rectangleResult = await LabelStudio.serialize();
+
+ Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 180);
+ });
+
+Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
+ .Scenario('Check the initial rotation of transformer for the couple of regions', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
+ const { shapeName } = current;
+ const Shape = shapes[shapeName];
+
+ I.amOnPage('/');
+ LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+
+ const bbox1 = {
+ x: 100,
+ y: 100,
+ width: 40,
+ height: 40,
+ };
+
+ const bbox2 = {
+ x: 160,
+ y: 160,
+ width: 40,
+ height: 40,
+ };
+
+ const transformerBbox = {
+ x: bbox1.x,
+ y: bbox1.y,
+ width: bbox2.x + bbox2.width - bbox1.x,
+ height: bbox2.y + bbox2.height - bbox1.y,
+ };
+ const transformerBboxCenter = {
+ x: transformerBbox.x + transformerBbox.width / 2,
+ y: transformerBbox.y + transformerBbox.height / 2,
+ };
+
+ // Draw the first region
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView);
+ AtSidebar.seeRegions(1);
+
+ // Draw the second region
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView);
+ AtSidebar.seeRegions(2);
+
+ // Switch to move tool and select them
+ I.pressKey('v');
+ AtImageView.drawThroughPoints([
+ [transformerBbox.x - 20, transformerBbox.y - 20],
+ [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20],
+ ]);
+ AtSidebar.seeSelectedRegion();
+
+ // The rotator anchor must be above top anchor by 50 pixels
+ const rotatorPosition = {
+ x: transformerBboxCenter.x,
+ y: transformerBbox.y - 50,
+ };
+
+ // Rotate for 180 degrees clockwise
+ AtImageView.drawThroughPoints([
+ [rotatorPosition.x, rotatorPosition.y],
+ [transformerBboxCenter.x + 100, transformerBboxCenter.y + 100],
+ [transformerBboxCenter.x, transformerBboxCenter.y + 100],
+ [transformerBboxCenter.x, transformerBboxCenter.y + 200],
+ ], 'steps', 10);
+
+ // Unselect current regions
+ I.pressKey('u');
+ AtSidebar.dontSeeSelectedRegion();
+
+ // Select them again
+ AtImageView.drawThroughPoints([
+ [transformerBbox.x - 20, transformerBbox.y - 20],
+ [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20],
+ ]);
+ AtSidebar.seeSelectedRegion();
+
+ // So we have couple of rotated regions, let's check if rotates still appears above the top anchor
+
+ // Rotate for 90 degrees clockwise
+ AtImageView.drawThroughPoints([
+ [rotatorPosition.x, rotatorPosition.y],
+ [transformerBboxCenter.x + 100, transformerBboxCenter.y],
+ [transformerBboxCenter.x + 200, transformerBboxCenter.y],
+ ], 'steps', 10);
+
+ // Check that region has been rotated for (180 + 90) degrees
+ const rectangleResult = await LabelStudio.serialize();
+
+ Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 180 + 90);
+ });
+
+// KeyPoints are transformed unpredictable so for now just skip them
+Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionTransformer && shapeName !== 'KeyPoint'))
+ .Scenario('Transforming of multiple regions', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
+ const { shapeName } = current;
+ const Shape = shapes[shapeName];
+
+ I.amOnPage('/');
+ LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+ const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height);
+
+ const bbox1 = {
+ x: 100,
+ y: 100,
+ width: 50,
+ height: 50,
+ };
+
+ const bbox2 = {
+ x: 150,
+ y: 150,
+ width: 50,
+ height: 50,
+ };
+
+ const transformerBbox = {
+ x: bbox1.x,
+ y: bbox1.y,
+ width: bbox2.x + bbox2.width - bbox1.x,
+ height: bbox2.y + bbox2.height - bbox1.y,
+ };
+ const transformerBboxCenter = {
+ get x() {
+ return transformerBbox.x + transformerBbox.width / 2;
+ },
+ get y() {
+ return transformerBbox.y + transformerBbox.height / 2;
+ },
+ };
+
+ // Draw the first region
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView);
+ AtSidebar.seeRegions(1);
+
+ // Draw the second region
+ I.pressKey(Shape.hotKey);
+ I.pressKeyDown('Control');
+ drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView);
+ I.pressKeyUp('Control');
+ AtSidebar.seeRegions(2);
+
+ // Switch to move tool and select them
+ I.pressKey('v');
+ AtImageView.drawThroughPoints([
+ [transformerBbox.x - 20, transformerBbox.y - 20],
+ [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20],
+ ]);
+ AtSidebar.seeSelectedRegion();
+ // Scale the shapes vertically
+ AtImageView.drawByDrag(transformerBboxCenter.x, transformerBbox.y + transformerBbox.height, 0, 50);
+ transformerBbox.height += 50;
+ AtSidebar.seeSelectedRegion();
+ // Scale the shapes horizontally
+ AtImageView.drawByDrag(transformerBbox.x + transformerBbox.width, transformerBboxCenter.y, 50, 0);
+ transformerBbox.width += 50;
+ AtSidebar.seeSelectedRegion();
+ // Scale the shapes in both directions
+ AtImageView.drawByDrag(transformerBbox.x + transformerBbox.width, transformerBbox.y + transformerBbox.height, 50, 50);
+ transformerBbox.height += 50;
+ transformerBbox.width += 50;
+ AtSidebar.seeSelectedRegion();
+
+ // Check resulting sizes
+ const rectangleResult = await LabelStudio.serialize();
+ const exceptedResult1 = Shape.byBBox(bbox1.x, bbox1.y, bbox1.width + 50, bbox1.height + 50).result;
+ const exceptedResult2 = Shape.byBBox(bbox2.x + 50, bbox2.y + 50, bbox2.width + 50, bbox2.height + 50).result;
+
+ Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult1));
+ Asserts.deepEqualWithTolerance(rectangleResult[1].value, convertToImageSize(exceptedResult2));
+ });
+
+Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionTransformer))
+ .Scenario('Move regions by drag', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
+ const { shapeName } = current;
+ const Shape = shapes[shapeName];
+
+ I.amOnPage('/');
+ LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+ const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height);
+
+ const bbox1 = {
+ x: 100,
+ y: 100,
+ width: 20,
+ height: 20,
+ };
+ const bbox1Center = {
+ x: bbox1.x + bbox1.width / 2,
+ y: bbox1.y + bbox1.height / 2,
+ };
+
+ const bbox2 = {
+ x: 140,
+ y: 140,
+ width: 20,
+ height: 20,
+ };
+ const bbox2Center = {
+ x: bbox2.x + bbox2.width / 2,
+ y: bbox2.y + bbox2.height / 2,
+ };
+
+ const transformerBbox = {
+ x: bbox1.x,
+ y: bbox1.y,
+ width: bbox2.x + bbox2.width - bbox1.x,
+ height: bbox2.y + bbox2.height - bbox1.y,
+ };
+ const transformerBboxCenter = {
+ x: transformerBbox.x + transformerBbox.width / 2,
+ y: transformerBbox.y + transformerBbox.height / 2,
+ };
+
+ // Draw the first region
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView);
+ AtSidebar.seeRegions(1);
+
+ // Draw the second region
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView);
+ AtSidebar.seeRegions(2);
+
+ if (shapeName === 'KeyPoint') {
+ // Draw more points to get more space in transformer
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, bbox1.x, bbox1.y, 0, 0, AtImageView);
+ AtSidebar.seeRegions(3);
+
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, bbox2.x + bbox2.width, bbox2.y + bbox2.height, 0, 0, AtImageView);
+ AtSidebar.seeRegions(4);
+ }
+
+ // Switch to move tool and select them
+ I.pressKey('v');
+ AtImageView.drawThroughPoints([
+ [transformerBbox.x - 20, transformerBbox.y - 20],
+ [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20],
+ ]);
+ AtSidebar.seeSelectedRegion();
+
+ const dragShapes = (startPoint, shift, rememberShift = true) => {
+ AtImageView.drawThroughPoints([
+ [startPoint.x, startPoint.y],
+ [startPoint.x + shift.x, startPoint.y + shift.y],
+ [startPoint.x + shift.x, startPoint.y + shift.y],
+ ], 'steps', 10);
+ AtSidebar.seeSelectedRegion();
+
+ if (rememberShift) {
+ bbox1Center.x += shift.x;
+ bbox1Center.y += shift.y;
+ bbox2Center.x += shift.x;
+ bbox2Center.y += shift.y;
+ transformerBboxCenter.x += shift.x;
+ transformerBboxCenter.y += shift.y;
+ }
+ };
+
+ // Drag shapes by holding onto the first region
+ dragShapes(bbox1Center, { x: 100, y: 0 });
+ // Drag shapes by holding onto the second region
+ dragShapes(bbox2Center, { x: 0, y: 100 });
+ // Drag shapes by holding onto the transformer background
+ dragShapes(transformerBboxCenter, { x: 150, y: 150 }, false);
+ // Move back throught history to check that transformer's background moving with it
+ I.pressKey(['CommandOrControl', 'z']);
+ // Drag shapes by holding onto the transformer background again
+ dragShapes(transformerBboxCenter, { x: 100, y: 100 }, false);
+
+
+ // Check that dragging was successful
+ const rectangleResult = await LabelStudio.serialize();
+ const exceptedResult1 = Shape.byBBox(bbox1.x + 200, bbox1.y + 200, bbox1.width, bbox1.height).result;
+ const exceptedResult2 = Shape.byBBox(bbox2.x + 200, bbox2.y + 200, bbox2.width, bbox2.height).result;
+
+ Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult1));
+ Asserts.deepEqualWithTolerance(rectangleResult[1].value, convertToImageSize(exceptedResult2));
+ });
+
+Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
+ .Scenario('Limitation of dragging a single rotated region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
+ const { shapeName } = current;
+ const Shape = shapes[shapeName];
+
+ I.amOnPage('/');
+ LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+
+ const bbox = {
+ x: canvasSize.width / 2 - 50,
+ y: canvasSize.height / 2 - 50,
+ width: 100,
+ height: 100,
+ };
+ const bboxCenter = {
+ x: bbox.x + bbox.width / 2,
+ y: bbox.y + bbox.height / 2,
+ };
+
+ // Draw a region
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, bbox.x, bbox.y, bbox.width, bbox.height, AtImageView);
+ AtSidebar.seeRegions(1);
+
+ // Select it
+ AtImageView.clickAt(bboxCenter.x, bboxCenter.y);
+ AtSidebar.seeSelectedRegion();
+
+ // Switch to move tool to force appearance of transformer
+ I.pressKey('v');
+ const isTransformerExist = await AtImageView.isTransformerExist();
+
+ assert.strictEqual(isTransformerExist, true);
+
+ // The rotator anchor must be above top anchor by 50 pixels
+ const rotatorPosition = {
+ x: bboxCenter.x,
+ y: bbox.y - 50,
+ };
+
+ // Rotate for 180 degrees clockwise
+ AtImageView.drawThroughPoints([
+ [rotatorPosition.x, rotatorPosition.y],
+ [bboxCenter.x + 100, bboxCenter.y],
+ [bboxCenter.x, bboxCenter.y + 100],
+ [bboxCenter.x, bboxCenter.y + 200],
+ ], 'steps', 10);
+
+ // When we have the rotated region, we need to check its behavior when we drag it across the borders of the image
+ let rectangleResult;
+
+ I.say('Drag the region over the left border');
+ AtImageView.drawThroughPoints([
+ [bboxCenter.x, bboxCenter.y],
+ [-500, bboxCenter.y],
+ ], 'steps', 20);
+ AtSidebar.seeSelectedRegion();
+ // moving of the region should be constrained by borders
+ rectangleResult = await LabelStudio.serialize();
+ Asserts.deepEqualWithTolerance(
+ rectangleResult[0].value.x * canvasSize.width / 100,
+ Shape.byBBox(bbox.width, bbox.y + bbox.height, -bbox.width, -bbox.height).result.x,
+ );
+ // reset position by undo
+ I.pressKey(['CommandOrControl', 'z']);
+
+ I.say('Drag the region over the top border');
+ AtImageView.drawThroughPoints([
+ [bboxCenter.x, bboxCenter.y],
+ [bboxCenter.x, -500],
+ ], 'steps', 20);
+ AtSidebar.seeSelectedRegion();
+ // moving of the region should be constrained by borders
+ rectangleResult = await LabelStudio.serialize();
+ Asserts.deepEqualWithTolerance(
+ rectangleResult[0].value.y * canvasSize.height / 100,
+ Shape.byBBox(bbox.x + bbox.width, bbox.height, -bbox.width, -bbox.height).result.y,
+ );
+ // reset position by undo
+ I.pressKey(['CommandOrControl', 'z']);
+
+ I.say('Drag the region over the right border');
+ AtImageView.drawThroughPoints([
+ [bboxCenter.x, bboxCenter.y],
+ [canvasSize.width + 500, bboxCenter.y],
+ ], 'steps', 20);
+ AtSidebar.seeSelectedRegion();
+ // moving of the region should be constrained by borders
+ rectangleResult = await LabelStudio.serialize();
+
+ Asserts.deepEqualWithTolerance(
+ rectangleResult[0].value.x * canvasSize.width / 100,
+ Shape.byBBox(canvasSize.width, bbox.y + bbox.height, -bbox.width, -bbox.height).result.x,
+ );
+ // reset position by undo
+ I.pressKey(['CommandOrControl', 'z']);
+
+ I.say('Drag the region over the bottom border');
+ AtImageView.drawThroughPoints([
+ [bboxCenter.x, bboxCenter.y],
+ [bboxCenter.x, canvasSize.height + 500],
+ ], 'steps', 20);
+ AtSidebar.seeSelectedRegion();
+ // moving of the region should be constrained by borders
+ rectangleResult = await LabelStudio.serialize();
+ Asserts.deepEqualWithTolerance(
+ rectangleResult[0].value.y * canvasSize.height / 100,
+ Shape.byBBox(bbox.x + bbox.width, canvasSize.height, -bbox.width, -bbox.height).result.y,
+ );
+ });
+
+Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator))
+ .Scenario('Limitation of dragging a couple of rotated regions', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
+ const { shapeName } = current;
+ const Shape = shapes[shapeName];
+
+ I.amOnPage('/');
+ LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+
+ const bbox1 = {
+ x: canvasSize.width / 2 - 50,
+ y: canvasSize.height / 2 - 50,
+ width: 50,
+ height: 50,
+ };
+
+ const bbox2 = {
+ x: canvasSize.width / 2,
+ y: canvasSize.height / 2,
+ width: 50,
+ height: 50,
+ };
+
+ const transformerBbox = {
+ x: bbox1.x,
+ y: bbox1.y,
+ width: bbox2.x + bbox2.width - bbox1.x,
+ height: bbox2.y + bbox2.height - bbox1.y,
+ };
+ const transformerBboxCenter = {
+ get x() {
+ return transformerBbox.x + transformerBbox.width / 2;
+ },
+ get y() {
+ return transformerBbox.y + transformerBbox.height / 2;
+ },
+ };
+
+ // Draw the first region
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView);
+ AtSidebar.seeRegions(1);
+
+ // Draw the second region
+ I.pressKey(Shape.hotKey);
+ I.pressKeyDown('Control');
+ drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView);
+ I.pressKeyUp('Control');
+ AtSidebar.seeRegions(2);
+
+ // Select them by move tool
+ I.pressKey('v');
+ AtImageView.drawThroughPoints(
+ [
+ [transformerBbox.x - 50, transformerBbox.y - 50],
+ [transformerBbox.x + transformerBbox.width + 50, transformerBbox.y + transformerBbox.height + 50],
+ ], 'steps', 10,
+ );
+ AtSidebar.seeSelectedRegion();
+
+ // The rotator anchor must be above top anchor by 50 pixels
+ const rotatorPosition = {
+ x: transformerBboxCenter.x,
+ y: transformerBbox.y - 50,
+ };
+
+ // Rotate for 180 degrees clockwise
+ AtImageView.drawThroughPoints([
+ [rotatorPosition.x, rotatorPosition.y],
+ [transformerBboxCenter.x + 100, transformerBboxCenter.y],
+ [transformerBboxCenter.x, transformerBboxCenter.y + 100],
+ [transformerBboxCenter.x, transformerBboxCenter.y + 200],
+ ], 'steps', 10);
+
+ // When we have the rotated region, we need to check its behavior when we drag it across the borders of the image
+ let rectangleResult;
+
+ I.say('Drag the region over the left border');
+ AtImageView.drawThroughPoints([
+ [transformerBboxCenter.x, transformerBboxCenter.y],
+ [-500, transformerBboxCenter.y],
+ ], 'steps', 20);
+ AtSidebar.seeSelectedRegion();
+ // moving of the region should be constrained by borders
+ rectangleResult = await LabelStudio.serialize();
+ Asserts.deepEqualWithTolerance(
+ rectangleResult[0].value.x * canvasSize.width / 100,
+ Shape.byBBox(transformerBbox.width, transformerBbox.y + transformerBbox.height, -bbox1.width, -bbox1.height).result.x,
+ );
+ Asserts.deepEqualWithTolerance(
+ rectangleResult[1].value.x * canvasSize.width / 100,
+ Shape.byBBox(bbox2.width, transformerBbox.y + bbox2.height, -bbox2.width, -bbox2.height).result.x,
+ );
+ // reset position by undo
+ I.pressKey(['CommandOrControl', 'z']);
+
+ I.say('Drag the region over the top border');
+ AtImageView.drawThroughPoints([
+ [transformerBboxCenter.x, transformerBboxCenter.y],
+ [transformerBboxCenter.x, -500],
+ ], 'steps', 20);
+ AtSidebar.seeSelectedRegion();
+ // moving of the region should be constrained by borders
+ rectangleResult = await LabelStudio.serialize();
+ Asserts.deepEqualWithTolerance(
+ rectangleResult[0].value.y * canvasSize.height / 100,
+ Shape.byBBox(transformerBbox.x + transformerBbox.width, transformerBbox.height, -bbox1.width, -bbox1.height).result.y,
+ );
+ Asserts.deepEqualWithTolerance(
+ rectangleResult[1].value.y * canvasSize.height / 100,
+ Shape.byBBox(transformerBbox.x + bbox2.width, bbox2.height, -bbox2.width, -bbox2.height).result.y,
+ );
+ // reset position by undo
+ I.pressKey(['CommandOrControl', 'z']);
+
+ I.say('Drag the region over the right border');
+ AtImageView.drawThroughPoints([
+ [transformerBboxCenter.x, transformerBboxCenter.y],
+ [canvasSize.width + 500, transformerBboxCenter.y],
+ ], 'steps', 20);
+ AtSidebar.seeSelectedRegion();
+ // moving of the region should be constrained by borders
+ rectangleResult = await LabelStudio.serialize();
+
+ Asserts.deepEqualWithTolerance(
+ rectangleResult[0].value.x * canvasSize.width / 100,
+ Shape.byBBox(canvasSize.width, transformerBbox.y + transformerBbox.height, -bbox1.width, -bbox1.height).result.x,
+ );
+ Asserts.deepEqualWithTolerance(
+ rectangleResult[1].value.x * canvasSize.width / 100,
+ Shape.byBBox(canvasSize.width - transformerBbox.width + bbox2.width, transformerBbox.y + bbox2.height, -bbox2.width, -bbox2.height).result.x,
+ );
+ // reset position by undo
+ I.pressKey(['CommandOrControl', 'z']);
+
+ I.say('Drag the region over the bottom border');
+ AtImageView.drawThroughPoints([
+ [transformerBboxCenter.x, transformerBboxCenter.y],
+ [transformerBboxCenter.x, canvasSize.height + 500],
+ ], 'steps', 20);
+ AtSidebar.seeSelectedRegion();
+ // moving of the region should be constrained by borders
+ rectangleResult = await LabelStudio.serialize();
+ Asserts.deepEqualWithTolerance(
+ rectangleResult[0].value.y * canvasSize.height / 100,
+ Shape.byBBox(transformerBbox.x + transformerBbox.width, canvasSize.height, -bbox1.width, -bbox1.height).result.y,
+ );
+ Asserts.deepEqualWithTolerance(
+ rectangleResult[1].value.y * canvasSize.height / 100,
+ Shape.byBBox(transformerBbox.x + bbox2.width, canvasSize.height - transformerBbox.height + bbox2.height, -bbox2.width, -bbox2.height).result.y,
+ );
+ });
+
+Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasRotator))
+ .Scenario('Rotating the region near the border', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => {
+ const { shapeName } = current;
+ const Shape = shapes[shapeName];
+
+ I.amOnPage('/');
+ LabelStudio.init(getParamsWithShape(shapeName, Shape.params));
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+
+ const bbox = {
+ x: canvasSize.width - Math.ceil(Math.sqrt(100 ** 2 + 100 ** 2)) / 2 - 50,
+ y: canvasSize.height - Math.ceil(Math.sqrt(100 ** 2 + 100 ** 2)) / 2 - 50,
+ width: 100,
+ height: 100,
+ };
+
+ const bboxCenter = {
+ x: bbox.x + bbox.width / 2,
+ y: bbox.y + bbox.height / 2,
+ };
+
+ // Draw the region
+ I.pressKey(Shape.hotKey);
+ drawShapeByBbox(Shape, bbox.x, bbox.y, bbox.width, bbox.height, AtImageView);
+ AtSidebar.seeRegions(1);
+
+ // Select it
+ AtImageView.clickAt(bboxCenter.x, bboxCenter.y);
+ AtSidebar.seeSelectedRegion();
+
+ // The rotator anchor must be above top anchor by 50 pixels
+ const rotatorPosition = {
+ x: bboxCenter.x,
+ y: bbox.y - 50,
+ };
+
+ // Check 7 different rotations
+ const rotatorWayPoints = [[rotatorPosition.x, rotatorPosition.y]];
+ const angle45 = Math.PI / 4;
+
+ for (let i = 0; i < 8; i++) {
+ const angle = angle45 * i;
+
+ rotatorWayPoints.push([bboxCenter.x + Math.sin(angle) * 100, bboxCenter.y - Math.cos(angle) * 100]);
+ rotatorWayPoints.push([bboxCenter.x + Math.sin(angle) * 1000, bboxCenter.y - Math.cos(angle) * 1000]);
+
+ // Rotate clockwise by 45 * i degrees
+ AtImageView.drawThroughPoints(rotatorWayPoints, 'steps', 10);
+ AtSidebar.seeSelectedRegion();
+ // Check that rotating was successful
+ const rectangleResult = await LabelStudio.serialize();
+
+ Asserts.deepEqualWithTolerance(
+ Math.round(rectangleResult[0].value.rotation),
+ 45 * i,
+ );
+
+ // undo rotation
+ I.pressKey(['CommandOrControl', 'z']);
+ // clear unnecessary waypoints
+ rotatorWayPoints.pop();
+ }
+ });
diff --git a/e2e/tests/image.zoom-rotate.test.js b/e2e/tests/image.zoom-rotate.test.js
index 30ea619dbf..07ee2475f0 100644
--- a/e2e/tests/image.zoom-rotate.test.js
+++ b/e2e/tests/image.zoom-rotate.test.js
@@ -4,8 +4,7 @@ const assert = require('assert');
Feature('Zooming and rotating');
-const IMAGE =
- 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
const BLUEVIOLET = {
color: '#8A2BE2',
diff --git a/e2e/tests/maxUsage.test.js b/e2e/tests/maxUsage.test.js
index e00b75e119..fc21118380 100644
--- a/e2e/tests/maxUsage.test.js
+++ b/e2e/tests/maxUsage.test.js
@@ -1,341 +1,340 @@
-Feature('Max usage');
-
-const IMAGE = 'https://user.fm/files/v2-901310d5cb3fa90e0616ca10590bacb3/spacexmoon-800x501.jpg';
-
-const createImageToolsConfig = ({ maxUsage }) => `
-
-
-
-
-
-
-
-
-
-
-
-`;
-
-const createImageLabelsConfig = ({ maxUsage }) => `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `;
-
-const shapes = {
- Rectangle: {
- drawAction: 'drawByDrag',
- shortcut: 'r',
- hotkey: '1',
- byBBox(x, y, width, height) {
- return {
- params: [x, y, width, height],
- };
- },
- },
- Ellipse: {
- drawAction: 'drawByDrag',
- shortcut: 'o',
- hotkey: '2',
- byBBox(x, y, width, height) {
- return {
- params: [x + width / 2, y + height / 2, width / 2, height / 2],
- };
- },
- },
- Brush: {
- drawAction: 'clickAt',
- shortcut: 'b',
- hotkey: '3',
- byBBox(x, y, width, height) {
- return {
- params: [x + width / 2, y + height / 2],
- };
- },
- },
- KeyPoint: {
- drawAction: 'clickAt',
- shortcut: 'k',
- hotkey: '4',
- byBBox(x, y, width, height) {
- return {
- params: [x + width / 2, y + height / 2],
- };
- },
- },
- Polygon: {
- drawAction: 'drawByClickingPoints',
- shortcut: 'p',
- hotkey: '5',
- byBBox(x, y, width, height) {
- const points = [];
-
- points.push([x, y]);
- points.push([x + width, y]);
- points.push([x + width / 2, y + height]);
- return {
- params: [[...points, points[0]]],
- };
- },
- },
-};
-
-function drawShapeByBbox(Shape, x, y, width, height, where) {
- where[Shape.drawAction](...Shape.byBBox(x, y, width, height).params);
-}
-
-const maxUsageImageToolsDataTable = new DataTable(['maxUsage', 'shapeName']);
-
-[1,3].forEach(maxUsage => {
- Object.keys(shapes).forEach(shapeName => {
- maxUsageImageToolsDataTable.add([maxUsage, shapeName]);
- });
-});
-
-const maxUsageDataTable = new DataTable(['maxUsage']);
-
-[1,3].forEach(maxUsage => {
- maxUsageDataTable.add([maxUsage]);
-});
-/*
-Data(maxUsageImageToolsDataTable).Scenario('Max usages of separated labels in ImageView on region creating', async function({ I, LabelStudio, AtImageView, AtSidebar, current }) {
- const { maxUsage, shapeName } = current;
- const shape = shapes[shapeName];
- const annotations = [];
-
- for (let k = 0; k < maxUsage; k++) {
- annotations.push({
- 'value': {
- 'x': k,
- 'y': 1,
- 'width': 0.6666666666666666,
- 'labels': [
- 'Label_1',
- ],
- },
- 'id': k,
- 'from_name': 'Labels',
- 'to_name': 'img',
- 'type': 'labels',
- });
- }
-
- I.amOnPage('/');
- LabelStudio.setFeatureFlags({
- fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true,
- });
- LabelStudio.init({
- config: createImageToolsConfig({ maxUsage }),
- data: {
- image: IMAGE,
- },
- annotations: [{
- id: 'test',
- result: annotations,
- }],
- });
- await AtImageView.waitForImage();
- await AtImageView.lookForStage();
- AtSidebar.seeRegions(maxUsage);
-
-
- I.pressKey('1');
- I.pressKey(shape.shortcut);
- AtImageView.clickAt(50, 50);
-
- I.see(`You can't use Label_1 more than ${maxUsage} time(s)`);
-});
-
-Data(maxUsageImageToolsDataTable).Scenario('Max usages of labels in ImageView on region creating', async function({ I, LabelStudio, AtImageView, AtSidebar, current }) {
- const { maxUsage, shapeName } = current;
- const shape = shapes[shapeName];
-
- I.amOnPage('/');
- LabelStudio.setFeatureFlags({
- fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true,
- });
- LabelStudio.init({
- config: createImageLabelsConfig({ maxUsage }),
- data: {
- image: IMAGE,
- },
- });
-
- await AtImageView.waitForImage();
- await AtImageView.lookForStage();
- AtSidebar.seeRegions(0);
-
- for (let k = 0; k < maxUsage; k++) {
- I.pressKey(shape.hotkey);
- drawShapeByBbox(shape, 1 + 50 * k, 1, 30,30, AtImageView);
- I.pressKey('u');
- }
-
- I.pressKey(shape.hotkey);
- AtImageView.clickAt(50, 50);
-
- I.see(`You can't use ${shapeName}_1 more than ${maxUsage} time(s)`);
-});
-
-Data(maxUsageDataTable).Scenario('Max usages of labels in Audio on region creation', async function({ I, LabelStudio, AtSidebar, AtAudioView, current }) {
- const { maxUsage } = current;
-
- LabelStudio.setFeatureFlags({
- fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true,
- ff_front_dev_2715_audio_3_280722_short: true,
- });
- I.amOnPage('/');
- LabelStudio.init({
- config: `
-
-
-
-
-
-
-`,
- data: {
- audio: 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/audio/barradeen-emotional.mp3',
- },
- });
-
- await AtAudioView.waitForAudio();
- await AtAudioView.lookForStage();
- AtSidebar.seeRegions(0);
-
- for (let k = 0; k < maxUsage; k++) {
- I.pressKey('1');
- AtAudioView.dragAudioElement(10 + 40 * k,30);
- I.pressKey('u');
- }
- I.pressKey('1');
- AtAudioView.dragAudioElement(10 + 40 * maxUsage,30);
-
- AtSidebar.seeRegions(maxUsage);
- I.see(`You can't use Label_1 more than ${maxUsage} time(s)`);
-});
-
-Data(maxUsageDataTable).Scenario('Max usages of labels in RichText on region creation', async function({ I, LabelStudio, AtSidebar, AtRichText, current }) {
- const { maxUsage } = current;
-
- I.amOnPage('/');
- LabelStudio.setFeatureFlags({
- fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true,
- });
- LabelStudio.init({
- config: `
-
-
-
-
-
-
-`,
- data: {
- url: 'https://htx-pub.s3.amazonaws.com/example.txt',
- },
- });
-
- LabelStudio.waitForObjectsReady();
- AtSidebar.seeRegions(0);
-
- for (let k = 0; k < maxUsage; k++) {
- I.pressKey('1');
- AtRichText.selectTextByGlobalOffset(1 + 5 * k, 5 * (k + 1));
- I.pressKey('u');
- }
- I.pressKey('1');
- AtRichText.selectTextByGlobalOffset(1 + 5 * maxUsage, 5 * (maxUsage + 1));
-
- I.see(`You can't use Label_1 more than ${maxUsage} time(s)`);
-});
-
-Data(maxUsageDataTable).Scenario('Max usages of labels in Paragraphs on region creation', async function({ I, LabelStudio, AtSidebar, AtParagraphs, current }) {
- const { maxUsage } = current;
-
- I.amOnPage('/');
- LabelStudio.setFeatureFlags({
- fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true,
- });
- LabelStudio.init({
- config: `
-
-
-
-
-
-
-`,
- data: require('../examples/text-paragraphs').data,
- });
-
- LabelStudio.waitForObjectsReady();
- AtSidebar.seeRegions(0);
-
- for (let k = 0; k < maxUsage; k++) {
- I.pressKey('1');
- AtParagraphs.selectTextByOffset(k + 1, 0, 3);
- I.pressKey('u');
- }
- I.pressKey('1');
- AtParagraphs.selectTextByOffset(maxUsage + 1, 0, 3);
-
- I.see(`You can't use Label_1 more than ${maxUsage} time(s)`);
-});
-
-Data(maxUsageDataTable).Scenario('Max usages of labels in Timeseries on region creation', async function({ I, LabelStudio, AtSidebar, AtTimeSeries, current }) {
- const { maxUsage } = current;
-
- I.amOnPage('/');
- LabelStudio.setFeatureFlags({
- fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true,
- });
- LabelStudio.init({
- config: `
-
-
-
-
-
-
-
-
-
-`,
- data: require('../examples/data/sample-sin.json'),
- });
-
- LabelStudio.waitForObjectsReady();
- AtSidebar.seeRegions(0);
- await AtTimeSeries.lookForStage();
-
- for (let k = 0; k < maxUsage; k++) {
- I.pressKey('1');
- AtTimeSeries.drawByDrag(1 + k * 20, 10);
- I.pressKey('u');
- }
- I.pressKey('1');
- AtTimeSeries.drawByDrag(1 + maxUsage * 20, 10);
-
- I.see(`You can't use Label_1 more than ${maxUsage} time(s)`);
-});
-*/
\ No newline at end of file
+Feature('Max usage');
+
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
+
+const createImageToolsConfig = ({ maxUsage }) => `
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+const createImageLabelsConfig = ({ maxUsage }) => `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+const shapes = {
+ Rectangle: {
+ drawAction: 'drawByDrag',
+ shortcut: 'r',
+ hotkey: '1',
+ byBBox(x, y, width, height) {
+ return {
+ params: [x, y, width, height],
+ };
+ },
+ },
+ Ellipse: {
+ drawAction: 'drawByDrag',
+ shortcut: 'o',
+ hotkey: '2',
+ byBBox(x, y, width, height) {
+ return {
+ params: [x + width / 2, y + height / 2, width / 2, height / 2],
+ };
+ },
+ },
+ Brush: {
+ drawAction: 'clickAt',
+ shortcut: 'b',
+ hotkey: '3',
+ byBBox(x, y, width, height) {
+ return {
+ params: [x + width / 2, y + height / 2],
+ };
+ },
+ },
+ KeyPoint: {
+ drawAction: 'clickAt',
+ shortcut: 'k',
+ hotkey: '4',
+ byBBox(x, y, width, height) {
+ return {
+ params: [x + width / 2, y + height / 2],
+ };
+ },
+ },
+ Polygon: {
+ drawAction: 'drawByClickingPoints',
+ shortcut: 'p',
+ hotkey: '5',
+ byBBox(x, y, width, height) {
+ const points = [];
+
+ points.push([x, y]);
+ points.push([x + width, y]);
+ points.push([x + width / 2, y + height]);
+ return {
+ params: [[...points, points[0]]],
+ };
+ },
+ },
+};
+
+function drawShapeByBbox(Shape, x, y, width, height, where) {
+ where[Shape.drawAction](...Shape.byBBox(x, y, width, height).params);
+}
+
+const maxUsageImageToolsDataTable = new DataTable(['maxUsage', 'shapeName']);
+
+[1,3].forEach(maxUsage => {
+ Object.keys(shapes).forEach(shapeName => {
+ maxUsageImageToolsDataTable.add([maxUsage, shapeName]);
+ });
+});
+
+const maxUsageDataTable = new DataTable(['maxUsage']);
+
+[1,3].forEach(maxUsage => {
+ maxUsageDataTable.add([maxUsage]);
+});
+
+Data(maxUsageImageToolsDataTable).Scenario('Max usages of separated labels in ImageView on region creating', async function({ I, LabelStudio, AtImageView, AtSidebar, current }) {
+ const { maxUsage, shapeName } = current;
+ const shape = shapes[shapeName];
+ const annotations = [];
+
+ for (let k = 0; k < maxUsage; k++) {
+ annotations.push({
+ 'value': {
+ 'x': k,
+ 'y': 1,
+ 'width': 0.6666666666666666,
+ 'labels': [
+ 'Label_1',
+ ],
+ },
+ 'id': k,
+ 'from_name': 'Labels',
+ 'to_name': 'img',
+ 'type': 'labels',
+ });
+ }
+
+ I.amOnPage('/');
+ LabelStudio.setFeatureFlags({
+ fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true,
+ });
+ LabelStudio.init({
+ config: createImageToolsConfig({ maxUsage }),
+ data: {
+ image: IMAGE,
+ },
+ annotations: [{
+ id: 'test',
+ result: annotations,
+ }],
+ });
+ await AtImageView.waitForImage();
+ await AtImageView.lookForStage();
+ AtSidebar.seeRegions(maxUsage);
+
+
+ I.pressKey('1');
+ I.pressKey(shape.shortcut);
+ AtImageView.clickAt(50, 50);
+
+ I.see(`You can't use Label_1 more than ${maxUsage} time(s)`);
+});
+
+Data(maxUsageImageToolsDataTable).Scenario('Max usages of labels in ImageView on region creating', async function({ I, LabelStudio, AtImageView, AtSidebar, current }) {
+ const { maxUsage, shapeName } = current;
+ const shape = shapes[shapeName];
+
+ I.amOnPage('/');
+ LabelStudio.setFeatureFlags({
+ fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true,
+ });
+ LabelStudio.init({
+ config: createImageLabelsConfig({ maxUsage }),
+ data: {
+ image: IMAGE,
+ },
+ });
+
+ await AtImageView.waitForImage();
+ await AtImageView.lookForStage();
+ AtSidebar.seeRegions(0);
+
+ for (let k = 0; k < maxUsage; k++) {
+ I.pressKey(shape.hotkey);
+ drawShapeByBbox(shape, 1 + 50 * k, 1, 30,30, AtImageView);
+ I.pressKey('u');
+ }
+
+ I.pressKey(shape.hotkey);
+ AtImageView.clickAt(50, 50);
+
+ I.see(`You can't use ${shapeName}_1 more than ${maxUsage} time(s)`);
+});
+
+Data(maxUsageDataTable).Scenario('Max usages of labels in Audio on region creation', async function({ I, LabelStudio, AtSidebar, AtAudioView, current }) {
+ const { maxUsage } = current;
+
+ LabelStudio.setFeatureFlags({
+ fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true,
+ ff_front_dev_2715_audio_3_280722_short: true,
+ });
+ I.amOnPage('/');
+ LabelStudio.init({
+ config: `
+
+
+
+
+
+
+`,
+ data: {
+ audio: 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/audio/barradeen-emotional.mp3',
+ },
+ });
+
+ await AtAudioView.waitForAudio();
+ await AtAudioView.lookForStage();
+ AtSidebar.seeRegions(0);
+
+ for (let k = 0; k < maxUsage; k++) {
+ I.pressKey('1');
+ AtAudioView.dragAudioElement(10 + 40 * k,30);
+ I.pressKey('u');
+ }
+ I.pressKey('1');
+ AtAudioView.dragAudioElement(10 + 40 * maxUsage,30);
+
+ AtSidebar.seeRegions(maxUsage);
+ I.see(`You can't use Label_1 more than ${maxUsage} time(s)`);
+});
+
+Data(maxUsageDataTable).Scenario('Max usages of labels in RichText on region creation', async function({ I, LabelStudio, AtSidebar, AtRichText, current }) {
+ const { maxUsage } = current;
+
+ I.amOnPage('/');
+ LabelStudio.setFeatureFlags({
+ fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true,
+ });
+ LabelStudio.init({
+ config: `
+
+
+
+
+
+
+`,
+ data: {
+ url: 'https://htx-pub.s3.amazonaws.com/example.txt',
+ },
+ });
+
+ LabelStudio.waitForObjectsReady();
+ AtSidebar.seeRegions(0);
+
+ for (let k = 0; k < maxUsage; k++) {
+ I.pressKey('1');
+ AtRichText.selectTextByGlobalOffset(1 + 5 * k, 5 * (k + 1));
+ I.pressKey('u');
+ }
+ I.pressKey('1');
+ AtRichText.selectTextByGlobalOffset(1 + 5 * maxUsage, 5 * (maxUsage + 1));
+
+ I.see(`You can't use Label_1 more than ${maxUsage} time(s)`);
+});
+
+Data(maxUsageDataTable).Scenario('Max usages of labels in Paragraphs on region creation', async function({ I, LabelStudio, AtSidebar, AtParagraphs, current }) {
+ const { maxUsage } = current;
+
+ I.amOnPage('/');
+ LabelStudio.setFeatureFlags({
+ fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true,
+ });
+ LabelStudio.init({
+ config: `
+
+
+
+
+
+
+`,
+ data: require('../examples/text-paragraphs').data,
+ });
+
+ LabelStudio.waitForObjectsReady();
+ AtSidebar.seeRegions(0);
+
+ for (let k = 0; k < maxUsage; k++) {
+ I.pressKey('1');
+ AtParagraphs.selectTextByOffset(k + 1, 0, 3);
+ I.pressKey('u');
+ }
+ I.pressKey('1');
+ AtParagraphs.selectTextByOffset(maxUsage + 1, 0, 3);
+
+ I.see(`You can't use Label_1 more than ${maxUsage} time(s)`);
+});
+
+Data(maxUsageDataTable).Scenario('Max usages of labels in Timeseries on region creation', async function({ I, LabelStudio, AtSidebar, AtTimeSeries, current }) {
+ const { maxUsage } = current;
+
+ I.amOnPage('/');
+ LabelStudio.setFeatureFlags({
+ fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true,
+ });
+ LabelStudio.init({
+ config: `
+
+
+
+
+
+
+
+
+
+`,
+ data: require('../examples/data/sample-sin.json'),
+ });
+
+ LabelStudio.waitForObjectsReady();
+ AtSidebar.seeRegions(0);
+ await AtTimeSeries.lookForStage();
+
+ for (let k = 0; k < maxUsage; k++) {
+ I.pressKey('1');
+ AtTimeSeries.drawByDrag(1 + k * 20, 10);
+ I.pressKey('u');
+ }
+ I.pressKey('1');
+ AtTimeSeries.drawByDrag(1 + maxUsage * 20, 10);
+
+ I.see(`You can't use Label_1 more than ${maxUsage} time(s)`);
+});
diff --git a/e2e/tests/outliner.test.js b/e2e/tests/outliner.test.js
index 0bfcee1875..c99c803857 100644
--- a/e2e/tests/outliner.test.js
+++ b/e2e/tests/outliner.test.js
@@ -3,8 +3,7 @@ const { centerOfBbox } = require('./helpers');
Feature('Outliner');
-const IMAGE =
- 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
Scenario('Basic details', async ({ I, LabelStudio, AtOutliner, AtDetails }) => {
const RESULT_LABELS = ['a', 'b', 'c'];
diff --git a/e2e/tests/paragraphs-filter.test.js b/e2e/tests/paragraphs-filter.test.js
index 9ebdbdbea2..7d3f58fc6a 100644
--- a/e2e/tests/paragraphs-filter.test.js
+++ b/e2e/tests/paragraphs-filter.test.js
@@ -1,600 +1,600 @@
-const assert = require('assert');
-const { omitBy } = require('./helpers');
-
-Feature('Paragraphs filter');
-
-const AUDIO = 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/audio/barradeen-emotional.mp3';
-
-const ANNOTATIONS = [
- {
- 'result': [
- {
- 'id':'ryzr4QdL93',
- 'from_name':'ner',
- 'to_name':'text',
- 'source':'$dialogue',
- 'type':'paragraphlabels',
- 'value':{
- 'start':'2',
- 'end':'4',
- 'startOffset':0,
- 'endOffset':134,
- 'paragraphlabels': ['Important Stuff'],
- 'text': 'Uncomfortable silences. Why do we feel its necessary to yak about nonsense in order to be comfortable?I dont know. Thats a good question.Thats when you know you found somebody really special. When you can just shut the door closed a minute, and comfortably share silence.',
- },
- },
- ],
- },
-];
-
-const DATA = {
- audio: AUDIO,
- dialogue: [
- {
- start: 3.1,
- end: 5.6,
- author: 'Mia Wallace',
- text: 'Dont you hate that?',
- },
- {
- start: 4.2,
- duration: 3.1,
- author: 'Vincent Vega:',
- text: 'Hate what?',
- },
- {
- author: 'Mia Wallace:',
- text: 'Uncomfortable silences. Why do we feel its necessary to yak about nonsense in order to be comfortable?',
- },
- {
- start: 90,
- author: 'Vincent Vega:',
- text: 'I dont know. Thats a good question.',
- },
- {
- author: 'Mia Wallace:',
- text:
- 'Thats when you know you found somebody really special. When you can just shut the door closed a minute, and comfortably share silence.',
- },
- ],
-};
-
-const CONFIG = `
-
-
-
-
-
-
-
-`;
-
-const FEATURE_FLAGS = {
- ff_front_dev_2669_paragraph_author_filter_210622_short: true,
- fflag_fix_front_dev_2918_labeling_filtered_paragraphs_250822_short: true,
-};
-
-Scenario('Create two results using excluding a phrase by the filter', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => {
- const params = {
- data: DATA,
- config: CONFIG,
- };
-
- I.amOnPage('/');
-
- LabelStudio.setFeatureFlags(FEATURE_FLAGS);
- LabelStudio.init(params);
- AtSidebar.seeRegions(0);
-
- I.say('Select 2 regions in the consecutive phrases of the one person');
-
- AtLabels.clickLabel('Random talk');
- AtParagraphs.setSelection(
- AtParagraphs.locateText('Hate what?'),
- 5,
- AtParagraphs.locateText('Hate what?'),
- 10,
- );
-
- AtLabels.clickLabel('Random talk');
- AtParagraphs.setSelection(
- AtParagraphs.locateText('I dont know. Thats a good question.'),
- 0,
- AtParagraphs.locateText('I dont know. Thats a good question.'),
- 11,
- );
- AtSidebar.seeRegions(2);
-
- I.say('Take a snapshot');
- const twoActionsResult = LabelStudio.serialize();
-
- I.say('Reset to initial state');
- LabelStudio.init(params);
- AtSidebar.seeRegions(0);
-
- I.say('Filter the phrases by that person.');
- AtParagraphs.clickFilter('Vincent Vega:');
-
- I.say('Try to get the same result in one action');
-
- AtLabels.clickLabel('Random talk');
- AtParagraphs.setSelection(
- AtParagraphs.locateText('Hate what?'),
- 5,
- AtParagraphs.locateText('I dont know. Thats a good question.'),
- 11,
- );
- AtSidebar.seeRegions(2);
-
- I.say('Take a second snapshot');
- const oneActionResult = LabelStudio.serialize();
-
- I.say('The results should be identical');
-
- assert.deepStrictEqual(twoActionsResult, oneActionResult);
-
-});
-
-Scenario('Check different cases ', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => {
- const dialogue = [
- 1,// 1
- 3,// 2
- 1,// 3
- 2,// 4
- 3,// 5
- 1,// 6
- 2,// 7
- 1,// 8
- 3,// 9
- 1,// 10
- ].map((authorId, idx)=>({
- start: idx+1,
- end: idx+2,
- author: `Author ${authorId}`,
- text: `Message ${idx+1}`,
- }));
- const params = {
- config: CONFIG,
- data: {
- audio: AUDIO,
- dialogue,
- },
- };
-
- I.amOnPage('/');
-
- LabelStudio.setFeatureFlags(FEATURE_FLAGS);
- LabelStudio.init(params);
- AtSidebar.seeRegions(0);
-
- I.say('Hide Author 3');
- AtParagraphs.clickFilter('Author 1', 'Author 2');
-
- I.say('Make regions by selecting everything');
- AtLabels.clickLabel('Random talk');
- AtParagraphs.setSelection(
- AtParagraphs.locateText('Message 1'),
- 0,
- AtParagraphs.locateText('Message 10'),
- 10,
- );
-
- I.say('There should be 4 new regions');
- AtSidebar.seeRegions(4);
- {
- const result = await LabelStudio.serialize();
-
- assert.strictEqual(result.length, 4);
-
- assert.deepStrictEqual(omitBy(result[0].value, (v, key)=> key === 'paragraphlabels'), {
- 'start': '0',
- 'end': '0',
- 'startOffset': 0,
- 'endOffset': 9,
- 'text': 'Message 1',
- });
-
- assert.deepStrictEqual(omitBy(result[1].value, (v, key)=> key === 'paragraphlabels'), {
- 'start': '2',
- 'end': '3',
- 'startOffset': 0,
- 'endOffset': 9,
- 'text': 'Message 3\n\nMessage 4',
- });
-
- assert.deepStrictEqual(omitBy(result[2].value, (v, key)=> key === 'paragraphlabels'), {
- 'start': '5',
- 'end': '7',
- 'startOffset': 0,
- 'endOffset': 9,
- 'text': 'Message 6\n\nMessage 7\n\nMessage 8',
- });
-
- assert.deepStrictEqual(omitBy(result[3].value, (v, key)=> key === 'paragraphlabels'), {
- 'start': '9',
- 'end': '9',
- 'startOffset': 0,
- 'endOffset': 10,
- 'text': 'Message 10',
- });
- }
-
- I.say('Test the overlaps of regions #1');
- AtLabels.clickLabel('Important Stuff');
- AtParagraphs.setSelection(
- AtParagraphs.locateText('Message 3'),
- 4,
- AtParagraphs.locateText('Message 8'),
- 4,
- );
- AtSidebar.seeRegions(6);
-
- {
- const result = await LabelStudio.serialize();
-
- assert.deepStrictEqual(omitBy(result[4].value, (v, key)=> key === 'paragraphlabels'), {
- 'start': '2',
- 'end': '3',
- 'startOffset': 4,
- 'endOffset': 9,
- 'text': 'age 3\n\nMessage 4',
- });
-
- assert.deepStrictEqual(omitBy(result[5].value, (v, key)=> key === 'paragraphlabels'), {
- 'start': '5',
- 'end': '7',
- 'startOffset': 0,
- 'endOffset': 4,
- 'text': 'Message 6\n\nMessage 7\n\nMess',
- });
- }
-
- I.say('Test the overlaps of regions #2');
- AtParagraphs.clickFilter('Author 2', 'Author 3');
- AtLabels.clickLabel('Important Stuff');
- AtParagraphs.setSelection(
- AtParagraphs.locateText('age 3'),
- 4,
- AtParagraphs.locateText('age 8'),
- 3,
- );
- AtSidebar.seeRegions(9);
-
- {
- const result = await LabelStudio.serialize();
-
- assert.deepStrictEqual(omitBy(result[6].value, (v, key)=> key === 'paragraphlabels'), {
- 'start': '2',
- 'end': '2',
- 'startOffset': 8,
- 'endOffset': 9,
- 'text': '3',
- });
-
- assert.deepStrictEqual(omitBy(result[7].value, (v, key)=> key === 'paragraphlabels'), {
- 'start': '4',
- 'end': '5',
- 'startOffset': 0,
- 'endOffset': 9,
- 'text': 'Message 5\n\nMessage 6',
- });
-
- assert.deepStrictEqual(omitBy(result[8].value, (v, key)=> key === 'paragraphlabels'), {
- 'start': '7',
- 'end': '7',
- 'startOffset': 0,
- 'endOffset': 7,
- 'text': 'Message',
- });
- }
-});
-
-Scenario(
- 'Check start and end indices do not leak to other lines',
- async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => {
- const dialogue = [
- 1, // 1
- 3, // 2
- 1, // 3
- 2, // 4
- 3, // 5
- 1, // 6
- 2, // 7
- 1, // 8
- 3, // 9
- 1, // 10
- 3, // 11
- 2, // 12
- 3, // 13
- 2, // 14
- ].map((authorId, idx) => ({
- start: idx + 1,
- end: idx + 2,
- author: `Author ${authorId}`,
- text: `Message ${idx + 1}`,
- }));
- const params = {
- config: CONFIG,
- data: {
- audio: AUDIO,
- dialogue,
- },
- };
-
- LabelStudio.setFeatureFlags(FEATURE_FLAGS);
- I.amOnPage('/');
-
- LabelStudio.init(params);
- AtSidebar.seeRegions(0);
-
- I.say(
- 'Test selection from the end of one turn to end of the one below correctly creates a single region with proper start,startOffset,end,endOffset',
- );
- AtLabels.clickLabel('Random talk');
- AtParagraphs.setSelection(
- AtParagraphs.locateText('Message 8'),
- 9,
- AtParagraphs.locateText('Message 9'),
- 9,
- );
- AtSidebar.seeRegions(1);
-
- {
- const result = await LabelStudio.serialize();
-
- assert.deepStrictEqual(
- omitBy(result[0].value, (v, key) => key === 'paragraphlabels'),
- {
- start: '8',
- end: '8',
- startOffset: 0,
- endOffset: 9,
- text: 'Message 9',
- },
- );
- }
-
- I.say(
- 'Test selection from the end of one turn to the very start of another below correctly creates a single region with proper start,startOffset,end,endOffset',
- );
- AtLabels.clickLabel('Random talk');
- AtParagraphs.setSelection(
- AtParagraphs.locateText('Message 8'),
- 9,
- AtParagraphs.locateText('Message 10'),
- 0,
- );
- AtSidebar.seeRegions(2);
-
- {
- const result = await LabelStudio.serialize();
-
- assert.deepStrictEqual(
- omitBy(result[1].value, (v, key) => key === 'paragraphlabels'),
- {
- start: '8',
- end: '8',
- startOffset: 0,
- endOffset: 9,
- text: 'Message 9',
- },
- );
- }
-
- I.say(
- 'Test selection from the end of one turn to end of ones below across collapsed text correctly creates regions with proper start,startOffset,end,endOffset',
- );
- AtParagraphs.clickFilter('Author 2', 'Author 3');
- AtLabels.clickLabel('Important Stuff');
- AtParagraphs.setSelection(
- AtParagraphs.locateText('Message 2'),
- 9,
- AtParagraphs.locateText('Message 8'),
- 9,
- );
- AtSidebar.seeRegions(4);
-
- {
- const result = await LabelStudio.serialize();
-
- assert.deepStrictEqual(
- omitBy(result[2].value, (v, key) => key === 'paragraphlabels'),
- {
- start: '3',
- end: '4',
- startOffset: 0,
- endOffset: 9,
- text: 'Message 4\n\nMessage 5',
- },
- );
- assert.deepStrictEqual(
- omitBy(result[3].value, (v, key) => key === 'paragraphlabels'),
- {
- start: '6',
- end: '6',
- startOffset: 0,
- endOffset: 9,
- text: 'Message 7',
- },
- );
- }
-
- I.say(
- 'Test selection from the end of one turn to very start of ones below across collapsed text correctly creates creates regions with proper start,startOffset,end,endOffset',
- );
- AtLabels.clickLabel('Other');
- AtParagraphs.setSelection(
- AtParagraphs.locateText('Message 2'),
- 9,
- AtParagraphs.locateText('Message 8'),
- 0,
- );
- AtSidebar.seeRegions(6);
-
- {
- const result = await LabelStudio.serialize();
-
- assert.deepStrictEqual(
- omitBy(result[4].value, (v, key) => key === 'paragraphlabels'),
- {
- start: '3',
- end: '4',
- startOffset: 0,
- endOffset: 9,
- text: 'Message 4\n\nMessage 5',
- },
- );
- assert.deepStrictEqual(
- omitBy(result[5].value, (v, key) => key === 'paragraphlabels'),
- {
- start: '6',
- end: '6',
- startOffset: 0,
- endOffset: 9,
- text: 'Message 7',
- },
- );
- }
-
- I.say(
- 'Test selection from the end of Message 11 to the start of Message 14 to get region over Message 12 and Message 13',
- );
- AtLabels.clickLabel('Random talk');
- AtParagraphs.setSelection(
- AtParagraphs.locateText('Message 11'),
- 10,
- AtParagraphs.locateText('Message 14'),
- 0,
- );
- AtSidebar.seeRegions(7);
-
- {
- const result = await LabelStudio.serialize();
-
- assert.deepStrictEqual(
- omitBy(result[6].value, (v, key) => key === 'paragraphlabels'),
- {
- start: '11',
- end: '12',
- startOffset: 0,
- endOffset: 10,
- text: 'Message 12\n\nMessage 13',
- },
- );
- }
- },
-);
-
-Scenario('Selecting the end character on a paragraph phrase to the very start of other phrases includes all selected phrases', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => {
- const params = {
- data: DATA,
- config: CONFIG,
- };
-
- I.amOnPage('/');
-
- LabelStudio.setFeatureFlags(FEATURE_FLAGS);
- LabelStudio.init(params);
- AtSidebar.seeRegions(0);
-
- I.say('Select 2 regions in the consecutive phrases');
-
- AtLabels.clickLabel('Random talk');
- AtParagraphs.setSelection(
- AtParagraphs.locateText('Dont you hate that?'),
- 18,
- AtParagraphs.locateText('Uncomfortable silences. Why do we feel its necessary to yak about nonsense in order to be comfortable?'),
- 0,
- );
-
- AtSidebar.seeRegions(1);
-
- const result = await LabelStudio.serialize();
-
- assert.deepStrictEqual(
- omitBy(result[0].value, (v, key) => key === 'paragraphlabels'),
- {
- start: '0',
- end: '1',
- startOffset: 18,
- endOffset: 10,
- text: '?\n\nHate what?',
- },
- );
-});
-
-Scenario('Selecting the end character on a paragraph phrase to the very start of other phrases includes all selected phrases except the very last one', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => {
- const params = {
- data: {
- ...DATA,
- dialogue: DATA.dialogue.map(d => [d, { ...d, text: `${d.text}2` }]).flat(),
- },
- config: CONFIG,
- };
-
- I.amOnPage('/');
-
- LabelStudio.setFeatureFlags(FEATURE_FLAGS);
- LabelStudio.init(params);
- AtSidebar.seeRegions(0);
-
-
- I.say('Select 2 regions in the consecutive phrases of the one person');
- AtParagraphs.clickFilter('Vincent Vega');
- AtLabels.clickLabel('Random talk');
- AtParagraphs.setSelection(
- AtParagraphs.locateText('Hate what?2'),
- 10,
- AtParagraphs.locateText('I dont know. Thats a good question.2'),
- 0,
- );
-
- AtSidebar.seeRegions(2);
-
- const result = await LabelStudio.serialize();
-
- assert.deepStrictEqual(
- omitBy(result[0].value, (v, key) => key === 'paragraphlabels'),
- {
- start: '3',
- end: '3',
- startOffset: 10,
- endOffset: 11,
- text: '2',
- },
- );
- assert.deepStrictEqual(
- omitBy(result[1].value, (v, key) => key === 'paragraphlabels'),
- {
- start: '6',
- end: '6',
- startOffset: 0,
- endOffset: 35,
- text: 'I dont know. Thats a good question.',
- },
- );
-});
-
-Scenario('Initializing a paragraph region range should not include author names in text', async ({ I, LabelStudio, AtSidebar }) => {
- const params = {
- data: DATA,
- annotations: ANNOTATIONS,
- config: CONFIG,
- };
-
- I.amOnPage('/');
- LabelStudio.setFeatureFlags(FEATURE_FLAGS);
-
- const [{ result : [region] }] = ANNOTATIONS;
- const { paragraphlabels: _paragraphlabels, ...value } = region.value;
-
- LabelStudio.init(params);
- AtSidebar.seeRegions(1);
-
- const result = await LabelStudio.serialize();
-
- assert.deepStrictEqual(
- omitBy(result[0].value, (v, key) => key === 'paragraphlabels'),
- value,
- );
-});
+const assert = require('assert');
+const { omitBy } = require('./helpers');
+
+Feature('Paragraphs filter');
+
+const AUDIO = 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/audio/barradeen-emotional.mp3';
+
+const ANNOTATIONS = [
+ {
+ 'result': [
+ {
+ 'id': 'ryzr4QdL93',
+ 'from_name': 'ner',
+ 'to_name': 'text',
+ 'source': '$dialogue',
+ 'type': 'paragraphlabels',
+ 'value': {
+ 'start': '2',
+ 'end': '4',
+ 'startOffset': 0,
+ 'endOffset': 134,
+ 'paragraphlabels': ['Important Stuff'],
+ 'text': 'Uncomfortable silences. Why do we feel its necessary to yak about nonsense in order to be comfortable?I dont know. Thats a good question.Thats when you know you found somebody really special. When you can just shut the door closed a minute, and comfortably share silence.',
+ },
+ },
+ ],
+ },
+];
+
+const DATA = {
+ audio: AUDIO,
+ dialogue: [
+ {
+ start: 3.1,
+ end: 5.6,
+ author: 'Mia Wallace',
+ text: 'Dont you hate that?',
+ },
+ {
+ start: 4.2,
+ duration: 3.1,
+ author: 'Vincent Vega:',
+ text: 'Hate what?',
+ },
+ {
+ author: 'Mia Wallace:',
+ text: 'Uncomfortable silences. Why do we feel its necessary to yak about nonsense in order to be comfortable?',
+ },
+ {
+ start: 90,
+ author: 'Vincent Vega:',
+ text: 'I dont know. Thats a good question.',
+ },
+ {
+ author: 'Mia Wallace:',
+ text:
+ 'Thats when you know you found somebody really special. When you can just shut the door closed a minute, and comfortably share silence.',
+ },
+ ],
+};
+
+const CONFIG = `
+
+
+
+
+
+
+
+`;
+
+const FEATURE_FLAGS = {
+ ff_front_dev_2669_paragraph_author_filter_210622_short: true,
+ fflag_fix_front_dev_2918_labeling_filtered_paragraphs_250822_short: true,
+};
+
+Scenario('Create two results using excluding a phrase by the filter', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => {
+ const params = {
+ data: DATA,
+ config: CONFIG,
+ };
+
+ I.amOnPage('/');
+
+ LabelStudio.setFeatureFlags(FEATURE_FLAGS);
+ LabelStudio.init(params);
+ AtSidebar.seeRegions(0);
+
+ I.say('Select 2 regions in the consecutive phrases of the one person');
+
+ AtLabels.clickLabel('Random talk');
+ AtParagraphs.setSelection(
+ AtParagraphs.locateText('Hate what?'),
+ 5,
+ AtParagraphs.locateText('Hate what?'),
+ 10,
+ );
+
+ AtLabels.clickLabel('Random talk');
+ AtParagraphs.setSelection(
+ AtParagraphs.locateText('I dont know. Thats a good question.'),
+ 0,
+ AtParagraphs.locateText('I dont know. Thats a good question.'),
+ 11,
+ );
+ AtSidebar.seeRegions(2);
+
+ I.say('Take a snapshot');
+ const twoActionsResult = LabelStudio.serialize();
+
+ I.say('Reset to initial state');
+ LabelStudio.init(params);
+ AtSidebar.seeRegions(0);
+
+ I.say('Filter the phrases by that person.');
+ AtParagraphs.clickFilter('Vincent Vega:');
+
+ I.say('Try to get the same result in one action');
+
+ AtLabels.clickLabel('Random talk');
+ AtParagraphs.setSelection(
+ AtParagraphs.locateText('Hate what?'),
+ 5,
+ AtParagraphs.locateText('I dont know. Thats a good question.'),
+ 11,
+ );
+ AtSidebar.seeRegions(2);
+
+ I.say('Take a second snapshot');
+ const oneActionResult = LabelStudio.serialize();
+
+ I.say('The results should be identical');
+
+ assert.deepStrictEqual(twoActionsResult, oneActionResult);
+
+});
+
+Scenario('Check different cases ', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => {
+ const dialogue = [
+ 1,// 1
+ 3,// 2
+ 1,// 3
+ 2,// 4
+ 3,// 5
+ 1,// 6
+ 2,// 7
+ 1,// 8
+ 3,// 9
+ 1,// 10
+ ].map((authorId, idx) => ({
+ start: idx + 1,
+ end: idx + 2,
+ author: `Author ${authorId}`,
+ text: `Message ${idx + 1}`,
+ }));
+ const params = {
+ config: CONFIG,
+ data: {
+ audio: AUDIO,
+ dialogue,
+ },
+ };
+
+ I.amOnPage('/');
+
+ LabelStudio.setFeatureFlags(FEATURE_FLAGS);
+ LabelStudio.init(params);
+ AtSidebar.seeRegions(0);
+
+ I.say('Hide Author 3');
+ AtParagraphs.clickFilter('Author 1', 'Author 2');
+
+ I.say('Make regions by selecting everything');
+ AtLabels.clickLabel('Random talk');
+ AtParagraphs.setSelection(
+ AtParagraphs.locateText('Message 1'),
+ 0,
+ AtParagraphs.locateText('Message 10'),
+ 10,
+ );
+
+ I.say('There should be 4 new regions');
+ AtSidebar.seeRegions(4);
+ {
+ const result = await LabelStudio.serialize();
+
+ assert.strictEqual(result.length, 4);
+
+ assert.deepStrictEqual(omitBy(result[0].value, (v, key) => key === 'paragraphlabels'), {
+ 'start': '0',
+ 'end': '0',
+ 'startOffset': 0,
+ 'endOffset': 9,
+ 'text': 'Message 1',
+ });
+
+ assert.deepStrictEqual(omitBy(result[1].value, (v, key) => key === 'paragraphlabels'), {
+ 'start': '2',
+ 'end': '3',
+ 'startOffset': 0,
+ 'endOffset': 9,
+ 'text': 'Message 3\n\nMessage 4',
+ });
+
+ assert.deepStrictEqual(omitBy(result[2].value, (v, key) => key === 'paragraphlabels'), {
+ 'start': '5',
+ 'end': '7',
+ 'startOffset': 0,
+ 'endOffset': 9,
+ 'text': 'Message 6\n\nMessage 7\n\nMessage 8',
+ });
+
+ assert.deepStrictEqual(omitBy(result[3].value, (v, key) => key === 'paragraphlabels'), {
+ 'start': '9',
+ 'end': '9',
+ 'startOffset': 0,
+ 'endOffset': 10,
+ 'text': 'Message 10',
+ });
+ }
+
+ I.say('Test the overlaps of regions #1');
+ AtLabels.clickLabel('Important Stuff');
+ AtParagraphs.setSelection(
+ AtParagraphs.locateText('Message 3'),
+ 4,
+ AtParagraphs.locateText('Message 8'),
+ 4,
+ );
+ AtSidebar.seeRegions(6);
+
+ {
+ const result = await LabelStudio.serialize();
+
+ assert.deepStrictEqual(omitBy(result[4].value, (v, key) => key === 'paragraphlabels'), {
+ 'start': '2',
+ 'end': '3',
+ 'startOffset': 4,
+ 'endOffset': 9,
+ 'text': 'age 3\n\nMessage 4',
+ });
+
+ assert.deepStrictEqual(omitBy(result[5].value, (v, key) => key === 'paragraphlabels'), {
+ 'start': '5',
+ 'end': '7',
+ 'startOffset': 0,
+ 'endOffset': 4,
+ 'text': 'Message 6\n\nMessage 7\n\nMess',
+ });
+ }
+
+ I.say('Test the overlaps of regions #2');
+ AtParagraphs.clickFilter('Author 2', 'Author 3');
+ AtLabels.clickLabel('Important Stuff');
+ AtParagraphs.setSelection(
+ AtParagraphs.locateText('age 3'),
+ 4,
+ AtParagraphs.locateText('age 8'),
+ 3,
+ );
+ AtSidebar.seeRegions(9);
+
+ {
+ const result = await LabelStudio.serialize();
+
+ assert.deepStrictEqual(omitBy(result[6].value, (v, key) => key === 'paragraphlabels'), {
+ 'start': '2',
+ 'end': '2',
+ 'startOffset': 8,
+ 'endOffset': 9,
+ 'text': '3',
+ });
+
+ assert.deepStrictEqual(omitBy(result[7].value, (v, key) => key === 'paragraphlabels'), {
+ 'start': '4',
+ 'end': '5',
+ 'startOffset': 0,
+ 'endOffset': 9,
+ 'text': 'Message 5\n\nMessage 6',
+ });
+
+ assert.deepStrictEqual(omitBy(result[8].value, (v, key) => key === 'paragraphlabels'), {
+ 'start': '7',
+ 'end': '7',
+ 'startOffset': 0,
+ 'endOffset': 7,
+ 'text': 'Message',
+ });
+ }
+});
+
+Scenario(
+ 'Check start and end indices do not leak to other lines',
+ async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => {
+ const dialogue = [
+ 1, // 1
+ 3, // 2
+ 1, // 3
+ 2, // 4
+ 3, // 5
+ 1, // 6
+ 2, // 7
+ 1, // 8
+ 3, // 9
+ 1, // 10
+ 3, // 11
+ 2, // 12
+ 3, // 13
+ 2, // 14
+ ].map((authorId, idx) => ({
+ start: idx + 1,
+ end: idx + 2,
+ author: `Author ${authorId}`,
+ text: `Message ${idx + 1}`,
+ }));
+ const params = {
+ config: CONFIG,
+ data: {
+ audio: AUDIO,
+ dialogue,
+ },
+ };
+
+ LabelStudio.setFeatureFlags(FEATURE_FLAGS);
+ I.amOnPage('/');
+
+ LabelStudio.init(params);
+ AtSidebar.seeRegions(0);
+
+ I.say(
+ 'Test selection from the end of one turn to end of the one below correctly creates a single region with proper start,startOffset,end,endOffset',
+ );
+ AtLabels.clickLabel('Random talk');
+ AtParagraphs.setSelection(
+ AtParagraphs.locateText('Message 8'),
+ 9,
+ AtParagraphs.locateText('Message 9'),
+ 9,
+ );
+ AtSidebar.seeRegions(1);
+
+ {
+ const result = await LabelStudio.serialize();
+
+ assert.deepStrictEqual(
+ omitBy(result[0].value, (v, key) => key === 'paragraphlabels'),
+ {
+ start: '8',
+ end: '8',
+ startOffset: 0,
+ endOffset: 9,
+ text: 'Message 9',
+ },
+ );
+ }
+
+ I.say(
+ 'Test selection from the end of one turn to the very start of another below correctly creates a single region with proper start,startOffset,end,endOffset',
+ );
+ AtLabels.clickLabel('Random talk');
+ AtParagraphs.setSelection(
+ AtParagraphs.locateText('Message 8'),
+ 9,
+ AtParagraphs.locateText('Message 10'),
+ 0,
+ );
+ AtSidebar.seeRegions(2);
+
+ {
+ const result = await LabelStudio.serialize();
+
+ assert.deepStrictEqual(
+ omitBy(result[1].value, (v, key) => key === 'paragraphlabels'),
+ {
+ start: '8',
+ end: '8',
+ startOffset: 0,
+ endOffset: 9,
+ text: 'Message 9',
+ },
+ );
+ }
+
+ I.say(
+ 'Test selection from the end of one turn to end of ones below across collapsed text correctly creates regions with proper start,startOffset,end,endOffset',
+ );
+ AtParagraphs.clickFilter('Author 2', 'Author 3');
+ AtLabels.clickLabel('Important Stuff');
+ AtParagraphs.setSelection(
+ AtParagraphs.locateText('Message 2'),
+ 9,
+ AtParagraphs.locateText('Message 8'),
+ 9,
+ );
+ AtSidebar.seeRegions(4);
+
+ {
+ const result = await LabelStudio.serialize();
+
+ assert.deepStrictEqual(
+ omitBy(result[2].value, (v, key) => key === 'paragraphlabels'),
+ {
+ start: '3',
+ end: '4',
+ startOffset: 0,
+ endOffset: 9,
+ text: 'Message 4\n\nMessage 5',
+ },
+ );
+ assert.deepStrictEqual(
+ omitBy(result[3].value, (v, key) => key === 'paragraphlabels'),
+ {
+ start: '6',
+ end: '6',
+ startOffset: 0,
+ endOffset: 9,
+ text: 'Message 7',
+ },
+ );
+ }
+
+ I.say(
+ 'Test selection from the end of one turn to very start of ones below across collapsed text correctly creates creates regions with proper start,startOffset,end,endOffset',
+ );
+ AtLabels.clickLabel('Other');
+ AtParagraphs.setSelection(
+ AtParagraphs.locateText('Message 2'),
+ 9,
+ AtParagraphs.locateText('Message 8'),
+ 0,
+ );
+ AtSidebar.seeRegions(6);
+
+ {
+ const result = await LabelStudio.serialize();
+
+ assert.deepStrictEqual(
+ omitBy(result[4].value, (v, key) => key === 'paragraphlabels'),
+ {
+ start: '3',
+ end: '4',
+ startOffset: 0,
+ endOffset: 9,
+ text: 'Message 4\n\nMessage 5',
+ },
+ );
+ assert.deepStrictEqual(
+ omitBy(result[5].value, (v, key) => key === 'paragraphlabels'),
+ {
+ start: '6',
+ end: '6',
+ startOffset: 0,
+ endOffset: 9,
+ text: 'Message 7',
+ },
+ );
+ }
+
+ I.say(
+ 'Test selection from the end of Message 11 to the start of Message 14 to get region over Message 12 and Message 13',
+ );
+ AtLabels.clickLabel('Random talk');
+ AtParagraphs.setSelection(
+ AtParagraphs.locateText('Message 11'),
+ 10,
+ AtParagraphs.locateText('Message 14'),
+ 0,
+ );
+ AtSidebar.seeRegions(7);
+
+ {
+ const result = await LabelStudio.serialize();
+
+ assert.deepStrictEqual(
+ omitBy(result[6].value, (v, key) => key === 'paragraphlabels'),
+ {
+ start: '11',
+ end: '12',
+ startOffset: 0,
+ endOffset: 10,
+ text: 'Message 12\n\nMessage 13',
+ },
+ );
+ }
+ },
+);
+
+Scenario('Selecting the end character on a paragraph phrase to the very start of other phrases includes all selected phrases', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => {
+ const params = {
+ data: DATA,
+ config: CONFIG,
+ };
+
+ I.amOnPage('/');
+
+ LabelStudio.setFeatureFlags(FEATURE_FLAGS);
+ LabelStudio.init(params);
+ AtSidebar.seeRegions(0);
+
+ I.say('Select 2 regions in the consecutive phrases');
+
+ AtLabels.clickLabel('Random talk');
+ AtParagraphs.setSelection(
+ AtParagraphs.locateText('Dont you hate that?'),
+ 18,
+ AtParagraphs.locateText('Uncomfortable silences. Why do we feel its necessary to yak about nonsense in order to be comfortable?'),
+ 0,
+ );
+
+ AtSidebar.seeRegions(1);
+
+ const result = await LabelStudio.serialize();
+
+ assert.deepStrictEqual(
+ omitBy(result[0].value, (v, key) => key === 'paragraphlabels'),
+ {
+ start: '0',
+ end: '1',
+ startOffset: 18,
+ endOffset: 10,
+ text: '?\n\nHate what?',
+ },
+ );
+});
+
+Scenario('Selecting the end character on a paragraph phrase to the very start of other phrases includes all selected phrases except the very last one', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => {
+ const params = {
+ data: {
+ ...DATA,
+ dialogue: DATA.dialogue.map(d => [d, { ...d, text: `${d.text}2` }]).flat(),
+ },
+ config: CONFIG,
+ };
+
+ I.amOnPage('/');
+
+ LabelStudio.setFeatureFlags(FEATURE_FLAGS);
+ LabelStudio.init(params);
+ AtSidebar.seeRegions(0);
+
+
+ I.say('Select 2 regions in the consecutive phrases of the one person');
+ AtParagraphs.clickFilter('Vincent Vega');
+ AtLabels.clickLabel('Random talk');
+ AtParagraphs.setSelection(
+ AtParagraphs.locateText('Hate what?2'),
+ 10,
+ AtParagraphs.locateText('I dont know. Thats a good question.2'),
+ 0,
+ );
+
+ AtSidebar.seeRegions(2);
+
+ const result = await LabelStudio.serialize();
+
+ assert.deepStrictEqual(
+ omitBy(result[0].value, (v, key) => key === 'paragraphlabels'),
+ {
+ start: '3',
+ end: '3',
+ startOffset: 10,
+ endOffset: 11,
+ text: '2',
+ },
+ );
+ assert.deepStrictEqual(
+ omitBy(result[1].value, (v, key) => key === 'paragraphlabels'),
+ {
+ start: '6',
+ end: '6',
+ startOffset: 0,
+ endOffset: 35,
+ text: 'I dont know. Thats a good question.',
+ },
+ );
+});
+
+Scenario('Initializing a paragraph region range should not include author names in text', async ({ I, LabelStudio, AtSidebar }) => {
+ const params = {
+ data: DATA,
+ annotations: ANNOTATIONS,
+ config: CONFIG,
+ };
+
+ I.amOnPage('/');
+ LabelStudio.setFeatureFlags(FEATURE_FLAGS);
+
+ const [{ result: [region] }] = ANNOTATIONS;
+ const { paragraphlabels: _paragraphlabels, ...value } = region.value;
+
+ LabelStudio.init(params);
+ AtSidebar.seeRegions(1);
+
+ const result = await LabelStudio.serialize();
+
+ assert.deepStrictEqual(
+ omitBy(result[0].value, (v, key) => key === 'paragraphlabels'),
+ value,
+ );
+});
diff --git a/e2e/tests/regression-tests/brush-relations.test.js b/e2e/tests/regression-tests/brush-relations.test.js
index 5700346ee4..4b748d9a81 100644
--- a/e2e/tests/regression-tests/brush-relations.test.js
+++ b/e2e/tests/regression-tests/brush-relations.test.js
@@ -1,103 +1,103 @@
-Feature('Brush relations').tag('@regress');
-
-const IMAGE = 'https://user.fm/files/v2-901310d5cb3fa90e0616ca10590bacb3/spacexmoon-800x501.jpg';
-
-const config = `
-
-
-
-
- `;
-
-function getPointAtSpiral(t, v , w) {
- return { x: v * t * Math.cos(w * t), y: v * t * Math.sin(w * t) };
-}
-
-function generateSpiralPoints(x0, y0, R, v , w) {
- let t = 1, x, y;
- const points = [];
-
- do {
- ({ x, y } = getPointAtSpiral(t++, v, w));
- points.push([x + x0, y + y0]);
- } while (x ** 2 + y ** 2 < R ** 2);
- return points;
-}
-
-xScenario('Brush relations shouldn\'t crash everything', async ({ I, LabelStudio, AtImageView, AtSidebar }) => {
- const params = {
- config,
- data: { image: IMAGE },
- };
-
- I.amOnPage('/');
- LabelStudio.init(params);
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
- const regionsCentralPoints = [];
-
- // create 4 brush regions
- for (let i = 0; i < 4; i++) {
- // find start position
- const x = canvasSize.width / 4 * ((i % 2) * 2 + 1);
- const y = canvasSize.height / 4 * ((Math.floor(i / 2) % 2) * 2 + 1);
- // generate points in a spiral
- const points = generateSpiralPoints(x, y, Math.min(canvasSize.width / 6, canvasSize.height / 6), .4, Math.PI / 18);
-
- // select the brush label
- I.pressKey('1');
- // draw a brush region
- AtImageView.drawThroughPoints(points);
- AtSidebar.seeRegions(i+1);
- // unselect the region
- I.pressKey('u');
- // save the central point
- regionsCentralPoints.push({ x, y });
- }
-
- // Check that we can create a relation between the brush regions
- {
- // switch to the move tool for easy region selecting
- I.pressKey('v');
- // select the first region
- AtImageView.clickAt(regionsCentralPoints[0].x, regionsCentralPoints[0].y);
- // create relation to the second region
- I.pressKey(['alt', 'r']);
- AtImageView.clickAt(regionsCentralPoints[1].x, regionsCentralPoints[1].y);
- // check that the relation has been created
- AtSidebar.seeRelations(1);
- }
-
- // Check that relations work fine on a brush restoration (from rle)
- {
- // export annotation
- const annotation = await LabelStudio.serialize();
-
- // reload LS with that datalabel studio logo
- LabelStudio.init({
- ...params,
- annotations: [{ id: 'imported', result: annotation }],
- });
-
- AtImageView.waitForImage();
- // Check that relation still exist
- AtSidebar.seeRelations(1);
-
- // Try to create new relation with restored regions
- {
- // switch to the move tool for easy region selecting
- I.pressKey('v');
- // select the third region
- AtImageView.clickAt(regionsCentralPoints[2].x, regionsCentralPoints[2].y);
- // create relation to the fourth region
- I.pressKey(['alt', 'r']);
- AtImageView.clickAt(regionsCentralPoints[3].x, regionsCentralPoints[3].y);
- // check that the relation has been created
- AtSidebar.seeRelations(2);
- }
-
- /// The potential errors should be caught by `errorsCollector` plugin
- }
-});
+Feature('Brush relations').tag('@regress');
+
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
+
+const config = `
+
+
+
+
+ `;
+
+function getPointAtSpiral(t, v , w) {
+ return { x: v * t * Math.cos(w * t), y: v * t * Math.sin(w * t) };
+}
+
+function generateSpiralPoints(x0, y0, R, v , w) {
+ let t = 1, x, y;
+ const points = [];
+
+ do {
+ ({ x, y } = getPointAtSpiral(t++, v, w));
+ points.push([x + x0, y + y0]);
+ } while (x ** 2 + y ** 2 < R ** 2);
+ return points;
+}
+
+Scenario('Brush relations shouldn\'t crash everything', async ({ I, LabelStudio, AtImageView, AtSidebar }) => {
+ const params = {
+ config,
+ data: { image: IMAGE },
+ };
+
+ I.amOnPage('/');
+ LabelStudio.init(params);
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+ const regionsCentralPoints = [];
+
+ // create 4 brush regions
+ for (let i = 0; i < 4; i++) {
+ // find start position
+ const x = canvasSize.width / 4 * ((i % 2) * 2 + 1);
+ const y = canvasSize.height / 4 * ((Math.floor(i / 2) % 2) * 2 + 1);
+ // generate points in a spiral
+ const points = generateSpiralPoints(x, y, Math.min(canvasSize.width / 6, canvasSize.height / 6), .4, Math.PI / 18);
+
+ // select the brush label
+ I.pressKey('1');
+ // draw a brush region
+ AtImageView.drawThroughPoints(points);
+ AtSidebar.seeRegions(i + 1);
+ // unselect the region
+ I.pressKey('u');
+ // save the central point
+ regionsCentralPoints.push({ x, y });
+ }
+
+ // Check that we can create a relation between the brush regions
+ {
+ // switch to the move tool for easy region selecting
+ I.pressKey('v');
+ // select the first region
+ AtImageView.clickAt(regionsCentralPoints[0].x, regionsCentralPoints[0].y);
+ // create relation to the second region
+ I.pressKey(['alt', 'r']);
+ AtImageView.clickAt(regionsCentralPoints[1].x, regionsCentralPoints[1].y);
+ // check that the relation has been created
+ AtSidebar.seeRelations(1);
+ }
+
+ // Check that relations work fine on a brush restoration (from rle)
+ {
+ // export annotation
+ const annotation = await LabelStudio.serialize();
+
+ // reload LS with that datalabel studio logo
+ LabelStudio.init({
+ ...params,
+ annotations: [{ id: 'imported', result: annotation }],
+ });
+
+ AtImageView.waitForImage();
+ // Check that relation still exist
+ AtSidebar.seeRelations(1);
+
+ // Try to create new relation with restored regions
+ {
+ // switch to the move tool for easy region selecting
+ I.pressKey('v');
+ // select the third region
+ AtImageView.clickAt(regionsCentralPoints[2].x, regionsCentralPoints[2].y);
+ // create relation to the fourth region
+ I.pressKey(['alt', 'r']);
+ AtImageView.clickAt(regionsCentralPoints[3].x, regionsCentralPoints[3].y);
+ // check that the relation has been created
+ AtSidebar.seeRelations(2);
+ }
+
+ /// The potential errors should be caught by `errorsCollector` plugin
+ }
+});
diff --git a/e2e/tests/regression-tests/dynamic-choices.test.js b/e2e/tests/regression-tests/dynamic-choices.test.js
index 53e3e9ad88..28020b51ce 100644
--- a/e2e/tests/regression-tests/dynamic-choices.test.js
+++ b/e2e/tests/regression-tests/dynamic-choices.test.js
@@ -1,80 +1,80 @@
-const assert = require('assert');
-
-Feature('Dynamic choices').tag('@regress');
-
-Scenario('Hotkeys for dynamic choices', async ({ I, LabelStudio })=>{
- const params = {
- config: `
-
-
-
-
-
-`,
- data: {
- text: 'Some text',
- choices: [
- {
- value: 'Header 1',
- children: [
- {
- value: 'Option 1.1',
- },
- {
- value: 'Option 1.2',
- },
- ],
- },
- {
- value: 'Header 2',
- children: [
- {
- value: 'Option 2.1',
- },
- {
- value: 'Option 2.2',
- },
- {
- value: 'Option 2.3',
- hotkey: 'q',
- },
- ],
- },
- ],
- },
- annotations: [{
- id: 'test',
- result: [],
- }],
- };
-
- I.amOnPage('/');
-
- LabelStudio.init(params);
-
- I.see('Header 1');
- I.see('Option 1.1');
- I.see('Header 2');
- I.see('Option 2.2');
-
- I.say('Select some choices by pressing hotkeys');
-
- I.pressKey('1');
- I.pressKey('q');
- I.pressKey('s');
-
- I.say('Check the result');
-
- I.seeElement('.ant-checkbox-checked [name=\'Header 1\']');
- I.seeElement('.ant-checkbox-indeterminate [name=\'Header 2\']');
- I.seeElement('.ant-checkbox-checked [name=\'Option 1.1\']');
- I.seeElement('.ant-checkbox-checked [name=\'Option 1.2\']');
- I.seeElement('.ant-checkbox-checked [name=\'Option 2.3\']');
- I.seeElement('.ant-checkbox-checked [name=\'Static option\']');
-
- const result = await LabelStudio.serialize();
-
- assert.deepStrictEqual(result.length, 1);
- assert.deepStrictEqual(result[0].value.choices, [['Static option'], ['Header 1'], ['Header 1', 'Option 1.1'], ['Header 1', 'Option 1.2'], ['Header 2', 'Option 2.3']]);
-
-});
+const assert = require('assert');
+
+Feature('Dynamic choices').tag('@regress');
+
+Scenario('Hotkeys for dynamic choices', async ({ I, LabelStudio }) => {
+ const params = {
+ config: `
+
+
+
+
+
+`,
+ data: {
+ text: 'Some text',
+ choices: [
+ {
+ value: 'Header 1',
+ children: [
+ {
+ value: 'Option 1.1',
+ },
+ {
+ value: 'Option 1.2',
+ },
+ ],
+ },
+ {
+ value: 'Header 2',
+ children: [
+ {
+ value: 'Option 2.1',
+ },
+ {
+ value: 'Option 2.2',
+ },
+ {
+ value: 'Option 2.3',
+ hotkey: 'q',
+ },
+ ],
+ },
+ ],
+ },
+ annotations: [{
+ id: 'test',
+ result: [],
+ }],
+ };
+
+ I.amOnPage('/');
+
+ LabelStudio.init(params);
+
+ I.see('Header 1');
+ I.see('Option 1.1');
+ I.see('Header 2');
+ I.see('Option 2.2');
+
+ I.say('Select some choices by pressing hotkeys');
+
+ I.pressKey('1');
+ I.pressKey('q');
+ I.pressKey('s');
+
+ I.say('Check the result');
+
+ I.seeElement('.ant-checkbox-checked [name=\'Header 1\']');
+ I.seeElement('.ant-checkbox-indeterminate [name=\'Header 2\']');
+ I.seeElement('.ant-checkbox-checked [name=\'Option 1.1\']');
+ I.seeElement('.ant-checkbox-checked [name=\'Option 1.2\']');
+ I.seeElement('.ant-checkbox-checked [name=\'Option 2.3\']');
+ I.seeElement('.ant-checkbox-checked [name=\'Static option\']');
+
+ const result = await LabelStudio.serialize();
+
+ assert.deepStrictEqual(result.length, 1);
+ assert.deepStrictEqual(result[0].value.choices, [['Static option'], ['Header 1'], ['Header 1', 'Option 1.1'], ['Header 1', 'Option 1.2'], ['Header 2', 'Option 2.3']]);
+
+});
diff --git a/e2e/tests/regression-tests/image-ctrl-drawing.test.js b/e2e/tests/regression-tests/image-ctrl-drawing.test.js
index 01e5fc3f35..bf66d8c603 100644
--- a/e2e/tests/regression-tests/image-ctrl-drawing.test.js
+++ b/e2e/tests/regression-tests/image-ctrl-drawing.test.js
@@ -3,8 +3,7 @@ const Asserts = require('../../utils/asserts');
Feature('Creating regions over other regions').tag('@regress');
-const IMAGE =
- 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
const BLUEVIOLET = {
color: '#8A2BE2',
diff --git a/e2e/tests/regression-tests/image-draw-undo.test.js b/e2e/tests/regression-tests/image-draw-undo.test.js
index 96dad595a7..69d44bb64a 100644
--- a/e2e/tests/regression-tests/image-draw-undo.test.js
+++ b/e2e/tests/regression-tests/image-draw-undo.test.js
@@ -1,7 +1,6 @@
Feature('Undoing drawing in one step').tag('@regress');
-const IMAGE =
- 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
const BLUEVIOLET = {
color: '#8A2BE2',
diff --git a/e2e/tests/regression-tests/image-width.test.js b/e2e/tests/regression-tests/image-width.test.js
index 51258f5b28..c19eca4258 100644
--- a/e2e/tests/regression-tests/image-width.test.js
+++ b/e2e/tests/regression-tests/image-width.test.js
@@ -1,44 +1,44 @@
-const assert = require('assert');
-
-Feature('Image width parameter').tag('@regress');
-
-const IMAGE = 'https://user.fm/files/v2-901310d5cb3fa90e0616ca10590bacb3/spacexmoon-800x501.jpg';
-
-const config = `
-
-
-
- `;
-
-xScenario('Setting width 50% shouldn\'t break canvas size on resize of working area', async ({ I, LabelStudio, AtImageView, AtSidebar }) => {
- const params = {
- config,
- data: { image: IMAGE },
- };
-
- I.amOnPage('/');
- LabelStudio.init(params);
- AtImageView.waitForImage();
- AtSidebar.seeRegions(0);
- await AtImageView.lookForStage();
- let canvasSize = await AtImageView.getCanvasSize();
- let imageSize = await AtImageView.getImageSize();
-
- // The sizes of the canvas and image element should be equal (or almost equal)
- assert.strictEqual(Math.ceil(imageSize.width), Math.ceil(canvasSize.width));
- assert.strictEqual(Math.ceil(imageSize.height), Math.ceil(canvasSize.height));
-
- // Create full-size region
- // This step is just for visual identification of the bug
- AtImageView.drawByDrag(5, 5, canvasSize.width - 10, canvasSize.height - 10);
-
- I.resizeWindow(800, 900);
- I.resizeWindow(1200, 900);
-
- canvasSize = await AtImageView.getCanvasSize();
- imageSize = await AtImageView.getImageSize();
-
- // The sizes should still be equal (or almost equal)
- assert.strictEqual(Math.ceil(imageSize.width), Math.ceil(canvasSize.width));
- assert.strictEqual(Math.ceil(imageSize.height), Math.ceil(canvasSize.height));
-});
+const assert = require('assert');
+
+Feature('Image width parameter').tag('@regress');
+
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
+
+const config = `
+
+
+
+ `;
+
+Scenario('Setting width 50% shouldn\'t break canvas size on resize of working area', async ({ I, LabelStudio, AtImageView, AtSidebar }) => {
+ const params = {
+ config,
+ data: { image: IMAGE },
+ };
+
+ I.amOnPage('/');
+ LabelStudio.init(params);
+ AtImageView.waitForImage();
+ AtSidebar.seeRegions(0);
+ await AtImageView.lookForStage();
+ let canvasSize = await AtImageView.getCanvasSize();
+ let imageSize = await AtImageView.getImageSize();
+
+ // The sizes of the canvas and image element should be equal (or almost equal)
+ assert.strictEqual(Math.ceil(imageSize.width), Math.ceil(canvasSize.width));
+ assert.strictEqual(Math.ceil(imageSize.height), Math.ceil(canvasSize.height));
+
+ // Create full-size region
+ // This step is just for visual identification of the bug
+ AtImageView.drawByDrag(5, 5, canvasSize.width - 10, canvasSize.height - 10);
+
+ I.resizeWindow(800, 900);
+ I.resizeWindow(1200, 900);
+
+ canvasSize = await AtImageView.getCanvasSize();
+ imageSize = await AtImageView.getImageSize();
+
+ // The sizes should still be equal (or almost equal)
+ assert.strictEqual(Math.ceil(imageSize.width), Math.ceil(canvasSize.width));
+ assert.strictEqual(Math.ceil(imageSize.height), Math.ceil(canvasSize.height));
+});
diff --git a/e2e/tests/regression-tests/image-zoom-position.test.js b/e2e/tests/regression-tests/image-zoom-position.test.js
index cfb94a114b..25ba8d01c0 100644
--- a/e2e/tests/regression-tests/image-zoom-position.test.js
+++ b/e2e/tests/regression-tests/image-zoom-position.test.js
@@ -3,7 +3,7 @@ const Helpers = require('../helpers');
Feature('Image zoom position').tag('@regress');
-const IMAGE = 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/005eb6e2010fa7aa.jpg';
const config = `
diff --git a/e2e/tests/regression-tests/image-zoom-transform.test.js b/e2e/tests/regression-tests/image-zoom-transform.test.js
index fb3fc7c88e..b53141c488 100644
--- a/e2e/tests/regression-tests/image-zoom-transform.test.js
+++ b/e2e/tests/regression-tests/image-zoom-transform.test.js
@@ -2,7 +2,7 @@ const assert = require('assert');
Feature('Zoomed transforms').tag('@regress');
-const IMAGE = 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
const config = `
diff --git a/e2e/tests/regression-tests/image.regions-select.test.js b/e2e/tests/regression-tests/image.regions-select.test.js
index ef9ece8d7f..634d4e4057 100644
--- a/e2e/tests/regression-tests/image.regions-select.test.js
+++ b/e2e/tests/regression-tests/image.regions-select.test.js
@@ -1,7 +1,6 @@
Feature('Select region by clicking on it');
-const IMAGE =
- 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
const BLUEVIOLET = {
color: '#8A2BE2',
diff --git a/e2e/tests/regression-tests/multiple-same-named-tools.test.js b/e2e/tests/regression-tests/multiple-same-named-tools.test.js
index d1d31f0289..bd09a5dcf5 100644
--- a/e2e/tests/regression-tests/multiple-same-named-tools.test.js
+++ b/e2e/tests/regression-tests/multiple-same-named-tools.test.js
@@ -16,8 +16,7 @@ const config = `
`;
const data = {
- image:
- 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg',
+ image: 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg',
};
Feature('Two or more same named tools referred same image').tag('@regress');
diff --git a/e2e/tests/regression-tests/preselected-choices.test.js b/e2e/tests/regression-tests/preselected-choices.test.js
index 7a2cfd1987..226f91fdfe 100644
--- a/e2e/tests/regression-tests/preselected-choices.test.js
+++ b/e2e/tests/regression-tests/preselected-choices.test.js
@@ -1,91 +1,91 @@
-const assert = require('assert');
-
-Feature('Preselected choices');
-
-Scenario('Make a duplicate of annotation with preselected choices', async ({ I, LabelStudio, AtTopbar })=>{
- const params = {
- config: `
-
-
-
-
-
-
-`,
- data: {
- text: 'Some text',
- },
- annotations: [{
- id: 'test',
- result: [
- {
- from_name: 'choices',
- id: 'mDp7Hpbw6_',
- origin: 'manual',
- to_name: 'text',
- type: 'choices',
- value: {
- choices: ['Option 2'],
- },
- },
- ],
- }],
- };
-
- I.amOnPage('/');
- LabelStudio.init(params);
- // Try to create copy of current annotation
- AtTopbar.click('[aria-label="Copy Annotation"]');
- const duplicateResult = await LabelStudio.serialize();
-
- // Make sure there are no results other than the copied ones
- assert.deepStrictEqual(duplicateResult.length, 1);
- assert.deepStrictEqual(duplicateResult[0].value.choices, ['Option 2']);
-
- // Create new annotation
- I.click('[aria-label="Annotations List Toggle"]');
- I.click('[aria-label="Create Annotation"]');
- const annotationWithPresetValues = await LabelStudio.serialize();
-
- // Check that there is only the result come from selecting by default
- assert.deepStrictEqual(annotationWithPresetValues.length, 1);
- assert.deepStrictEqual(annotationWithPresetValues[0].value.choices, ['Option 1']);
-});
-
-Scenario('Make a duplicate of empty annotation with preselected choices', async ({ I, LabelStudio, AtTopbar })=>{
- const params = {
- config: `
-
-
-
-
-
-
-`,
- data: {
- text: 'Some text',
- },
- annotations: [{
- id: 'test',
- result: [],
- }],
- };
-
- I.amOnPage('/');
- LabelStudio.init(params);
- // Try to create copy of current annotation
- AtTopbar.click('[aria-label="Copy Annotation"]');
- const duplicateResult = await LabelStudio.serialize();
-
- // Make sure there are no preselected results
- assert.deepStrictEqual(duplicateResult.length, 0);
-
- // Create new annotation
- I.click('[aria-label="Annotations List Toggle"]');
- I.click('[aria-label="Create Annotation"]');
- const annotationWithPresetValues = await LabelStudio.serialize();
-
- // Check that there is only the result come from selecting by default
- assert.deepStrictEqual(annotationWithPresetValues.length, 1);
- assert.deepStrictEqual(annotationWithPresetValues[0].value.choices, ['Option 1']);
-});
+const assert = require('assert');
+
+Feature('Preselected choices');
+
+Scenario('Make a duplicate of annotation with preselected choices', async ({ I, LabelStudio, AtTopbar }) => {
+ const params = {
+ config: `
+
+
+
+
+
+
+`,
+ data: {
+ text: 'Some text',
+ },
+ annotations: [{
+ id: 'test',
+ result: [
+ {
+ from_name: 'choices',
+ id: 'mDp7Hpbw6_',
+ origin: 'manual',
+ to_name: 'text',
+ type: 'choices',
+ value: {
+ choices: ['Option 2'],
+ },
+ },
+ ],
+ }],
+ };
+
+ I.amOnPage('/');
+ LabelStudio.init(params);
+ // Try to create copy of current annotation
+ AtTopbar.click('[aria-label="Copy Annotation"]');
+ const duplicateResult = await LabelStudio.serialize();
+
+ // Make sure there are no results other than the copied ones
+ assert.deepStrictEqual(duplicateResult.length, 1);
+ assert.deepStrictEqual(duplicateResult[0].value.choices, ['Option 2']);
+
+ // Create new annotation
+ I.click('[aria-label="Annotations List Toggle"]');
+ I.click('[aria-label="Create Annotation"]');
+ const annotationWithPresetValues = await LabelStudio.serialize();
+
+ // Check that there is only the result come from selecting by default
+ assert.deepStrictEqual(annotationWithPresetValues.length, 1);
+ assert.deepStrictEqual(annotationWithPresetValues[0].value.choices, ['Option 1']);
+});
+
+Scenario('Make a duplicate of empty annotation with preselected choices', async ({ I, LabelStudio, AtTopbar }) => {
+ const params = {
+ config: `
+
+
+
+
+
+
+`,
+ data: {
+ text: 'Some text',
+ },
+ annotations: [{
+ id: 'test',
+ result: [],
+ }],
+ };
+
+ I.amOnPage('/');
+ LabelStudio.init(params);
+ // Try to create copy of current annotation
+ AtTopbar.click('[aria-label="Copy Annotation"]');
+ const duplicateResult = await LabelStudio.serialize();
+
+ // Make sure there are no preselected results
+ assert.deepStrictEqual(duplicateResult.length, 0);
+
+ // Create new annotation
+ I.click('[aria-label="Annotations List Toggle"]');
+ I.click('[aria-label="Create Annotation"]');
+ const annotationWithPresetValues = await LabelStudio.serialize();
+
+ // Check that there is only the result come from selecting by default
+ assert.deepStrictEqual(annotationWithPresetValues.length, 1);
+ assert.deepStrictEqual(annotationWithPresetValues[0].value.choices, ['Option 1']);
+});
diff --git a/e2e/tests/regression-tests/richtext.test.js b/e2e/tests/regression-tests/richtext.test.js
index 542c897713..4b3d71734b 100644
--- a/e2e/tests/regression-tests/richtext.test.js
+++ b/e2e/tests/regression-tests/richtext.test.js
@@ -293,7 +293,7 @@ Scenario('Neighboring nested regions misplacement', async ({ I, LabelStudio, AtS
},
]);
- Data(startBeforeEndParams).Scenario('Start before end problem', async ({ I, LabelStudio, AtSidebar, AtRichText, current }) => {
+ Data(startBeforeEndParams).Scenario('Start before end problem', async ({ I, LabelStudio, AtSidebar, current }) => {
const { tag, content, range } = current;
LabelStudio.setFeatureFlags({
diff --git a/e2e/tests/regression-tests/zoomed-image-displaying.test.js b/e2e/tests/regression-tests/zoomed-image-displaying.test.js
index 994e72526c..dc8ad62569 100644
--- a/e2e/tests/regression-tests/zoomed-image-displaying.test.js
+++ b/e2e/tests/regression-tests/zoomed-image-displaying.test.js
@@ -2,7 +2,7 @@ const assert = require('assert');
Feature('Zoomed image displaying').tag('@regress');
-const IMAGE = 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
const config = `
diff --git a/e2e/tests/taxonomy.test.js b/e2e/tests/taxonomy.test.js
index 2fd8b96297..840bba6f93 100644
--- a/e2e/tests/taxonomy.test.js
+++ b/e2e/tests/taxonomy.test.js
@@ -1,4 +1,3 @@
-/* global Before */
const assert = require('assert');
Feature('Taxonomy');
diff --git a/e2e/tests/text-area.test.js b/e2e/tests/text-area.test.js
index 6187dec563..6a2dfdea6f 100644
--- a/e2e/tests/text-area.test.js
+++ b/e2e/tests/text-area.test.js
@@ -1,6 +1,3 @@
-
-/* global Feature, Scenario */
-
const assert = require('assert');
const { serialize } = require('./helpers');
diff --git a/e2e/tests/timeseries.test.js b/e2e/tests/timeseries.test.js
index 836c894e0a..a7ecfec8a4 100644
--- a/e2e/tests/timeseries.test.js
+++ b/e2e/tests/timeseries.test.js
@@ -285,7 +285,7 @@ Scenario('TimeSeries with optimized data', async ({ I, LabelStudio, AtTimeSeries
I.say(`I see ${timestamp}`);
assert(timestamp === lastTimestamp || timestamp - lastTimestamp === 1,
- `Timestamps should not be skipped. Got ${lastTimestamp} and ${timestamp} but ${timestamp - 1} is missed`);
+ `Timestamps should not be skipped. Got ${lastTimestamp} and ${timestamp} but ${timestamp - 1} is missed`);
}
lastTimestamp = timestamp;
}
diff --git a/e2e/tests/toggle-visibility.test.js b/e2e/tests/toggle-visibility.test.js
index 78731b1ee4..055bcd5216 100644
--- a/e2e/tests/toggle-visibility.test.js
+++ b/e2e/tests/toggle-visibility.test.js
@@ -17,8 +17,7 @@ const config = `
`;
const data = {
- image:
- 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg',
+ image: 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg',
};
const createRegion = (from_name, type, values) => ({
@@ -207,7 +206,7 @@ examples.forEach(example => {
examplesTable.add([title, config, data, result]);
});
-Data(examplesTable).Scenario('Check visibility switcher through all examples', ({ I, AtSidebar, current })=> {
+Data(examplesTable).Scenario('Check visibility switcher through all examples', ({ I, AtSidebar, current }) => {
const { config, data, result } = current;
const params = { annotations: [{ id: 'test', result }], config, data };
diff --git a/e2e/tests/unfinished-polygons.test.js b/e2e/tests/unfinished-polygons.test.js
index 8c61bf9a84..0a23e873eb 100644
--- a/e2e/tests/unfinished-polygons.test.js
+++ b/e2e/tests/unfinished-polygons.test.js
@@ -1,606 +1,605 @@
-const { saveDraftLocally, getLocallySavedDraft } = require('./helpers');
-const assert = require('assert');
-
-
-Feature('Unfinished polygons');
-
-const IMAGE =
- 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg';
-
-const CONFIG = `
-
-
-
-
-
-
-
-`;
-
-const CONFIG_MULTIPLE = `
-
-
-
-
-
-
-
-
-`;
-
-const FLAGS = {
- ff_feat_front_DEV_2576_undo_key_points_polygon_short: true,
- ff_front_dev_2431_delete_polygon_points_080622_short: true,
- ff_front_dev_2432_auto_save_polygon_draft_210622_short: true,
-};
-
-Scenario('Drafts for unfinished polygons', async function({ I, LabelStudio, AtLabels, AtImageView }) {
- I.amOnPage('/');
- LabelStudio.init({
- config: CONFIG,
- data: {
- image: IMAGE,
- },
- params: {
- onSubmitDraft: saveDraftLocally,
- },
- });
- LabelStudio.setFeatureFlags(FLAGS);
-
- AtImageView.waitForImage();
-
- await AtImageView.lookForStage();
-
- I.say('start drawing polygon without finishing it');
- AtLabels.clickLabel('Hello');
- AtImageView.drawByClickingPoints([[50,50], [100, 50], [100, 80]]);
-
- I.say('wait until autosave');
- I.waitForFunction(()=>!!window.LSDraft, .5);
- I.say('check result');
- const draft = await I.executeScript(getLocallySavedDraft);
-
- assert.strictEqual(draft[0].value.points.length, 3);
- assert.strictEqual(draft[0].value.closed, false);
-});
-
-Scenario('Saving polygon drawing steps to history', async function({ I, LabelStudio, AtLabels, AtImageView }) {
- I.amOnPage('/');
- LabelStudio.setFeatureFlags(FLAGS);
- LabelStudio.init({
- config: CONFIG,
- data: {
- image: IMAGE,
- },
- });
-
- AtImageView.waitForImage();
-
- await AtImageView.lookForStage();
-
- I.say('put one point of polygon');
- AtLabels.clickLabel('Hello');
- AtImageView.drawByClick(50, 50);
-
- I.say('check current history size');
- let historyStepsCount = await I.executeScript(()=>window.Htx.annotationStore.selected.history.history.length);
-
- assert.strictEqual(historyStepsCount, 2);
-
- I.say('try to draw some more points and close polygon');
- AtImageView.drawByClick(100, 50);
- AtImageView.drawByClick(125, 100);
- AtImageView.drawByClick(50, 50);
-
- I.say('check current history size and result');
- historyStepsCount = await I.executeScript(()=>window.Htx.annotationStore.selected.history.history.length);
- assert.strictEqual(historyStepsCount, 5);
- let result = await LabelStudio.serialize();
-
- assert.strictEqual(result[0].value.points.length, 3);
- assert.strictEqual(result[0].value.closed, true);
-
- I.say('try to undo closing and 2 last points');
- I.click('button[aria-label=Undo]');
- I.click('button[aria-label=Undo]');
- I.click('button[aria-label=Undo]');
- I.say('check current history index and result');
- historyStepsCount = await I.executeScript(()=>window.Htx.annotationStore.selected.history.undoIdx);
- assert.strictEqual(historyStepsCount, 1);
- result = await LabelStudio.serialize();
- assert.strictEqual(result[0].value.points.length, 1);
- assert.strictEqual(result[0].value.closed, false);
-
-});
-
-Scenario('Init an annotation with old format of closed polygon result', async function({ I, LabelStudio, AtImageView }) {
- I.amOnPage('/');
- LabelStudio.setFeatureFlags(FLAGS);
- LabelStudio.init({
- config: CONFIG,
- data: {
- image: IMAGE,
- },
- annotations: [
- {
- id: 'test',
- result: [
- {
- 'original_width': 2242,
- 'original_height': 2802,
- 'image_rotation': 0,
- 'value': {
- 'points': [
- [
- 22.38442822384428,
- 27.042801556420233,
- ],
- [
- 77.61557177615572,
- 24.90272373540856,
- ],
- [
- 48.90510948905109,
- 76.07003891050584,
- ],
- ],
- 'polygonlabels': [
- 'Hello',
- ],
- },
- 'id': 'tNe7Bjmydb',
- 'from_name': 'tag',
- 'to_name': 'img',
- 'type': 'polygonlabels',
- 'origin': 'manual',
- },
- ],
- },
- ],
- });
-
- AtImageView.waitForImage();
-
- const result = await LabelStudio.serialize();
-
- assert.strictEqual(result[0].value.points.length, 3);
- assert.strictEqual(result[0].value.closed, true);
-});
-
-Scenario('Init an annotation with result of new format of polygon results', async function({ I, LabelStudio, AtImageView }) {
- I.amOnPage('/');
- LabelStudio.setFeatureFlags(FLAGS);
- LabelStudio.init({
- config: CONFIG,
- data: {
- image: IMAGE,
- },
- annotations: [
- {
- id: 'test',
- result: [
- {
- 'original_width': 2242,
- 'original_height': 2802,
- 'image_rotation': 0,
- 'value': {
- 'points': [
- [
- 40,
- 40,
- ],
- [
- 50,
- 40,
- ],
- [
- 50,
- 50,
- ],
- [
- 40,
- 50,
- ],
- ],
- 'closed': true,
- 'polygonlabels': [
- 'World',
- ],
- },
- 'id': 'tNe7Bjmydb_2',
- 'from_name': 'tag',
- 'to_name': 'img',
- 'type': 'polygonlabels',
- 'origin': 'manual',
- },
- {
- 'original_width': 2242,
- 'original_height': 2802,
- 'image_rotation': 0,
- 'value': {
- 'points': [
- [
- 10,
- 10,
- ],
- [
- 30,
- 10,
- ],
- [
- 20,
- 20,
- ],
- ],
- 'closed': false,
- 'polygonlabels': [
- 'Hello',
- ],
- },
- 'id': 'tNe7Bjmydb',
- 'from_name': 'tag',
- 'to_name': 'img',
- 'type': 'polygonlabels',
- 'origin': 'manual',
- },
- ],
- },
- ],
- });
-
- AtImageView.waitForImage();
-
- I.say('check loaded regions');
- let result = await LabelStudio.serialize();
-
- assert.strictEqual(result.length, 2);
- assert.strictEqual(result[0].value.points.length, 4);
- assert.strictEqual(result[0].value.closed, true);
- assert.strictEqual(result[1].value.points.length, 3);
- assert.strictEqual(result[1].value.closed, false);
-
- I.say('try to continue drawing loaded unfinished region');
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
-
- AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .40 );
- result = await LabelStudio.serialize();
- assert.strictEqual(result[1].value.points.length, 4);
- assert.strictEqual(result[1].value.closed, false);
-
- I.say('try to close loaded region');
- AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .10);
- result = await LabelStudio.serialize();
- assert.strictEqual(result[1].value.points.length, 4);
- assert.strictEqual(result[1].value.closed, true);
-
- // I.say("check that it is possible to go back throught history");
- // I.pressKey(['CommandOrControl', 'Z']);
- // result = await LabelStudio.serialize();
- // assert.strictEqual(result[1].value.points.length, 4);
- // assert.strictEqual(result[1].value.closed, false);
- //
- // I.say("check that it is possible to close this region again");
- // AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .10);
- // result = await LabelStudio.serialize();
- // assert.strictEqual(result[1].value.points.length, 4);
- // assert.strictEqual(result[1].value.closed, true);
-
-});
-
-Scenario('Removing a polygon by going back through history', async function({ I, LabelStudio, AtLabels, AtImageView }) {
- I.amOnPage('/');
- LabelStudio.setFeatureFlags(FLAGS);
- LabelStudio.init({
- config: CONFIG,
- data: {
- image: IMAGE,
- },
- params: {
- onSubmitDraft: saveDraftLocally,
- },
- });
-
- AtImageView.waitForImage();
-
- await AtImageView.lookForStage();
-
- I.say('start drawing polygon');
- AtLabels.clickLabel('Hello');
- AtImageView.drawByClickingPoints([[50,50], [100, 50]]);
-
- I.say('revert all changes and creating of the region');
- I.pressKey(['CommandOrControl', 'Z']);
- I.pressKey(['CommandOrControl', 'Z']);
-
- I.say('polygon should disappear and polygon tool should be switched of');
- let result = await LabelStudio.serialize();
-
- assert.strictEqual(result.length, 0);
-
- I.say('try to draw after that');
- AtImageView.drawByClickingPoints([[50,50], [100, 50]]);
-
- I.say('check if it was possible to do this (it shouldn\'t)');
- result = await LabelStudio.serialize();
- assert.strictEqual(result.length, 0);
-
- I.say('check if there were any errors');
- // The potential errors should be caught by `errorsCollector` plugin
-});
-
-Scenario('Continue annotating after closing region from draft', async function({ I, LabelStudio, AtLabels, AtImageView }) {
- I.amOnPage('/');
- LabelStudio.setFeatureFlags(FLAGS);
- LabelStudio.init({
- config: CONFIG,
- data: {
- image: IMAGE,
- },
- annotations: [
- {
- id: 'test',
- result: [
- {
- 'original_width': 2242,
- 'original_height': 2802,
- 'image_rotation': 0,
- 'value': {
- 'points': [
- [
- 10,
- 10,
- ],
- [
- 30,
- 10,
- ],
- [
- 20,
- 20,
- ],
- ],
- 'closed': false,
- 'polygonlabels': [
- 'Hello',
- ],
- },
- 'id': 'tNe7Bjmydb',
- 'from_name': 'tag',
- 'to_name': 'img',
- 'type': 'polygonlabels',
- 'origin': 'manual',
- },
- ],
- },
- ],
- });
-
- AtImageView.waitForImage();
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
-
- I.say('close loaded region');
- AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .10);
-
- I.say('try to create another region');
- AtLabels.clickLabel('World');
-
- AtImageView.drawByClickingPoints([
- [canvasSize.width * .40, canvasSize.height * .40],
- [canvasSize.width * .50, canvasSize.height * .40],
- [canvasSize.width * .50, canvasSize.height * .50],
- [canvasSize.width * .40, canvasSize.height * .50],
- [canvasSize.width * .40, canvasSize.height * .40],
- ]);
-
- const result = await LabelStudio.serialize();
-
- assert.strictEqual(result.length, 2);
- assert.strictEqual(result[1].value.points.length, 4);
- assert.strictEqual(result[1].value.closed, true);
-
-});
-
-Scenario('Change label on unfinished polygons', async function({ I, LabelStudio, AtLabels, AtImageView }) {
- I.amOnPage('/');
- LabelStudio.init({
- config: CONFIG,
- data: {
- image: IMAGE,
- },
- params: {
- onSubmitDraft: saveDraftLocally,
- },
- });
- LabelStudio.setFeatureFlags(FLAGS);
-
- AtImageView.waitForImage();
-
- await AtImageView.lookForStage();
-
- I.say('start drawing polygon without finishing it');
- AtLabels.clickLabel('Hello');
- AtImageView.drawByClickingPoints([[50,50], [100, 50], [100, 80]]);
- AtLabels.clickLabel('World');
-
- I.say('wait until autosave');
- I.waitForFunction(()=>!!window.LSDraft, .5);
- I.say('check result');
- const draft = await I.executeScript(getLocallySavedDraft);
-
- assert.strictEqual(draft[0].value.polygonlabels[0], 'World');
-});
-
-
-const selectedLabelsVariants = new DataTable(['labels']);
-
-selectedLabelsVariants.add([['Label 1']]);
-selectedLabelsVariants.add([['Label 2', 'Label 3']]);
-
-Data(selectedLabelsVariants).Scenario('Indicate selected labels', async function({ I, LabelStudio, AtLabels, AtImageView, current }) {
- const { labels } = current;
-
- I.amOnPage('/');
- LabelStudio.setFeatureFlags(FLAGS);
- LabelStudio.init({
- config: CONFIG_MULTIPLE,
- data: {
- image: IMAGE,
- },
- annotations: [
- {
- id: 'test',
- result: [
- {
- 'original_width': 2242,
- 'original_height': 2802,
- 'image_rotation': 0,
- 'value': {
- 'points': [
- [
- 10,
- 10,
- ],
- [
- 30,
- 10,
- ],
- [
- 20,
- 20,
- ],
- ],
- 'closed': false,
- 'polygonlabels': labels,
- },
- 'id': 'tNe7Bjmydb',
- 'from_name': 'tag',
- 'to_name': 'img',
- 'type': 'polygonlabels',
- 'origin': 'manual',
- },
- ],
- },
- ],
- });
-
- AtImageView.waitForImage();
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
-
- I.say('check if we see an indication of selected labels after resuming from draft');
- for (const label of labels) {
- AtLabels.seeSelectedLabel(label);
- }
-
- I.say('close loaded region');
- AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .10);
-
- I.say('check that we do not see an indication of selected after region completion');
- for (const label of labels) {
- AtLabels.dontSeeSelectedLabel(label);
- }
-
- I.say('check if we see an indication of selected labels after going back through the history');
- I.pressKey(['CommandOrControl', 'Z']);
- for (const label of labels) {
- AtLabels.seeSelectedLabel(label);
- }
-
-});
-
-const selectedPolygonAfterCreatingVariants = new DataTable(['shouldSelect', 'description']);
-
-selectedPolygonAfterCreatingVariants.add([false, 'Without set setting']);
-selectedPolygonAfterCreatingVariants.add([true, 'With set setting']);
-
-Data(selectedPolygonAfterCreatingVariants).Scenario('Select polygon after creating from unfinished draft', async function({ I, LabelStudio, AtImageView, AtSidebar, AtSettings, current }) {
- const { shouldSelect, description } = current;
-
- I.say(description);
- I.amOnPage('/');
- LabelStudio.setFeatureFlags(FLAGS);
- LabelStudio.init({
- config: CONFIG,
- data: {
- image: IMAGE,
- },
- annotations: [
- {
- id: 'test',
- result: [
- {
- 'original_width': 2242,
- 'original_height': 2802,
- 'image_rotation': 0,
- 'value': {
- 'points': [
- [
- 10,
- 10,
- ],
- [
- 30,
- 10,
- ],
- [
- 20,
- 20,
- ],
- ],
- 'closed': false,
- 'polygonlabels': [
- 'Hello',
- ],
- },
- 'id': 'tNe7Bjmydb',
- 'from_name': 'tag',
- 'to_name': 'img',
- 'type': 'polygonlabels',
- 'origin': 'manual',
- },
- ],
- },
- ],
- });
-
- if (shouldSelect) {
- AtSettings.open();
- AtSettings.setGeneralSettings({
- [AtSettings.GENERAL_SETTINGS.AUTO_SELECT_REGION]: shouldSelect,
- });
- AtSettings.close();
- }
-
- AtImageView.waitForImage();
- await AtImageView.lookForStage();
- const canvasSize = await AtImageView.getCanvasSize();
-
- I.say('close loaded region');
- AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .10);
-
- I.say(`check that region ${shouldSelect ? 'is' : 'is not'} selected`);
- if (shouldSelect) {
- AtSidebar.seeSelectedRegion();
- } else {
- AtSidebar.dontSeeSelectedRegion();
- }
-
- I.say('unselect regions');
- I.pressKey('u');
- AtSidebar.dontSeeSelectedRegion();
-
- I.say('go back through the history');
- I.pressKey(['CommandOrControl', 'Z']);
- AtSidebar.dontSeeSelectedRegion();
-
- I.say('repeat creation and checking');
- AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .10);
- if (shouldSelect) {
- AtSidebar.seeSelectedRegion();
- } else {
- AtSidebar.dontSeeSelectedRegion();
- }
-
-});
+const { saveDraftLocally, getLocallySavedDraft } = require('./helpers');
+const assert = require('assert');
+
+
+Feature('Unfinished polygons');
+
+const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg';
+
+const CONFIG = `
+
+
+
+
+
+
+
+`;
+
+const CONFIG_MULTIPLE = `
+
+
+
+
+
+
+
+
+`;
+
+const FLAGS = {
+ ff_feat_front_DEV_2576_undo_key_points_polygon_short: true,
+ ff_front_dev_2431_delete_polygon_points_080622_short: true,
+ ff_front_dev_2432_auto_save_polygon_draft_210622_short: true,
+};
+
+Scenario('Drafts for unfinished polygons', async function({ I, LabelStudio, AtLabels, AtImageView }) {
+ I.amOnPage('/');
+ LabelStudio.init({
+ config: CONFIG,
+ data: {
+ image: IMAGE,
+ },
+ params: {
+ onSubmitDraft: saveDraftLocally,
+ },
+ });
+ LabelStudio.setFeatureFlags(FLAGS);
+
+ AtImageView.waitForImage();
+
+ await AtImageView.lookForStage();
+
+ I.say('start drawing polygon without finishing it');
+ AtLabels.clickLabel('Hello');
+ AtImageView.drawByClickingPoints([[50,50], [100, 50], [100, 80]]);
+
+ I.say('wait until autosave');
+ I.waitForFunction(() => !!window.LSDraft, .5);
+ I.say('check result');
+ const draft = await I.executeScript(getLocallySavedDraft);
+
+ assert.strictEqual(draft[0].value.points.length, 3);
+ assert.strictEqual(draft[0].value.closed, false);
+});
+
+Scenario('Saving polygon drawing steps to history', async function({ I, LabelStudio, AtLabels, AtImageView }) {
+ I.amOnPage('/');
+ LabelStudio.setFeatureFlags(FLAGS);
+ LabelStudio.init({
+ config: CONFIG,
+ data: {
+ image: IMAGE,
+ },
+ });
+
+ AtImageView.waitForImage();
+
+ await AtImageView.lookForStage();
+
+ I.say('put one point of polygon');
+ AtLabels.clickLabel('Hello');
+ AtImageView.drawByClick(50, 50);
+
+ I.say('check current history size');
+ let historyStepsCount = await I.executeScript(() => window.Htx.annotationStore.selected.history.history.length);
+
+ assert.strictEqual(historyStepsCount, 2);
+
+ I.say('try to draw some more points and close polygon');
+ AtImageView.drawByClick(100, 50);
+ AtImageView.drawByClick(125, 100);
+ AtImageView.drawByClick(50, 50);
+
+ I.say('check current history size and result');
+ historyStepsCount = await I.executeScript(() => window.Htx.annotationStore.selected.history.history.length);
+ assert.strictEqual(historyStepsCount, 5);
+ let result = await LabelStudio.serialize();
+
+ assert.strictEqual(result[0].value.points.length, 3);
+ assert.strictEqual(result[0].value.closed, true);
+
+ I.say('try to undo closing and 2 last points');
+ I.click('button[aria-label=Undo]');
+ I.click('button[aria-label=Undo]');
+ I.click('button[aria-label=Undo]');
+ I.say('check current history index and result');
+ historyStepsCount = await I.executeScript(() => window.Htx.annotationStore.selected.history.undoIdx);
+ assert.strictEqual(historyStepsCount, 1);
+ result = await LabelStudio.serialize();
+ assert.strictEqual(result[0].value.points.length, 1);
+ assert.strictEqual(result[0].value.closed, false);
+
+});
+
+Scenario('Init an annotation with old format of closed polygon result', async function({ I, LabelStudio, AtImageView }) {
+ I.amOnPage('/');
+ LabelStudio.setFeatureFlags(FLAGS);
+ LabelStudio.init({
+ config: CONFIG,
+ data: {
+ image: IMAGE,
+ },
+ annotations: [
+ {
+ id: 'test',
+ result: [
+ {
+ 'original_width': 2242,
+ 'original_height': 2802,
+ 'image_rotation': 0,
+ 'value': {
+ 'points': [
+ [
+ 22.38442822384428,
+ 27.042801556420233,
+ ],
+ [
+ 77.61557177615572,
+ 24.90272373540856,
+ ],
+ [
+ 48.90510948905109,
+ 76.07003891050584,
+ ],
+ ],
+ 'polygonlabels': [
+ 'Hello',
+ ],
+ },
+ 'id': 'tNe7Bjmydb',
+ 'from_name': 'tag',
+ 'to_name': 'img',
+ 'type': 'polygonlabels',
+ 'origin': 'manual',
+ },
+ ],
+ },
+ ],
+ });
+
+ AtImageView.waitForImage();
+
+ const result = await LabelStudio.serialize();
+
+ assert.strictEqual(result[0].value.points.length, 3);
+ assert.strictEqual(result[0].value.closed, true);
+});
+
+Scenario('Init an annotation with result of new format of polygon results', async function({ I, LabelStudio, AtImageView }) {
+ I.amOnPage('/');
+ LabelStudio.setFeatureFlags(FLAGS);
+ LabelStudio.init({
+ config: CONFIG,
+ data: {
+ image: IMAGE,
+ },
+ annotations: [
+ {
+ id: 'test',
+ result: [
+ {
+ 'original_width': 2242,
+ 'original_height': 2802,
+ 'image_rotation': 0,
+ 'value': {
+ 'points': [
+ [
+ 40,
+ 40,
+ ],
+ [
+ 50,
+ 40,
+ ],
+ [
+ 50,
+ 50,
+ ],
+ [
+ 40,
+ 50,
+ ],
+ ],
+ 'closed': true,
+ 'polygonlabels': [
+ 'World',
+ ],
+ },
+ 'id': 'tNe7Bjmydb_2',
+ 'from_name': 'tag',
+ 'to_name': 'img',
+ 'type': 'polygonlabels',
+ 'origin': 'manual',
+ },
+ {
+ 'original_width': 2242,
+ 'original_height': 2802,
+ 'image_rotation': 0,
+ 'value': {
+ 'points': [
+ [
+ 10,
+ 10,
+ ],
+ [
+ 30,
+ 10,
+ ],
+ [
+ 20,
+ 20,
+ ],
+ ],
+ 'closed': false,
+ 'polygonlabels': [
+ 'Hello',
+ ],
+ },
+ 'id': 'tNe7Bjmydb',
+ 'from_name': 'tag',
+ 'to_name': 'img',
+ 'type': 'polygonlabels',
+ 'origin': 'manual',
+ },
+ ],
+ },
+ ],
+ });
+
+ AtImageView.waitForImage();
+
+ I.say('check loaded regions');
+ let result = await LabelStudio.serialize();
+
+ assert.strictEqual(result.length, 2);
+ assert.strictEqual(result[0].value.points.length, 4);
+ assert.strictEqual(result[0].value.closed, true);
+ assert.strictEqual(result[1].value.points.length, 3);
+ assert.strictEqual(result[1].value.closed, false);
+
+ I.say('try to continue drawing loaded unfinished region');
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+
+ AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .40);
+ result = await LabelStudio.serialize();
+ assert.strictEqual(result[1].value.points.length, 4);
+ assert.strictEqual(result[1].value.closed, false);
+
+ I.say('try to close loaded region');
+ AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .10);
+ result = await LabelStudio.serialize();
+ assert.strictEqual(result[1].value.points.length, 4);
+ assert.strictEqual(result[1].value.closed, true);
+
+ // I.say("check that it is possible to go back throught history");
+ // I.pressKey(['CommandOrControl', 'Z']);
+ // result = await LabelStudio.serialize();
+ // assert.strictEqual(result[1].value.points.length, 4);
+ // assert.strictEqual(result[1].value.closed, false);
+ //
+ // I.say("check that it is possible to close this region again");
+ // AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .10);
+ // result = await LabelStudio.serialize();
+ // assert.strictEqual(result[1].value.points.length, 4);
+ // assert.strictEqual(result[1].value.closed, true);
+
+});
+
+Scenario('Removing a polygon by going back through history', async function({ I, LabelStudio, AtLabels, AtImageView }) {
+ I.amOnPage('/');
+ LabelStudio.setFeatureFlags(FLAGS);
+ LabelStudio.init({
+ config: CONFIG,
+ data: {
+ image: IMAGE,
+ },
+ params: {
+ onSubmitDraft: saveDraftLocally,
+ },
+ });
+
+ AtImageView.waitForImage();
+
+ await AtImageView.lookForStage();
+
+ I.say('start drawing polygon');
+ AtLabels.clickLabel('Hello');
+ AtImageView.drawByClickingPoints([[50,50], [100, 50]]);
+
+ I.say('revert all changes and creating of the region');
+ I.pressKey(['CommandOrControl', 'Z']);
+ I.pressKey(['CommandOrControl', 'Z']);
+
+ I.say('polygon should disappear and polygon tool should be switched of');
+ let result = await LabelStudio.serialize();
+
+ assert.strictEqual(result.length, 0);
+
+ I.say('try to draw after that');
+ AtImageView.drawByClickingPoints([[50,50], [100, 50]]);
+
+ I.say('check if it was possible to do this (it shouldn\'t)');
+ result = await LabelStudio.serialize();
+ assert.strictEqual(result.length, 0);
+
+ I.say('check if there were any errors');
+ // The potential errors should be caught by `errorsCollector` plugin
+});
+
+Scenario('Continue annotating after closing region from draft', async function({ I, LabelStudio, AtLabels, AtImageView }) {
+ I.amOnPage('/');
+ LabelStudio.setFeatureFlags(FLAGS);
+ LabelStudio.init({
+ config: CONFIG,
+ data: {
+ image: IMAGE,
+ },
+ annotations: [
+ {
+ id: 'test',
+ result: [
+ {
+ 'original_width': 2242,
+ 'original_height': 2802,
+ 'image_rotation': 0,
+ 'value': {
+ 'points': [
+ [
+ 10,
+ 10,
+ ],
+ [
+ 30,
+ 10,
+ ],
+ [
+ 20,
+ 20,
+ ],
+ ],
+ 'closed': false,
+ 'polygonlabels': [
+ 'Hello',
+ ],
+ },
+ 'id': 'tNe7Bjmydb',
+ 'from_name': 'tag',
+ 'to_name': 'img',
+ 'type': 'polygonlabels',
+ 'origin': 'manual',
+ },
+ ],
+ },
+ ],
+ });
+
+ AtImageView.waitForImage();
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+
+ I.say('close loaded region');
+ AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .10);
+
+ I.say('try to create another region');
+ AtLabels.clickLabel('World');
+
+ AtImageView.drawByClickingPoints([
+ [canvasSize.width * .40, canvasSize.height * .40],
+ [canvasSize.width * .50, canvasSize.height * .40],
+ [canvasSize.width * .50, canvasSize.height * .50],
+ [canvasSize.width * .40, canvasSize.height * .50],
+ [canvasSize.width * .40, canvasSize.height * .40],
+ ]);
+
+ const result = await LabelStudio.serialize();
+
+ assert.strictEqual(result.length, 2);
+ assert.strictEqual(result[1].value.points.length, 4);
+ assert.strictEqual(result[1].value.closed, true);
+
+});
+
+Scenario('Change label on unfinished polygons', async function({ I, LabelStudio, AtLabels, AtImageView }) {
+ I.amOnPage('/');
+ LabelStudio.init({
+ config: CONFIG,
+ data: {
+ image: IMAGE,
+ },
+ params: {
+ onSubmitDraft: saveDraftLocally,
+ },
+ });
+ LabelStudio.setFeatureFlags(FLAGS);
+
+ AtImageView.waitForImage();
+
+ await AtImageView.lookForStage();
+
+ I.say('start drawing polygon without finishing it');
+ AtLabels.clickLabel('Hello');
+ AtImageView.drawByClickingPoints([[50,50], [100, 50], [100, 80]]);
+ AtLabels.clickLabel('World');
+
+ I.say('wait until autosave');
+ I.waitForFunction(() => !!window.LSDraft, .5);
+ I.say('check result');
+ const draft = await I.executeScript(getLocallySavedDraft);
+
+ assert.strictEqual(draft[0].value.polygonlabels[0], 'World');
+});
+
+
+const selectedLabelsVariants = new DataTable(['labels']);
+
+selectedLabelsVariants.add([['Label 1']]);
+selectedLabelsVariants.add([['Label 2', 'Label 3']]);
+
+Data(selectedLabelsVariants).Scenario('Indicate selected labels', async function({ I, LabelStudio, AtLabels, AtImageView, current }) {
+ const { labels } = current;
+
+ I.amOnPage('/');
+ LabelStudio.setFeatureFlags(FLAGS);
+ LabelStudio.init({
+ config: CONFIG_MULTIPLE,
+ data: {
+ image: IMAGE,
+ },
+ annotations: [
+ {
+ id: 'test',
+ result: [
+ {
+ 'original_width': 2242,
+ 'original_height': 2802,
+ 'image_rotation': 0,
+ 'value': {
+ 'points': [
+ [
+ 10,
+ 10,
+ ],
+ [
+ 30,
+ 10,
+ ],
+ [
+ 20,
+ 20,
+ ],
+ ],
+ 'closed': false,
+ 'polygonlabels': labels,
+ },
+ 'id': 'tNe7Bjmydb',
+ 'from_name': 'tag',
+ 'to_name': 'img',
+ 'type': 'polygonlabels',
+ 'origin': 'manual',
+ },
+ ],
+ },
+ ],
+ });
+
+ AtImageView.waitForImage();
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+
+ I.say('check if we see an indication of selected labels after resuming from draft');
+ for (const label of labels) {
+ AtLabels.seeSelectedLabel(label);
+ }
+
+ I.say('close loaded region');
+ AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .10);
+
+ I.say('check that we do not see an indication of selected after region completion');
+ for (const label of labels) {
+ AtLabels.dontSeeSelectedLabel(label);
+ }
+
+ I.say('check if we see an indication of selected labels after going back through the history');
+ I.pressKey(['CommandOrControl', 'Z']);
+ for (const label of labels) {
+ AtLabels.seeSelectedLabel(label);
+ }
+
+});
+
+const selectedPolygonAfterCreatingVariants = new DataTable(['shouldSelect', 'description']);
+
+selectedPolygonAfterCreatingVariants.add([false, 'Without set setting']);
+selectedPolygonAfterCreatingVariants.add([true, 'With set setting']);
+
+Data(selectedPolygonAfterCreatingVariants).Scenario('Select polygon after creating from unfinished draft', async function({ I, LabelStudio, AtImageView, AtSidebar, AtSettings, current }) {
+ const { shouldSelect, description } = current;
+
+ I.say(description);
+ I.amOnPage('/');
+ LabelStudio.setFeatureFlags(FLAGS);
+ LabelStudio.init({
+ config: CONFIG,
+ data: {
+ image: IMAGE,
+ },
+ annotations: [
+ {
+ id: 'test',
+ result: [
+ {
+ 'original_width': 2242,
+ 'original_height': 2802,
+ 'image_rotation': 0,
+ 'value': {
+ 'points': [
+ [
+ 10,
+ 10,
+ ],
+ [
+ 30,
+ 10,
+ ],
+ [
+ 20,
+ 20,
+ ],
+ ],
+ 'closed': false,
+ 'polygonlabels': [
+ 'Hello',
+ ],
+ },
+ 'id': 'tNe7Bjmydb',
+ 'from_name': 'tag',
+ 'to_name': 'img',
+ 'type': 'polygonlabels',
+ 'origin': 'manual',
+ },
+ ],
+ },
+ ],
+ });
+
+ if (shouldSelect) {
+ AtSettings.open();
+ AtSettings.setGeneralSettings({
+ [AtSettings.GENERAL_SETTINGS.AUTO_SELECT_REGION]: shouldSelect,
+ });
+ AtSettings.close();
+ }
+
+ AtImageView.waitForImage();
+ await AtImageView.lookForStage();
+ const canvasSize = await AtImageView.getCanvasSize();
+
+ I.say('close loaded region');
+ AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .10);
+
+ I.say(`check that region ${shouldSelect ? 'is' : 'is not'} selected`);
+ if (shouldSelect) {
+ AtSidebar.seeSelectedRegion();
+ } else {
+ AtSidebar.dontSeeSelectedRegion();
+ }
+
+ I.say('unselect regions');
+ I.pressKey('u');
+ AtSidebar.dontSeeSelectedRegion();
+
+ I.say('go back through the history');
+ I.pressKey(['CommandOrControl', 'Z']);
+ AtSidebar.dontSeeSelectedRegion();
+
+ I.say('repeat creation and checking');
+ AtImageView.drawByClick(canvasSize.width * .10, canvasSize.height * .10);
+ if (shouldSelect) {
+ AtSidebar.seeSelectedRegion();
+ } else {
+ AtSidebar.dontSeeSelectedRegion();
+ }
+
+});