Bin
2025-12-16 9e0b2ba2c317b1a86212f24cbae3195ad1f3dbfa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
const { serialize } = require("./helpers");
const Utils = require("../examples/utils");
const examples = [
  require("../examples/audio-regions"),
  require("../examples/audio-paragraphs"),
  require("../examples/image-bboxes"),
  require("../examples/image-ellipses"),
  require("../examples/image-keypoints"),
  require("../examples/image-polygons"),
  require("../examples/ner-url"),
  require("../examples/nested"),
  require("../examples/text-html"),
  require("../examples/text-paragraphs"),
  require("../examples/timeseries-url-indexed"),
];
 
const assert = require("assert");
 
function roundFloats(struct) {
  return JSON.parse(
    JSON.stringify(struct, (key, value) => {
      if (typeof value === "number") {
        return value.toFixed(1);
      }
      return value;
    }),
  );
}
function assertWithTolerance(actual, expected) {
  assert.deepEqual(roundFloats(actual), roundFloats(expected));
}
 
Feature("Smoke test through all the examples");
 
// old audio is broken, so skipping it
examples.slice(1).forEach((example) =>
  Scenario(
    example.title || "Noname smoke test",
    async ({ I, LabelStudio, AtOutliner, AtTopbar, AtDetails, AtAudioView }) => {
      LabelStudio.setFeatureFlags({
        ff_front_dev_2715_audio_3_280722_short: true,
      });
 
      // @todo optional predictions in example
      const { annotations, config, data, result = annotations[0].result } = example;
      const params = { annotations: [{ id: "test", result }], config, data };
      const configTree = Utils.parseXml(config);
      const ids = [];
      // add all unique ids from non-classification results
      // @todo some classifications will be reflected in Results list soon
 
      result.forEach((r) => !ids.includes(r.id) && Object.keys(r.value).length > 1 && ids.push(r.id));
      const count = ids.length;
 
      await I.amOnPage("/");
 
      LabelStudio.init(params);
 
      AtOutliner.seeRegions(count);
 
      let restored;
 
      LabelStudio.waitForObjectsReady();
 
      if (Utils.xmlFindBy(configTree, (node) => node["#name"] === "Audio")) {
        await AtAudioView.waitForAudio();
      }
 
      if (Utils.xmlFindBy(configTree, (node) => ["text", "hypertext"].includes(node["#name"].toLowerCase()))) {
        I.waitForVisible(".lsf-htx-richtext", 5);
      }
 
      I.dontSeeElement(locate(".lsf-errors"));
 
      restored = await I.executeScript(serialize);
      assertWithTolerance(restored, result);
 
      if (count) {
        AtOutliner.clickRegion(1);
        // I.click('Delete Entity') - it founds something by tooltip, but not a button
        // so click the bin button in entity's info block
        AtDetails.clickDeleteRegion();
        AtOutliner.seeRegions(count - 1);
        AtTopbar.clickAria("Reset");
        AtOutliner.seeRegions(count);
        // Reset is undoable
        AtTopbar.clickAria("Undo");
 
        // so after all these manipulations first region should be deleted
        restored = await I.executeScript(serialize);
        assertWithTolerance(
          restored,
          result.filter((r) => r.id !== ids[0]),
        );
      }
      // Click on annotation copy button
      AtTopbar.clickAria("Copy Annotation");
 
      // Check if new annotation exists
      AtTopbar.seeAnnotationAt(2);
 
      // Check for regions count
      AtOutliner.seeRegions(count);
 
      await I.executeScript(async () => {
        // await window.LabelStudio.destroyAll();
        // return true;
      });
    },
  ),
);