ner-text.test.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. const { serialize, selectText } = require("./helpers");
  2. const assert = require("assert");
  3. Feature("NERText");
  4. function removeTextFromResult(result) {
  5. return result.map(({ value: { start, end, labels }, ...r }) => ({ ...r, value: { start, end, labels } }));
  6. }
  7. const configSimple = `
  8. <View>
  9. <Labels name="ner" toName="text">
  10. <Label value="Person"></Label>
  11. <Label value="Words"></Label>
  12. </Labels>
  13. <Text name="text" value="$text" />
  14. </View>
  15. `;
  16. const url = "https://htx-pub.s3.amazonaws.com/example.txt";
  17. const configUrl = configSimple.replace('value="$text"', 'valueType="url" value="$url"');
  18. const configUrlSaveText = configUrl.replace('valueType="url"', 'valueType="url" saveTextResult="yes"');
  19. const resultsFromUrl = [
  20. {
  21. id: "Hnyt9sO_RX",
  22. from_name: "ner",
  23. to_name: "text",
  24. type: "labels",
  25. origin: "manual",
  26. value: { start: 0, end: 17, labels: ["Person"], text: "George Washington" },
  27. },
  28. {
  29. id: "tQHRBpvGo0",
  30. from_name: "ner",
  31. to_name: "text",
  32. type: "labels",
  33. origin: "manual",
  34. value: { start: 453, end: 474, labels: ["Words"], text: "Father of His Country" },
  35. },
  36. ];
  37. const resultsFromUrlWithoutText = removeTextFromResult(resultsFromUrl);
  38. const text = `"But I don’t want to go among mad people," Alice remarked.
  39. "Oh, you can’t help that," said the Cat: "we’re all mad here. I’m mad. You’re mad."
  40. "How do you know I’m mad?" said Alice.
  41. "You must be," said the Cat, "or you wouldn’t have come here."
  42. ― Lewis Carroll, Alice in Wonderland`;
  43. const results = [
  44. {
  45. id: "abcdef",
  46. from_name: "ner",
  47. to_name: "text",
  48. type: "labels",
  49. origin: "manual",
  50. value: { start: 175, end: 180, labels: ["Person"], text: "Alice" },
  51. },
  52. {
  53. id: "qwerty",
  54. from_name: "ner",
  55. to_name: "text",
  56. type: "labels",
  57. parentID: "abcdef",
  58. origin: "manual",
  59. value: { start: 1, end: 40, labels: ["Words"], text: "But I don’t want to go among mad people" },
  60. },
  61. ];
  62. const resultsWithoutText = removeTextFromResult(results);
  63. const newResult = {
  64. id: "WpqCf9g_3z",
  65. from_name: "ner",
  66. to_name: "text",
  67. type: "labels",
  68. origin: "manual",
  69. value: { start: 233, end: 237, text: "come", labels: ["Words"] },
  70. };
  71. Scenario("NERText", async ({ I, LabelStudio, AtOutliner, AtTopbar }) => {
  72. const params = {
  73. annotations: [{ id: "TestCmpl", result: results }],
  74. config: configSimple,
  75. data: { text },
  76. };
  77. I.amOnPage("/");
  78. LabelStudio.init(params);
  79. // better to always check the text are on the page,
  80. // so there are no errors and text is displayed correctly;
  81. // text should not be from regions to check the object tag, not the regions list
  82. I.see("Alice remarked");
  83. let result;
  84. // restore saved result and check it back that it didn't change
  85. result = await I.executeScript(serialize);
  86. assert.deepEqual(result, results);
  87. // Create a new annotation to create the same result from scratch
  88. I.click('[aria-label="Annotations List Toggle"]');
  89. I.click('[aria-label="Create Annotation"]');
  90. I.pressKey("2");
  91. I.executeScript(selectText, {
  92. selector: ".lsf-htx-richtext",
  93. rangeStart: 233,
  94. rangeEnd: 237,
  95. });
  96. result = await I.executeScript(serialize);
  97. // id is auto-generated, so use already assigned
  98. newResult.id = result[0].id;
  99. assert.deepEqual(result, [newResult]);
  100. // delete this new annotation
  101. AtTopbar.clickAria("Delete");
  102. I.click("Proceed"); // approve
  103. I.pressKey("1");
  104. I.executeScript(selectText, {
  105. selector: ".lsf-htx-richtext",
  106. rangeStart: 233,
  107. rangeEnd: 237,
  108. });
  109. result = await I.executeScript(serialize);
  110. newResult.id = result[2].id;
  111. newResult.value.labels = ["Person"];
  112. assert.deepEqual(result, [...results, newResult]);
  113. // @todo this hotkey doesn't work. why?
  114. // I.pressKey('R')
  115. I.wait(5);
  116. AtOutliner.clickRegion("Alice");
  117. I.click("Create Relation");
  118. I.click(locate(".htx-highlight").withText("come"));
  119. I.wait(1);
  120. I.click(locate(".htx-highlight").withText("come"));
  121. I.see("Relations (1)");
  122. result = await I.executeScript(serialize);
  123. assert.equal(result.length, 4);
  124. assert.deepEqual(result[0].value, results[0].value);
  125. assert.deepEqual(result[1].value, results[1].value);
  126. assert.equal(result[3].type, "relation");
  127. assert.equal(result[3].from_id, result[0].id);
  128. assert.equal(result[3].to_id, result[2].id);
  129. });
  130. Scenario("NER Text with text field missing", async ({ I, LabelStudio }) => {
  131. const params = {
  132. annotations: [{ id: "TestCmpl", result: resultsWithoutText }],
  133. config: configSimple,
  134. data: { text },
  135. };
  136. I.amOnPage("/");
  137. LabelStudio.init(params);
  138. I.see("Alice remarked");
  139. // restore saved result and check it back that it didn't change
  140. const result = await I.executeScript(serialize);
  141. assert.deepEqual(result, results);
  142. });
  143. // for security reasons text is not saved by default for valueType=url
  144. Scenario("NER Text from url", async ({ I, LabelStudio }) => {
  145. const params = {
  146. annotations: [{ id: "TestCmpl", result: resultsFromUrl }],
  147. config: configUrl,
  148. data: { url },
  149. };
  150. I.amOnPage("/");
  151. LabelStudio.init(params);
  152. // wait for text to be loaded
  153. I.see("American political leader");
  154. // restore saved result and check it back that it didn't change
  155. const result = await I.executeScript(serialize);
  156. assert.deepEqual(result, resultsFromUrlWithoutText);
  157. });
  158. Scenario("NER Text from url with text saved", async ({ I, LabelStudio }) => {
  159. const params = {
  160. annotations: [{ id: "TestCmpl", result: resultsFromUrlWithoutText }],
  161. config: configUrlSaveText,
  162. data: { url },
  163. };
  164. I.amOnPage("/");
  165. LabelStudio.init(params);
  166. // wait for text to be loaded
  167. I.see("American political leader");
  168. // restore saved result and check it back that it didn't change
  169. const result = await I.executeScript(serialize);
  170. assert.deepEqual(result, resultsFromUrl);
  171. });
  172. Scenario("NER Text with SECURE MODE and wrong valueType", async ({ I, LabelStudio }) => {
  173. const params = {
  174. annotations: [{ id: "TestCmpl", result: results }],
  175. config: configSimple,
  176. data: { url },
  177. };
  178. I.amOnPage("/");
  179. I.executeScript(() => {
  180. window.OLD_LS_SECURE_MODE = window.LS_SECURE_MODE;
  181. window.LS_SECURE_MODE = true;
  182. });
  183. LabelStudio.init(params);
  184. I.see("In SECURE MODE"); // error about valueType in secure mode
  185. I.dontSee("American political leader");
  186. I.executeScript(() => {
  187. window.LS_SECURE_MODE = window.OLD_LS_SECURE_MODE;
  188. });
  189. });
  190. Scenario("NER Text with SECURE MODE", async ({ I, LabelStudio }) => {
  191. const params = {
  192. annotations: [{ id: "TestCmpl", result: resultsFromUrl }],
  193. config: configUrl,
  194. data: { url },
  195. };
  196. I.amOnPage("/");
  197. I.executeScript(() => {
  198. window.OLD_LS_SECURE_MODE = window.LS_SECURE_MODE;
  199. window.LS_SECURE_MODE = true;
  200. });
  201. LabelStudio.init(params);
  202. I.waitForElement(".lsf-richtext__line", 60);
  203. I.see("American political leader");
  204. // restore saved result and check it back that it didn't change
  205. const result = await I.executeScript(serialize);
  206. // text should not be saved in secure mode
  207. assert.deepEqual(result, resultsFromUrlWithoutText);
  208. I.executeScript(() => {
  209. window.LS_SECURE_MODE = window.OLD_LS_SECURE_MODE;
  210. });
  211. });
  212. Scenario("NER Text regions in Outliner", async ({ I, LabelStudio }) => {
  213. const params = {
  214. annotations: [{ id: "TestCmpl", result: resultsFromUrl }],
  215. config: configUrl,
  216. data: { url },
  217. };
  218. I.amOnPage("/");
  219. // enabling both flags for New UI, but will work with Outliner one only as well
  220. LabelStudio.setFeatureFlags({
  221. fflag_feat_front_dev_3873_labeling_ui_improvements_short: true,
  222. });
  223. LabelStudio.init(params);
  224. I.waitForElement(".lsf-richtext__line", 60);
  225. I.see("American political leader");
  226. I.seeElement(locate(".lsf-outliner-item").withText(resultsFromUrl[0].value.text));
  227. I.seeElement(locate(".lsf-outliner-item").withText(resultsFromUrl[1].value.text));
  228. });