const assert = require("assert");
Feature("Richtext cases").tag("@regress");
const createConfig = (tag = "Text", granularity = "symbol") => {
return `
<${tag} name="text" value="$text" inline="true" ${granularity ? `granularity="${granularity}"` : ""}/>
`;
};
Scenario("Broken limits", async ({ I, LabelStudio, AtOutliner }) => {
I.amOnPage("/");
LabelStudio.init({
annotations: [
{
id: "test",
result: [
{
id: "a",
from_name: "label",
to_name: "text",
type: "labels",
value: { start: -1, end: 6, labels: ["Label"], text: "Second" },
},
],
},
],
config: createConfig(),
data: { text: "First line\nSecond line" },
});
LabelStudio.waitForObjectsReady();
AtOutliner.seeRegions(1);
AtOutliner.clickRegion(1);
// The potential errors should be caught by `errorsCollector` plugin
});
Scenario("The selection in degenerate cases", async ({ I, LabelStudio, AtOutliner, AtRichText }) => {
I.amOnPage("/");
LabelStudio.init({
annotations: [{ id: "test", result: [] }],
config: createConfig(),
data: { text: "\n\nThird line" },
});
LabelStudio.waitForObjectsReady();
AtOutliner.seeRegions(0);
I.pressKey("1");
await AtRichText.selectTextByGlobalOffset(0, 2);
// The potential errors should be caught by `errorsCollector` plugin
});
Scenario("Exactly 1 word", async ({ I, LabelStudio, AtOutliner, AtRichText }) => {
I.amOnPage("/");
LabelStudio.init({
annotations: [
{
id: "test",
result: [],
},
],
config: createConfig("Text", "word"),
data: { text: "Somé wórds\n\nwíth\n\nmultiline" },
});
LabelStudio.waitForObjectsReady();
AtOutliner.seeRegions(0);
I.pressKey("1");
AtRichText.dblClickOnWord("Somé");
AtOutliner.see("Somé");
AtOutliner.dontSee("Somé wórds");
// The potential errors should be caught by `errorsCollector` plugin
});
Scenario("Trim spaces around the word", async ({ I, LabelStudio, AtOutliner, AtRichText }) => {
I.amOnPage("/");
LabelStudio.init({
annotations: [
{
id: "test",
result: [],
},
],
config: createConfig("Text", "word"),
data: { text: "One two three four five six" },
});
LabelStudio.waitForObjectsReady();
AtOutliner.seeRegions(0);
I.pressKey("1");
AtRichText.dblClickOnWord("four");
AtOutliner.see("four");
I.pressKey("1");
await AtRichText.selectTextByGlobalOffset(3, 8);
AtOutliner.see("two");
const result = await LabelStudio.serialize();
assert.strictEqual(result[0].value.text, "four");
assert.strictEqual(result[1].value.text, "two");
// The potential errors should be caught by `errorsCollector` plugin
});
Scenario("Trim spaces with BRs", async ({ I, LabelStudio, AtOutliner, AtRichText }) => {
I.amOnPage("/");
LabelStudio.init({
annotations: [
{
id: "test",
result: [],
},
],
config: createConfig("Text", "word"),
data: { text: "Three\n\n\nBRs\n\n\ntrim test" },
});
LabelStudio.waitForObjectsReady();
AtOutliner.seeRegions(0);
I.pressKey("1");
await AtRichText.selectTextByGlobalOffset(5, 14);
AtOutliner.see("BRs");
const result = await LabelStudio.serialize();
assert.strictEqual(result[0].value.text, "BRs");
// The potential errors should be caught by `errorsCollector` plugin
});
Scenario("Overlap checks", async ({ I, LabelStudio, AtOutliner, AtRichText }) => {
I.amOnPage("/");
LabelStudio.init({
annotations: [
{
id: "test",
result: [],
},
],
config: createConfig(),
data: { text: "Halfword" },
});
LabelStudio.waitForObjectsReady();
AtOutliner.seeRegions(0);
I.pressKey("1");
await AtRichText.selectTextByGlobalOffset(0, 4);
AtOutliner.see("Half");
I.pressKey("1");
await AtRichText.selectTextByGlobalOffset(4, 8);
I.seeNumberOfElements(AtRichText.locate("span.htx-highlight"), 2);
// The potential errors should be caught by `errorsCollector` plugin
});
Scenario("Non-standard characters in words", async ({ I, LabelStudio, AtOutliner, AtRichText }) => {
I.amOnPage("/");
LabelStudio.init({
annotations: [
{
id: "test",
result: [],
},
],
config: createConfig("Text", "word"),
data: { text: "Somé wórds" },
});
LabelStudio.waitForObjectsReady();
AtOutliner.seeRegions(0);
I.pressKey("1");
await AtRichText.selectTextByGlobalOffset(0, 5);
AtOutliner.seeRegions(1);
AtOutliner.see("Somé");
// The potential errors should be caught by `errorsCollector` plugin
});
Scenario("Should not select words from next line", async ({ I, LabelStudio, AtOutliner, AtRichText }) => {
I.amOnPage("/");
LabelStudio.init({
annotations: [
{
id: "test",
result: [],
},
],
config: createConfig("Text", "word"),
data: { text: "Оne\nline" },
});
LabelStudio.waitForObjectsReady();
AtOutliner.seeRegions(0);
I.pressKey("1");
AtRichText.setSelection(AtRichText.locateText(), 0, AtRichText.locateRoot(), 2);
AtOutliner.seeRegions(1);
AtOutliner.see("Оne");
AtOutliner.dontSee("line");
// The potential errors should be caught by `errorsCollector` plugin
});
Scenario("Trying to select alt attr", async ({ I, LabelStudio, AtOutliner, AtRichText }) => {
I.amOnPage("/");
LabelStudio.init({
annotations: [
{
id: "test",
result: [],
},
],
config: createConfig("HyperText", "word"),
data: { text: "The bad
we got here" },
});
LabelStudio.waitForObjectsReady();
AtOutliner.seeRegions(0);
I.pressKey("1");
AtRichText.dblClickOnElement('img[alt="image"]');
AtOutliner.seeRegions(0);
// The potential errors should be caught by `errorsCollector` plugin
});
Scenario("Neighboring nested regions misplacement", async ({ I, LabelStudio, AtOutliner, AtRichText }) => {
I.amOnPage("/");
LabelStudio.init({
annotations: [
{
id: "test",
result: [
{
id: "Catfish",
from_name: "label",
to_name: "html",
type: "labels",
value: {
start: "/div[1]/text()[1]",
startOffset: 0,
end: "/div[1]/text()[1]",
endOffset: 7,
labels: ["Fish"],
},
},
{
id: "Cat",
from_name: "label",
to_name: "html",
type: "labels",
value: {
start: "/div[1]/text()[1]",
startOffset: 0,
end: "/div[1]/text()[1]",
endOffset: 3,
labels: ["Cat"],
},
},
{
id: "Fish",
from_name: "label",
to_name: "html",
type: "labels",
value: {
start: "/div[1]/text()[1]",
startOffset: 3,
end: "/div[1]/text()[1]",
endOffset: 7,
labels: ["Fish"],
},
},
],
},
],
config: `
`,
data: { text: "
Catfish
" },
});
LabelStudio.waitForObjectsReady();
AtOutliner.seeRegions(3);
within({ frame: ".lsf-richtext__iframe" }, () => {
I.seeElement(locate(".htx-highlight + .htx-highlight").withText("fish"));
});
I.say("Delete last region");
AtOutliner.clickRegion(3);
I.pressKey("Backspace");
I.say("Create this region again manually");
I.pressKey("2");
AtRichText.selectTextByGlobalOffset(3, 7);
AtOutliner.seeRegions(3);
within({ frame: ".lsf-richtext__iframe" }, () => {
I.seeElement(locate(".htx-highlight + .htx-highlight").withText("fish"));
});
});
{
const startBeforeEndParams = new DataTable(["tag", "content", "range"]);
startBeforeEndParams.add([
"Text",
"Beginning Region Ending",
{
start: 16,
end: 10,
},
]);
startBeforeEndParams.add([
"Hypertext",
"Beginning Region Ending
",
{
start: "/div[1]/text()[1]",
startOffset: 16,
end: "/div[1]/text()[1]",
endOffset: 10,
},
]);
startBeforeEndParams.add([
"Hypertext",
"Beginning from to Ending
",
{
start: "/div[1]/span[3]/text()[1]",
startOffset: 2,
end: "/div[1]/span[2]/text()[1]",
endOffset: 0,
},
]);
Data(startBeforeEndParams).Scenario("Start before end problem", async ({ I, LabelStudio, AtOutliner, current }) => {
const { tag, content, range } = current;
I.amOnPage("/");
LabelStudio.init({
annotations: [
{
id: "test",
result: [
{
id: "Region",
from_name: "label",
to_name: "text",
type: "labels",
value: {
...range,
labels: ["Region"],
},
},
],
},
],
config: `
<${tag} name="text" value="$text" />
`,
data: { text: content },
});
LabelStudio.waitForObjectsReady();
AtOutliner.seeRegions(1);
switch (tag.toLowerCase()) {
case "text": {
I.seeElement(locate(".htx-highlight"));
const text = await I.grabHTMLFrom(locate(".htx-highlight"));
assert.strictEqual(text, "", "Region should be collapsed and the text should be empty");
break;
}
case "hypertext": {
within({ frame: ".lsf-richtext__iframe" }, async () => {
const text = await I.grabHTMLFrom(locate(".htx-highlight"));
assert.strictEqual(text, "", "Region should be collapsed and the text should be empty");
});
break;
}
}
});
}