Feature("MIG"); const assert = require("assert"); const rectConfig = ` `; const brushConfig = ` `; const data = { images: [ "https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg", "https://data.heartex.net/open-images/train_0/mini/00155094b7acc33b.jpg", "https://data.heartex.net/open-images/train_0/mini/00133643bbf063a9.jpg", "https://data.heartex.net/open-images/train_0/mini/0061ec6e9576b520.jpg", ], }; const result = [ { original_width: 768, original_height: 576, image_rotation: 0, value: { x: 7.814060788954878, y: 42.05253910374108, width: 11.226011120078905, height: 10.491211550533542, rotation: 0, rectanglelabels: ["Planet"], }, id: "SBHuSbuOoI", from_name: "tag", to_name: "img", type: "rectanglelabels", origin: "manual", item_index: 0, }, { id: "spPCrj0omt", type: "rectanglelabels", value: { x: 40.237685381355924, y: 22.88135593220339, width: 7.878707627118645, height: 18.64406779661017, rotation: 0, rectanglelabels: ["Moonwalker"], }, origin: "manual", to_name: "img", from_name: "tag", item_index: 1, image_rotation: 0, original_width: 768, original_height: 510, }, { original_width: 768, original_height: 576, image_rotation: 0, value: { x: 37.190330463635505, y: 50.85521215886758, width: 15.008743610438549, height: 14.539802110423578, rotation: 0, rectanglelabels: ["Planet"], }, id: "4SkI4GVN4u", from_name: "tag", to_name: "img", type: "rectanglelabels", origin: "manual", item_index: 0, }, ]; Before(async ({ LabelStudio }) => { LabelStudio.setFeatureFlags({ fflag_feat_front_lsdv_4583_multi_image_segmentation_short: true, }); }); Scenario("Image list rendering", async ({ I, LabelStudio, AtImageView }) => { const params = { config: rectConfig, data, annotations: [{ id: 1, result: [] }], }; I.amOnPage("/"); LabelStudio.init(params); LabelStudio.waitForObjectsReady(); await AtImageView.lookForStage(); I.seeElement(`img[src="${data.images[0]}"]`); }); Scenario("Image list with page navigation", async ({ I, AtImageView, LabelStudio }) => { const params = { config: rectConfig, data, annotations: [{ id: 1, result: [] }], }; const prevPageButton = locate(".lsf-pagination__btn.lsf-pagination__btn_arrow-left"); const nextPageButton = locate(".lsf-pagination__btn.lsf-pagination__btn_arrow-right"); I.amOnPage("/"); LabelStudio.init(params); LabelStudio.waitForObjectsReady(); await AtImageView.lookForStage(); I.say("Loading first image"); I.seeElement(`img[src="${data.images[0]}"]`); I.say("Pagination is visible"); I.seeElement(".lsf-pagination"); I.say("The number of pages is correct"); I.see("1 of 4"); I.say("Clicking on the next page"); I.click(nextPageButton); I.say("Loading second image"); I.seeElement(`img[src="${data.images[1]}"]`); I.see("2 of 4"); I.say("Clicking on the previous page"); I.click(prevPageButton); I.seeElement(`img[src="${data.images[0]}"]`); I.see("1 of 4"); }); Scenario("Image list with hotkey navigation", async ({ I, AtImageView, LabelStudio }) => { const params = { config: rectConfig, data, annotations: [{ id: 1, result: [] }], }; I.amOnPage("/"); LabelStudio.init(params); LabelStudio.waitForObjectsReady(); await AtImageView.lookForStage(); I.say("Loading first image"); I.seeElement(`img[src="${data.images[0]}"]`); I.say("Pagination is visible"); I.seeElement(".lsf-pagination"); I.say("The number of pages is correct"); I.see("1 of 4"); await AtImageView.multiImageGoForwardWithHotkey(); I.say("Loading second image"); I.seeElement(`img[src="${data.images[1]}"]`); I.see("2 of 4"); await AtImageView.multiImageGoBackwardWithHotkey(); I.seeElement(`img[src="${data.images[0]}"]`); I.see("1 of 4"); }); Scenario("View All disables MIG pagination", async ({ I, AtImageView, LabelStudio }) => { const params = { config: rectConfig, data, annotations: [ { id: 1, result: [] }, { id: 2, result: [] }, ], additionalInterfaces: ["annotations:view-all"], }; const prevSelector = ".lsf-pagination__btn_arrow-left"; const nextSelector = ".lsf-pagination__btn_arrow-right"; // FFs for a proper interface with View All button LabelStudio.setFeatureFlags({ fflag_feat_front_dev_3873_labeling_ui_improvements_short: true, }); I.amOnPage("/"); LabelStudio.init(params); LabelStudio.waitForObjectsReady(); await AtImageView.lookForStage(); I.say("Move to next page to have a changed state"); I.click(locate(nextSelector)); I.seeElement(`img[src="${data.images[1]}"]`); I.see("2 of 4"); I.say("Enable View All mode"); I.click('[aria-label="Compare all annotations"]'); I.say("Navigation buttons should be disabled"); I.seeElement(locate(`${nextSelector}[class$=disabled]`)); I.seeElement(locate(`${prevSelector}[class$=disabled]`)); I.see("2 of 4"); I.say("Hotkeys for navigation should not work"); await AtImageView.multiImageGoForwardWithHotkey(); I.seeElement(`img[src="${data.images[1]}"]`); I.see("2 of 4"); await AtImageView.multiImageGoBackwardWithHotkey(); I.seeElement(`img[src="${data.images[1]}"]`); I.see("2 of 4"); }); Scenario( "Ensure that results are the same when exporting existing regions", async ({ I, AtImageView, LabelStudio }) => { const params = { config: rectConfig, data, annotations: [{ id: 1, result }], }; I.amOnPage("/"); LabelStudio.init(params); LabelStudio.waitForObjectsReady(); await AtImageView.lookForStage(); I.say("Result must be exactly the same as we're not modifying anything"); await LabelStudio.resultsNotChanged(result); }, ); Scenario("Image list exports correct data", async ({ I, LabelStudio, AtImageView }) => { const params = { config: rectConfig, data, annotations: [{ id: 1, result }], }; I.amOnPage("/"); LabelStudio.init(params); LabelStudio.waitForObjectsReady(); await AtImageView.lookForStage(); AtImageView.multiImageGoForwardWithHotkey(); LabelStudio.waitForObjectsReady(); await AtImageView.lookForStage(); I.seeElement(`img[src="${data.images[1]}"]`); await LabelStudio.resultsNotChanged(result); }); Scenario("Regions are not changes when duplicating an annotation", async ({ I, LabelStudio, AtImageView }) => { const params = { config: rectConfig, data, annotations: [{ id: 1, result }], }; I.amOnPage("/"); LabelStudio.init(params); LabelStudio.waitForObjectsReady(); await AtImageView.lookForStage(); I.say("Attempting to duplicate an annotaion"); I.click('[aria-label="Copy Annotation"]'); LabelStudio.waitForObjectsReady(); await AtImageView.lookForStage(); I.say("Confirm that result is not changed"); await LabelStudio.resultsNotChanged(result); }); Scenario("No errors during brush export in MIG", async ({ I, LabelStudio, AtImageView, AtLabels, AtPanels }) => { const params = { config: brushConfig, data, annotations: [{ id: 1, result: [] }], }; const brushRegionPoints = [ [20, 20], [20, 40], [40, 40], [40, 20], [20, 20], ]; const AtDetailsPanel = AtPanels.usePanel(AtPanels.PANEL.DETAILS); I.amOnPage("/"); LabelStudio.init(params); AtDetailsPanel.collapsePanel(); LabelStudio.waitForObjectsReady(); await AtImageView.lookForStage(); I.say("Create brush regions on the first image"); AtLabels.clickLabel("Moonwalker"); AtImageView.drawThroughPoints(brushRegionPoints); // @todo: We cannot use these hotkeys due to duplicating regions action used the same hotkey // await AtImageView.multiImageGoForwardWithHotkey(); await AtImageView.multiImageGoForward(); I.pressKey("u"); I.say("Create brush regions on the second image"); AtLabels.clickLabel("Planet"); AtImageView.drawThroughPoints(brushRegionPoints); // Brush might not have a chance to finish whatewer it's // doing, so it's safer to wait a little before exporting it I.wait(2); const result = await LabelStudio.serialize(); assert.equal(result.length, 2); });