Commit 42d632ef authored by Diegodlh's avatar Diegodlh
Browse files

Fix #T305900: Switch to one-based numbering

parent 2fef3392
......@@ -496,7 +496,7 @@
"config": {
"title": "Configuration",
"description": "One or more comma-separated ranges: \"start(:end)\", \"start:\" or \":end\".",
"options": { "infoText": "Ranges are zero-based, meaning that the first item is item 0" },
"options": { "infoText": "Ranges use one-based numbering, meaning that the first item is item 1" },
"type": "string"
},
"itemwise": {
......
......@@ -304,7 +304,7 @@ describe("Configuration revisions", () => {
transformations: [
{
type: "range",
config: "0",
config: "1",
itemwise: true,
},
{
......@@ -361,7 +361,7 @@ describe("Configuration revisions", () => {
transformations: [
{
type: "range",
config: "0",
config: "1",
itemwise: true,
},
],
......
......@@ -108,7 +108,7 @@ export const fallbackTemplate: FallbackTemplateDefinition = {
"transformations": [
{
"type": "range",
"config": "0",
"config": "1",
"itemwise": false
}
]
......
......@@ -26,7 +26,7 @@ it("applies a translation procedure", () => {
new CitoidSelection("authorFirst"),
];
procedure.transformations = [
new RangeTransformation(undefined, "1,2,0"),
new RangeTransformation(undefined, "2,3,1"),
new JoinTransformation(),
];
return procedure.translate(target).then((output) => {
......@@ -88,7 +88,7 @@ it("constructor optionally skips invalid translation step definitions", () => {
transformations: [
{
type: "range",
config: "0",
config: "1",
itemwise: true,
},
{
......@@ -115,7 +115,7 @@ it("constructor optionally skips invalid translation step definitions", () => {
transformations: [
{
type: "range",
config: "0",
config: "1",
itemwise: true,
},
],
......
......@@ -124,81 +124,81 @@ describe("Date transformation", () => {
});
describe("Range transformation", () => {
const input = ["zero", "one", "two", "three"];
const input = ["one", "two", "three", "four"];
it("selects single item", async () => {
const transformation = new RangeTransformation();
transformation.config = "1";
expect(await transformation.transform(input)).toEqual(["one"]);
transformation.config = "1:1";
expect(await transformation.transform(input)).toEqual(["one"]);
transformation.config = "1,";
expect(await transformation.transform(input)).toEqual(["one"]);
transformation.config = "2";
expect(await transformation.transform(input)).toEqual(["two"]);
transformation.config = "2:2";
expect(await transformation.transform(input)).toEqual(["two"]);
transformation.config = "2,";
expect(await transformation.transform(input)).toEqual(["two"]);
});
it("selects multiple single items", async () => {
const transformation = new RangeTransformation();
transformation.config = "1,0";
expect(await transformation.transform(input)).toEqual(["one", "zero"]);
transformation.config = "2,1";
expect(await transformation.transform(input)).toEqual(["two", "one"]);
});
it("selects single range with start and end", async () => {
const transformation = new RangeTransformation();
transformation.config = "1:2";
expect(await transformation.transform(input)).toEqual(["one", "two"]);
transformation.config = "2:3";
expect(await transformation.transform(input)).toEqual(["two", "three"]);
});
it("selects single range without end", async () => {
const transformation = new RangeTransformation();
transformation.config = "1:";
transformation.config = "2:";
expect(await transformation.transform(input)).toEqual([
"one",
"two",
"three",
"four",
]);
});
it("selects single range without start", async () => {
const transformation = new RangeTransformation();
transformation.config = ":2";
transformation.config = ":3";
expect(await transformation.transform(input)).toEqual([
"zero",
"one",
"two",
"three",
]);
});
it("selects multiple ranges", async () => {
const transformation = new RangeTransformation();
transformation.config = "1:2,:2, 1:";
transformation.config = "2:3,:3, 2:";
expect(await transformation.transform(input)).toEqual([
"one",
"two",
"zero",
"three",
"one",
"two",
"one",
"three",
"two",
"three",
"four",
]);
});
it("tolerates too wide ranges", async () => {
const transformation = new RangeTransformation();
transformation.config = "1:1000";
transformation.config = "2:1000";
expect(await transformation.transform(input)).toEqual([
"one",
"two",
"three",
"four",
]);
transformation.config = "100:1000";
expect(await transformation.transform(input)).toEqual([]);
});
it("tolerates impossible ranges", async () => {
const transformation = new RangeTransformation();
transformation.config = "2:1";
transformation.config = "3:2";
expect(await transformation.transform(input)).toEqual([]);
});
it("ignores empty range configurations", async () => {
const transformation = new RangeTransformation();
transformation.config = ",1:2,,1";
transformation.config = ",2:3,,2";
expect(await transformation.transform(input)).toEqual([
"one",
"two",
"one",
"three",
"two",
]);
transformation.config = "";
expect(await transformation.transform(input)).toEqual([]);
......@@ -220,6 +220,15 @@ describe("Range transformation", () => {
TransformationConfigTypeError
);
});
it("rejects zero indices", async () => {
const transformation = new RangeTransformation();
expect(() => (transformation.config = "0")).toThrow(
TransformationConfigTypeError
);
expect(() => (transformation.config = ":0")).toThrow(
TransformationConfigTypeError
);
});
});
describe("Match transformation", () => {
......
......@@ -192,7 +192,7 @@ function isDateConfig(config: string): config is DateConfig {
}
export class RangeTransformation extends Transformation {
constructor(itemwise = false, range = "0:") {
constructor(itemwise = false, range = "1:") {
super("range", itemwise);
this.config = range;
}
......@@ -201,7 +201,11 @@ export class RangeTransformation extends Transformation {
let ranges = config.replace(/\s/g, "").split(",");
// ignore empty ranges, i.e., successive commas
ranges = ranges.filter((range) => range);
if (ranges.every((range) => /^(\d+(:(\d+)?)?|:\d+)$/.test(range))) {
if (
ranges.every((range) =>
/^([1-9]\d*(:([1-9]\d*)?)?|:[1-9]\d*)$/.test(range)
)
) {
this._config = ranges.join(",");
} else {
throw new TransformationConfigTypeError(this.type, config);
......@@ -241,12 +245,12 @@ export class RangeTransformation extends Transformation {
const [start, end] = rangeString.split(":");
const range: Range = {
// see https://github.com/microsoft/TypeScript/issues/41638
start: start === "" ? 0 : parseInt(start as string),
start: start === "" ? 0 : parseInt(start as string) - 1,
};
if (end === undefined) {
range.end = range.start;
} else if (end !== "") {
range.end = parseInt(end);
range.end = parseInt(end) - 1;
}
ranges.push(range);
return ranges;
......
......@@ -142,7 +142,7 @@ export abstract class TranslationField {
{ type: "citoid", config: "code" },
{ type: "citoid", config: "reporter" },
],
transformations: [{ type: "range", config: "0", itemwise: false }],
transformations: [{ type: "range", config: "1", itemwise: false }],
},
control: false,
},
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment