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 ({ I, LabelStudio, AtImageView, AtOutliner, AtPanels, Modals, current }) => { const { maxUsage, shapeName } = current; const shape = shapes[shapeName]; const annotations = []; const AtDetailsPanel = AtPanels.usePanel(AtPanels.PANEL.DETAILS); 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.init({ config: createImageToolsConfig({ maxUsage }), data: { image: IMAGE, }, annotations: [ { id: "test", result: annotations, }, ], }); AtDetailsPanel.collapsePanel(); LabelStudio.waitForObjectsReady(); await AtImageView.lookForStage(); AtOutliner.seeRegions(maxUsage); I.pressKey("1"); I.pressKey(shape.shortcut); AtImageView.clickAt(50, 50); Modals.seeWarning(`You can't use Label_1 more than ${maxUsage} time(s)`); }, ); Data(maxUsageImageToolsDataTable).Scenario( "Max usages of labels in ImageView on region creating", async ({ I, LabelStudio, AtImageView, AtOutliner, AtPanels, Modals, current }) => { const { maxUsage, shapeName } = current; const shape = shapes[shapeName]; const AtDetailsPanel = AtPanels.usePanel(AtPanels.PANEL.DETAILS); I.amOnPage("/"); LabelStudio.init({ config: createImageLabelsConfig({ maxUsage }), data: { image: IMAGE, }, }); AtDetailsPanel.collapsePanel(); LabelStudio.waitForObjectsReady(); await AtImageView.lookForStage(); AtOutliner.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"); AtOutliner.seeRegions(k + 1); I.waitTicks(2); } I.pressKey(shape.hotkey); AtImageView.clickAt(50, 50); Modals.seeWarning(`You can't use ${shapeName}_1 more than ${maxUsage} time(s)`); }, ); Data(maxUsageDataTable).Scenario( "Max usages of labels in Audio on region creation", async ({ I, LabelStudio, AtOutliner, AtAudioView, Modals, current }) => { const { maxUsage } = current; I.amOnPage("/"); LabelStudio.init({ config: ` `, data: { audio: "/public/files/barradeen-emotional.mp3", }, }); await AtAudioView.waitForAudio(); await AtAudioView.lookForStage(); AtOutliner.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); AtOutliner.seeRegions(maxUsage); Modals.seeWarning(`You can't use Label_1 more than ${maxUsage} time(s)`); }, ); Data(maxUsageDataTable).Scenario( "Max usages of labels in RichText on region creation", async ({ I, LabelStudio, AtOutliner, AtRichText, Modals, current }) => { const { maxUsage } = current; I.amOnPage("/"); LabelStudio.init({ config: ` `, data: { url: "https://htx-pub.s3.amazonaws.com/example.txt", }, }); AtOutliner.seeRegions(0); LabelStudio.waitForObjectsReady(); 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)); Modals.seeWarning(`You can't use Label_1 more than ${maxUsage} time(s)`); }, ); Data(maxUsageDataTable).Scenario( "Max usages of labels in Paragraphs on region creation", async ({ I, LabelStudio, AtOutliner, AtParagraphs, current }) => { const { maxUsage } = current; I.amOnPage("/"); LabelStudio.init({ config: ` `, data: require("../examples/text-paragraphs").data, }); AtOutliner.seeRegions(0); LabelStudio.waitForObjectsReady(); for (let k = 0; k < maxUsage; k++) { I.pressKey("1"); AtParagraphs.selectTextByOffset(k + 1, 0, 3); I.pressKey("u"); I.pressKey("Escape"); } I.pressKey("1"); AtParagraphs.selectTextByOffset(maxUsage + 1, 0, 3); // Verify that exactly maxUsage regions exist (not maxUsage + 1) AtOutliner.seeRegions(maxUsage); // Additional validation: Check that the 4th region creation was prevented I.executeScript((maxUsageValue) => { const regions = window.Htx.annotation?.regionStore?.regions || []; const label1Regions = regions.filter((r) => r.hasLabel("Label_1")); if (label1Regions.length > maxUsageValue) { throw new Error( `Max usage validation failed: ${label1Regions.length} Label_1 regions created, but max is ${maxUsageValue}`, ); } }, maxUsage); }, ); Data(maxUsageDataTable).Scenario( "Max usages of labels in Timeseries on region creation", async ({ I, LabelStudio, AtOutliner, AtTimeSeries, Modals, current }) => { const { maxUsage } = current; I.amOnPage("/"); LabelStudio.init({ config: ` `, data: require("../examples/data/sample-sin.json"), }); AtOutliner.seeRegions(0); LabelStudio.waitForObjectsReady(); 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); Modals.seeWarning(`You can't use Label_1 more than ${maxUsage} time(s)`); }, );