outliner.test.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. const assert = require("assert");
  2. const { centerOfBbox } = require("./helpers");
  3. Feature("Outliner");
  4. const IMAGE =
  5. "https://htx-pub.s3.us-east-1.amazonaws.com/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg";
  6. Scenario("Basic details", async ({ I, LabelStudio, AtOutliner, AtDetails }) => {
  7. const RESULT_LABELS = ["a", "b", "c"];
  8. const getRectangleRegion = (results) => {
  9. const region = results.find((item) => item.from_name === "rect" && item.type === "rectangle");
  10. assert(region, "Rectangle region not found in serialized results");
  11. return region;
  12. };
  13. const result = [
  14. {
  15. value: {
  16. start: 0,
  17. end: 4,
  18. labels: ["a", "b", "c"],
  19. },
  20. id: "test_t_1",
  21. from_name: "label",
  22. to_name: "text",
  23. type: "labels",
  24. },
  25. {
  26. value: {
  27. start: 5,
  28. end: 6,
  29. labels: [],
  30. },
  31. id: "test_t_2",
  32. from_name: "label",
  33. to_name: "text",
  34. type: "labels",
  35. },
  36. {
  37. value: {
  38. x: 25,
  39. y: 25,
  40. width: 50,
  41. height: 50,
  42. },
  43. id: "test_i_1",
  44. from_name: "rect",
  45. to_name: "img",
  46. type: "rectangle",
  47. },
  48. {
  49. original_width: 2242,
  50. original_height: 2802,
  51. image_rotation: 0,
  52. value: {
  53. x: 25,
  54. y: 25,
  55. width: 50,
  56. height: 50,
  57. rotation: 0,
  58. },
  59. id: "test_i_1",
  60. from_name: "rect",
  61. to_name: "img",
  62. type: "rectangle",
  63. origin: "manual",
  64. },
  65. {
  66. original_width: 2242,
  67. original_height: 2802,
  68. image_rotation: 0,
  69. value: {
  70. x: 25,
  71. y: 25,
  72. width: 50,
  73. height: 50,
  74. rotation: 0,
  75. rating: 4,
  76. },
  77. id: "test_i_1",
  78. from_name: "rating",
  79. to_name: "img",
  80. type: "rating",
  81. origin: "manual",
  82. },
  83. {
  84. original_width: 2242,
  85. original_height: 2802,
  86. image_rotation: 0,
  87. value: {
  88. x: 25,
  89. y: 25,
  90. width: 50,
  91. height: 50,
  92. rotation: 0,
  93. text: ["text", "area"],
  94. },
  95. id: "test_i_1",
  96. from_name: "textarea",
  97. to_name: "img",
  98. type: "textarea",
  99. origin: "manual",
  100. },
  101. {
  102. original_width: 2242,
  103. original_height: 2802,
  104. image_rotation: 0,
  105. value: {
  106. x: 25,
  107. y: 25,
  108. width: 50,
  109. height: 50,
  110. rotation: 0,
  111. choices: ["option 1", "option 2"],
  112. },
  113. id: "test_i_1",
  114. from_name: "choices",
  115. to_name: "img",
  116. type: "choices",
  117. origin: "manual",
  118. },
  119. ];
  120. const fillByPressKeyDown = (keysList) => {
  121. for (const keys of keysList) {
  122. for (let idx = 0; idx < keys.length; idx++) {
  123. I.pressKeyDown(keys[idx]);
  124. }
  125. for (let idx = keys.length - 1; idx >= 0; idx--) {
  126. I.pressKeyUp(keys[idx]);
  127. }
  128. }
  129. };
  130. I.amOnPage("/");
  131. LabelStudio.init({
  132. config: `
  133. <View>
  134. <Text name="text" value="$text"/>
  135. <Labels name="label" toName="text" choice="multiple">
  136. <Label value="a" hotkey="1" />
  137. <Label value="b" hotkey="2" />
  138. <Label value="c" hotkey="3" />
  139. </Labels>
  140. <Image name="img" value="$image"/>
  141. <Rectangle name="rect" toName="img"/>
  142. <Rating name="rating" toName="img" perRegion="true"/>
  143. <Textarea name="textarea" toName="img" perRegion="true"/>
  144. <Choices name="choices" toName="img" perRegion="true">
  145. <Choice value="option 1"/>
  146. <Choice value="option 2"/>
  147. </Choices>
  148. </View>
  149. `,
  150. data: {
  151. text: "Just a text",
  152. image: IMAGE,
  153. },
  154. annotations: [
  155. {
  156. id: "test",
  157. result,
  158. },
  159. ],
  160. });
  161. AtOutliner.seeRegions(3);
  162. LabelStudio.waitForObjectsReady();
  163. I.say("Select text region");
  164. AtOutliner.clickRegion(1);
  165. I.say("Check it's details");
  166. for (const value of RESULT_LABELS) {
  167. AtDetails.seeLabel(value);
  168. }
  169. AtDetails.seeLabels(RESULT_LABELS.length);
  170. AtDetails.seeText("Just");
  171. I.say("Select second text region");
  172. AtOutliner.clickRegion(2);
  173. I.say("Check it's details");
  174. AtDetails.seeLabels(0);
  175. AtDetails.seeText("a");
  176. I.say("Select image region");
  177. AtOutliner.clickRegion(3);
  178. AtDetails.seeFieldWithValue("X", "25");
  179. AtDetails.seeFieldWithValue("H", "50");
  180. I.say("Check perregions displaying");
  181. AtDetails.seeResultRating(4);
  182. AtDetails.seeResultTextarea(["text", "area"]);
  183. AtDetails.seeResultChoices(["option 1", "option 2"]);
  184. I.say("Add new meta and check result");
  185. AtDetails.clickEditMeta();
  186. fillByPressKeyDown([["M"], ["Space"], ["1"], ["Shift", "Enter"], ["M"], ["Space"], ["2"], ["Enter"]]);
  187. AtDetails.seeMeta("M 1");
  188. AtDetails.seeMeta("M 2");
  189. I.say("Add line to meta");
  190. AtDetails.clickMeta();
  191. fillByPressKeyDown([["Shift", "Enter"], ["3"], ["Enter"]]);
  192. AtDetails.seeMeta("3");
  193. AtDetails.dontSeeMeta("23");
  194. I.say("Check that meta is saved correctly");
  195. const resultWithMeta = await LabelStudio.serialize();
  196. const regionWithMeta = getRectangleRegion(resultWithMeta);
  197. assert.deepStrictEqual(regionWithMeta.meta?.text, ["M 1\nM 2\n3"]);
  198. I.say("Remove meta");
  199. AtDetails.clickMeta();
  200. fillByPressKeyDown([["CommandOrControl", "a"], ["Backspace"], ["Enter"]]);
  201. I.say("Check that meta is removed correctly");
  202. const resultWithoutMeta = await LabelStudio.serialize();
  203. const regionWithoutMeta = getRectangleRegion(resultWithoutMeta);
  204. assert.deepStrictEqual(resultWithoutMeta[2].meta, undefined);
  205. }).retry(3);
  206. Scenario("Panels manipulations", async ({ I, LabelStudio, AtPanels }) => {
  207. I.amOnPage("/");
  208. LabelStudio.init({
  209. config: `
  210. <View>
  211. <Text name="text" value="$text"/>
  212. </View>
  213. `,
  214. data: {
  215. text: "Just a text",
  216. },
  217. annotations: [
  218. {
  219. id: "test",
  220. result: [],
  221. },
  222. ],
  223. });
  224. const AtOutlinerPanel = AtPanels.usePanel(AtPanels.PANEL.OUTLINER);
  225. const AtDetailsPanel = AtPanels.usePanel(AtPanels.PANEL.DETAILS);
  226. I.say("See panels at default positions");
  227. AtOutlinerPanel.seePanelAttachedLeft();
  228. AtDetailsPanel.seePanelAttachedRight();
  229. I.say("They should be fully visible");
  230. AtOutlinerPanel.seePanelBody();
  231. AtDetailsPanel.seePanelBody();
  232. I.say("and not collapsed");
  233. AtOutlinerPanel.dontSeeExpandButton();
  234. AtDetailsPanel.dontSeeExpandButton();
  235. I.say("Collapse both panels");
  236. AtOutlinerPanel.collapsePanel();
  237. AtDetailsPanel.collapsePanel();
  238. I.say("Make sure there is no body or collapse button");
  239. AtOutlinerPanel.dontSeePanelBody();
  240. AtDetailsPanel.dontSeePanelBody();
  241. AtOutlinerPanel.dontSeeСollapseButton();
  242. AtDetailsPanel.dontSeeСollapseButton();
  243. AtOutlinerPanel.seeExpandButton();
  244. AtDetailsPanel.seeExpandButton();
  245. I.say("Try to move collapsed panel");
  246. await AtOutlinerPanel.dragPanelBy(400, 0);
  247. I.say("Check that nothing changes");
  248. AtOutlinerPanel.seePanelAttachedLeft();
  249. AtOutlinerPanel.dontSeePanelBody();
  250. AtOutlinerPanel.dontSeeСollapseButton();
  251. I.say("Expand both panels");
  252. AtOutlinerPanel.expandPanel();
  253. AtDetailsPanel.expandPanel();
  254. I.say("Make sure that body and collapse appears");
  255. AtOutlinerPanel.seePanelBody();
  256. AtDetailsPanel.seePanelBody();
  257. AtOutlinerPanel.seeСollapseButton();
  258. AtDetailsPanel.seeСollapseButton();
  259. AtOutlinerPanel.dontSeeExpandButton();
  260. AtDetailsPanel.dontSeeExpandButton();
  261. I.say("Try to drag one panel over another");
  262. await AtOutlinerPanel.dragPanelToElement(AtDetailsPanel.locatePanel());
  263. I.say("It should not affect other panel");
  264. AtDetailsPanel.seePanelAttachedRight();
  265. I.say("But it should detach dragged panel");
  266. AtOutlinerPanel.seePanelDetached();
  267. {
  268. I.say("And x coordinate of panels should be equal due to limitation of moving panel through the border");
  269. const panel1HeaderBbox = await AtOutlinerPanel.grabHeaderBbox();
  270. const panel2HeaderBbox = await AtOutlinerPanel.grabHeaderBbox();
  271. assert.strictEqual(panel1HeaderBbox.x, panel2HeaderBbox.x);
  272. }
  273. I.say("Drag panel somewhere to the center of the screen");
  274. {
  275. let panelBbox = await AtOutlinerPanel.grabPanelBbox();
  276. const panelsContainerBbox = await AtPanels.grabPanelsContainerBbox();
  277. const panelsContainerCenter = centerOfBbox(panelsContainerBbox);
  278. await AtOutlinerPanel.dragPanelTo(panelsContainerCenter.x, panelsContainerCenter.y - panelBbox.height / 2);
  279. I.say("Try to resize panel in all directions");
  280. panelBbox = await AtOutlinerPanel.grabPanelBbox();
  281. {
  282. I.say("drag TopLeft corner");
  283. await AtOutlinerPanel.dragResizerBy(-1, -2, AtOutlinerPanel.resizeTopLeft);
  284. const newPanelBbox = await AtOutlinerPanel.grabPanelBbox();
  285. assert.strictEqual(newPanelBbox.x - panelBbox.x, -1);
  286. assert.strictEqual(newPanelBbox.y - panelBbox.y, -2);
  287. assert.strictEqual(newPanelBbox.width - panelBbox.width, 1);
  288. assert.strictEqual(newPanelBbox.height - panelBbox.height, 2);
  289. panelBbox = newPanelBbox;
  290. }
  291. {
  292. I.say("drag TopRight corner");
  293. await AtOutlinerPanel.dragResizerBy(-1, -2, AtOutlinerPanel.resizeTopRight);
  294. const newPanelBbox = await AtOutlinerPanel.grabPanelBbox();
  295. assert.strictEqual(newPanelBbox.x - panelBbox.x, 0);
  296. assert.strictEqual(newPanelBbox.y - panelBbox.y, -2);
  297. assert.strictEqual(newPanelBbox.width - panelBbox.width, -1);
  298. assert.strictEqual(newPanelBbox.height - panelBbox.height, 2);
  299. panelBbox = newPanelBbox;
  300. }
  301. {
  302. I.say("drag BottomRight corner");
  303. await AtOutlinerPanel.dragResizerBy(3, 5, AtOutlinerPanel.resizeBottomRight);
  304. const newPanelBbox = await AtOutlinerPanel.grabPanelBbox();
  305. assert.strictEqual(newPanelBbox.x - panelBbox.x, 0);
  306. assert.strictEqual(newPanelBbox.y - panelBbox.y, 0);
  307. assert.strictEqual(newPanelBbox.width - panelBbox.width, 3);
  308. assert.strictEqual(newPanelBbox.height - panelBbox.height, 5);
  309. panelBbox = newPanelBbox;
  310. }
  311. {
  312. I.say("drag BottomLeft corner");
  313. await AtOutlinerPanel.dragResizerBy(3, -5, AtOutlinerPanel.resizeBottomLeft);
  314. const newPanelBbox = await AtOutlinerPanel.grabPanelBbox();
  315. assert.strictEqual(newPanelBbox.x - panelBbox.x, 3);
  316. assert.strictEqual(newPanelBbox.y - panelBbox.y, 0);
  317. assert.strictEqual(newPanelBbox.width - panelBbox.width, -3);
  318. assert.strictEqual(newPanelBbox.height - panelBbox.height, -5);
  319. panelBbox = newPanelBbox;
  320. }
  321. {
  322. I.say("drag Top border");
  323. await AtOutlinerPanel.dragResizerBy(10, -10, AtOutlinerPanel.resizeTop, 2);
  324. const newPanelBbox = await AtOutlinerPanel.grabPanelBbox();
  325. assert.strictEqual(newPanelBbox.x - panelBbox.x, 0);
  326. assert.strictEqual(newPanelBbox.y - panelBbox.y, -10);
  327. assert.strictEqual(newPanelBbox.width - panelBbox.width, 0);
  328. assert.strictEqual(newPanelBbox.height - panelBbox.height, 10);
  329. panelBbox = newPanelBbox;
  330. }
  331. {
  332. I.say("drag Right border");
  333. await AtOutlinerPanel.dragResizerBy(100, -7, AtOutlinerPanel.resizeRight, 2);
  334. const newPanelBbox = await AtOutlinerPanel.grabPanelBbox();
  335. assert.strictEqual(newPanelBbox.x - panelBbox.x, 0);
  336. assert.strictEqual(newPanelBbox.y - panelBbox.y, 0);
  337. assert.strictEqual(newPanelBbox.width - panelBbox.width, 100);
  338. assert.strictEqual(newPanelBbox.height - panelBbox.height, 0);
  339. panelBbox = newPanelBbox;
  340. }
  341. {
  342. I.say("drag Bottom border");
  343. await AtOutlinerPanel.dragResizerBy(11, 11, AtOutlinerPanel.resizeBottom);
  344. const newPanelBbox = await AtOutlinerPanel.grabPanelBbox();
  345. assert.strictEqual(newPanelBbox.x - panelBbox.x, 0);
  346. assert.strictEqual(newPanelBbox.y - panelBbox.y, 0);
  347. assert.strictEqual(newPanelBbox.width - panelBbox.width, 0);
  348. assert.strictEqual(newPanelBbox.height - panelBbox.height, 11);
  349. panelBbox = newPanelBbox;
  350. }
  351. {
  352. I.say("drag Left border");
  353. await AtOutlinerPanel.dragResizerBy(7, -7, AtOutlinerPanel.resizeLeft);
  354. const newPanelBbox = await AtOutlinerPanel.grabPanelBbox();
  355. assert.strictEqual(newPanelBbox.x - panelBbox.x, 7);
  356. assert.strictEqual(newPanelBbox.y - panelBbox.y, 0);
  357. assert.strictEqual(newPanelBbox.width - panelBbox.width, -7);
  358. assert.strictEqual(newPanelBbox.height - panelBbox.height, 0);
  359. panelBbox = newPanelBbox;
  360. }
  361. {
  362. I.say("Check maximal size restriction");
  363. await AtOutlinerPanel.dragResizerBy(-1000, -1000, AtOutlinerPanel.resizeTopLeft);
  364. const newPanelBbox = await AtOutlinerPanel.grabPanelBbox();
  365. assert(newPanelBbox.x - panelBbox.x < 500);
  366. assert(newPanelBbox.y - panelBbox.y < 500);
  367. assert(newPanelBbox.width - panelBbox.width > -500);
  368. assert(newPanelBbox.height - panelBbox.height > -500);
  369. panelBbox = newPanelBbox;
  370. }
  371. {
  372. I.say("Check minimal size restriction");
  373. await AtOutlinerPanel.dragResizerBy(panelBbox.width, panelBbox.height + 50, AtOutlinerPanel.resizeTopLeft);
  374. const newPanelBbox = await AtOutlinerPanel.grabPanelBbox();
  375. assert(newPanelBbox.width > 100);
  376. assert(newPanelBbox.height > 100);
  377. assert(newPanelBbox.x < panelBbox.x + panelBbox.width - 100);
  378. assert(newPanelBbox.y < panelBbox.y + panelBbox.height - 100);
  379. panelBbox = newPanelBbox;
  380. }
  381. }
  382. I.say("Move details to the left socket");
  383. await AtDetailsPanel.dragPanelToLeftSocket();
  384. AtDetailsPanel.seePanelAttachedLeft();
  385. {
  386. I.say("Move outliner to the right socket by moving to the left (check that there is some gap)");
  387. const panelBbox = await AtOutlinerPanel.grabPanelBbox();
  388. const panelContainerWidth = await AtOutlinerPanel.grabPanelsContainerBbox("width");
  389. const shiftX = 50;
  390. await AtOutlinerPanel.dragPanelTo(panelContainerWidth - panelBbox.width / 2 - shiftX, panelBbox.y);
  391. AtOutlinerPanel.seePanelDetached();
  392. await AtOutlinerPanel.dragResizerBy(shiftX, 0, AtOutlinerPanel.resizeRight);
  393. AtOutlinerPanel.seePanelDetached();
  394. await AtOutlinerPanel.dragPanelBy(-5, 0);
  395. AtOutlinerPanel.seePanelAttachedRight();
  396. }
  397. I.say("Attached panels should be resizable");
  398. {
  399. const panelBbox = await AtOutlinerPanel.grabPanelBbox();
  400. await AtOutlinerPanel.dragResizerBy(-10, 0, AtOutlinerPanel.resizeLeft);
  401. const newPanelBbox = await AtOutlinerPanel.grabPanelBbox();
  402. assert(newPanelBbox.width - panelBbox.width, 10);
  403. }
  404. {
  405. const panelBbox = await AtDetailsPanel.grabPanelBbox();
  406. await AtDetailsPanel.dragResizerBy(10, 0, AtOutlinerPanel.resizeRight);
  407. const newPanelBbox = await AtDetailsPanel.grabPanelBbox();
  408. assert(newPanelBbox.width - panelBbox.width, 10);
  409. }
  410. I.say("Collapse is still working");
  411. AtOutlinerPanel.collapsePanel();
  412. AtOutlinerPanel.dontSeePanelBody();
  413. AtOutlinerPanel.dontSeeСollapseButton();
  414. AtOutlinerPanel.seeExpandButton();
  415. I.say("Drag panel somewhere to the center of the screen");
  416. {
  417. const panelBbox = await AtDetailsPanel.grabPanelBbox();
  418. const panelsContainerBbox = await AtPanels.grabPanelsContainerBbox();
  419. const panelsContainerCenter = centerOfBbox(panelsContainerBbox);
  420. await AtDetailsPanel.dragPanelTo(panelsContainerCenter.x, panelsContainerCenter.y - panelBbox.height / 2);
  421. AtDetailsPanel.seePanelDetached();
  422. }
  423. I.say("Collapse detached panel");
  424. AtDetailsPanel.collapsePanel();
  425. AtDetailsPanel.dontSeePanelBody();
  426. AtDetailsPanel.dontSeeСollapseButton();
  427. AtDetailsPanel.seeExpandButton();
  428. I.say("Make sure that it is still movable");
  429. await AtDetailsPanel.dragPanelToLeftSocket();
  430. I.say("and attachable");
  431. AtDetailsPanel.seePanelAttachedLeft();
  432. I.say("and expandable");
  433. AtDetailsPanel.expandPanel();
  434. AtDetailsPanel.seePanelBody();
  435. AtDetailsPanel.seeСollapseButton();
  436. AtDetailsPanel.dontSeeExpandButton();
  437. });