const assert = require("assert"); const { kebabCase } = require("lodash"); Feature("Images' labels type matching"); const IMAGE = "https://htx-pub.s3.us-east-1.amazonaws.com/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg"; const createConfig = ({ shapes = ["Rectangle"], props } = {}) => { return ` ${shapes .map( (shapeName) => ` <${shapeName} name="image${shapeName}" toName="image" ${props} /> <${shapeName}Labels name="image${shapeName}Labels" toName="image" allowEmpty="true" ${props}> `; }; const createShape = { Rectangle: { byBBox(x, y, width, height, opts = {}) { return { ...opts, action: "drawByDrag", params: [x, y, width, height], result: { width, height, rotation: 0, x, y, }, }; }, }, Ellipse: { byBBox(x, y, width, height, opts = {}) { return { ...opts, action: "drawByDrag", 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: { byBBox(x, y, width, height, opts = {}) { const points = []; points.push([x, y]); points.push([x + width, y]); points.push([x + width, y + height]); points.push([x, y + height]); return { ...opts, action: "drawByClickingPoints", params: [[...points, points[0]]], result: { points, closed: true, }, }; }, }, KeyPoint: { byBBox(x, y, width, height, opts = {}) { return { ...opts, action: "drawByClickingPoints", params: [[[x + width / 2, y + height / 2]]], result: { x: x + width / 2, y: y + height / 2, width: 5, }, }; }, }, Brush: { byBBox(x, y, width, height, opts = {}) { const points = []; const startPoint = { x: x + 5, y: y + 5 }; const endPoint = { x: x + width - 5, y: y + height - 5 }; const rows = Math.ceil((endPoint.y - startPoint.y) / 10); const step = (endPoint.y - startPoint.y) / rows; for (let j = 0; j < rows; j++) { const cY = startPoint.y + step * j; points.push([startPoint.x, cY]); points.push([endPoint.x, cY]); } return { ...opts, action: "drawThroughPoints", params: [points], }; }, }, }; const DataStore = Data(Object.keys(createShape)); DataStore.Scenario( "Preventing applying labels of mismatch types", async ({ I, LabelStudio, AtImageView, AtOutliner, AtLabels, AtPanels, current }) => { const shape = current; const config = createConfig({ shapes: [shape], props: 'strokewidth="5"', }); const params = { config, data: { image: IMAGE }, }; const AtDetailsPanel = AtPanels.usePanel(AtPanels.PANEL.DETAILS); I.amOnPage("/"); LabelStudio.init(params); AtDetailsPanel.collapsePanel(); LabelStudio.waitForObjectsReady(); AtOutliner.seeRegions(0); const canvasSize = await AtImageView.getCanvasSize(); const size = Math.min(canvasSize.width, canvasSize.height); const offset = size * 0.05; const toolSelectors = [ (shapeName, shapeIdx) => { I.click( locate(".lsf-toolbar") .find(".lsf-tool") .at(+shapeIdx + 1), ); }, (_, shapeIdx) => { I.click(AtLabels.locateLabel("blank").at(+shapeIdx + 1)); }, (shapeName) => { AtLabels.clickLabel(`${shapeName}Create`); }, ]; for (const creator of Object.values(createShape[shape])) { const regions = toolSelectors.map((selector, idx) => { const x1 = (size / 3) * idx + offset; const x2 = (size / 3) * (idx + 1) - offset; const y1 = size / 3; const y2 = (size / 3) * 2; return creator(x1, y1, x2 - x1, y2 - y1, { shape }); }); const labelsCounter = (results, currentLabelName = "Label") => { return results.reduce((counter, result) => { const { type, value } = result; return counter + (type.endsWith("labels") && value[type] && value[type].includes(currentLabelName)); }, 0); }; const toolSelector = `[aria-label=${kebabCase(`${shape}-tool`)}]`; LabelStudio.init(params); LabelStudio.waitForObjectsReady(); AtOutliner.seeRegions(0); I.click(toolSelector); await AtImageView.lookForStage(); I.say(`${shape}: Drawing.`); regions.forEach((region, idx) => { toolSelectors[idx](shape, 0); AtImageView[region.action](...region.params); AtOutliner.seeRegions(idx + 1); I.pressKey(["u"]); }); I.click(toolSelector); I.say(`${shape}: Labeling.`); const currentLabelName = `${shape}Append`; regions.forEach((region, idx) => { AtOutliner.clickRegion(+idx + 1); AtLabels.clickLabel(currentLabelName); I.pressKey(["u"]); }); const results1 = await LabelStudio.serialize(); assert.strictEqual(labelsCounter(results1, currentLabelName), 3, "Labels number don't match"); regions.forEach((region, idx) => { I.say(`Click label ${idx}`); AtOutliner.clickRegion(+idx + 1); AtLabels.clickLabel("Label"); }); const results = await LabelStudio.serialize(); assert.strictEqual(labelsCounter(results, "Label"), 3, "Labels number don't match"); } }, );