1namespace ts { 2 import theory = Utils.theory; 3 describe("unittests:: semver", () => { 4 describe("VersionRange", () => { 5 function assertVersionRange(version: string, good: string[], bad: string[]): () => void { 6 return () => { 7 const range = VersionRange.tryParse(version)!; 8 assert(range); 9 for (const g of good) { 10 assert.isTrue(range.test(g), g); 11 } 12 for (const b of bad) { 13 assert.isFalse(range.test(b), b); 14 } 15 }; 16 } 17 it("< works", assertVersionRange("<3.8.0", ["3.6", "3.7"], ["3.8", "3.9", "4.0"])); 18 it("<= works", assertVersionRange("<=3.8.0", ["3.6", "3.7", "3.8"], ["3.9", "4.0"])); 19 it("> works", assertVersionRange(">3.8.0", ["3.9", "4.0"], ["3.6", "3.7", "3.8"])); 20 it(">= works", assertVersionRange(">=3.8.0", ["3.8", "3.9", "4.0"], ["3.6", "3.7"])); 21 22 it("< works with prerelease", assertVersionRange("<3.8.0-0", ["3.6", "3.7"], ["3.8", "3.9", "4.0"])); 23 it("<= works with prerelease", assertVersionRange("<=3.8.0-0", ["3.6", "3.7"], ["3.8", "3.9", "4.0"])); 24 it("> works with prerelease", assertVersionRange(">3.8.0-0", ["3.8", "3.9", "4.0"], ["3.6", "3.7"])); 25 it(">= works with prerelease", assertVersionRange(">=3.8.0-0", ["3.8", "3.9", "4.0"], ["3.6", "3.7"])); 26 }); 27 describe("Version", () => { 28 function assertVersion(version: Version, [major, minor, patch, prerelease, build]: [number, number, number, string[]?, string[]?]) { 29 assert.strictEqual(version.major, major); 30 assert.strictEqual(version.minor, minor); 31 assert.strictEqual(version.patch, patch); 32 assert.deepEqual(version.prerelease, prerelease || emptyArray); 33 assert.deepEqual(version.build, build || emptyArray); 34 } 35 describe("new", () => { 36 it("text", () => { 37 assertVersion(new Version("1.2.3-pre.4+build.5"), [1, 2, 3, ["pre", "4"], ["build", "5"]]); 38 }); 39 it("parts", () => { 40 assertVersion(new Version(1, 2, 3, "pre.4", "build.5"), [1, 2, 3, ["pre", "4"], ["build", "5"]]); 41 assertVersion(new Version(1, 2, 3), [1, 2, 3]); 42 assertVersion(new Version(1, 2), [1, 2, 0]); 43 assertVersion(new Version(1), [1, 0, 0]); 44 }); 45 }); 46 it("toString", () => { 47 assert.strictEqual(new Version(1, 2, 3, "pre.4", "build.5").toString(), "1.2.3-pre.4+build.5"); 48 assert.strictEqual(new Version(1, 2, 3, "pre.4").toString(), "1.2.3-pre.4"); 49 assert.strictEqual(new Version(1, 2, 3, /*prerelease*/ undefined, "build.5").toString(), "1.2.3+build.5"); 50 assert.strictEqual(new Version(1, 2, 3).toString(), "1.2.3"); 51 assert.strictEqual(new Version(1, 2).toString(), "1.2.0"); 52 assert.strictEqual(new Version(1).toString(), "1.0.0"); 53 }); 54 it("compareTo", () => { 55 // https://semver.org/#spec-item-11 56 // > Precedence is determined by the first difference when comparing each of these 57 // > identifiers from left to right as follows: Major, minor, and patch versions are 58 // > always compared numerically. 59 assert.strictEqual(new Version("1.0.0").compareTo(new Version("2.0.0")), Comparison.LessThan); 60 assert.strictEqual(new Version("1.0.0").compareTo(new Version("1.1.0")), Comparison.LessThan); 61 assert.strictEqual(new Version("1.0.0").compareTo(new Version("1.0.1")), Comparison.LessThan); 62 assert.strictEqual(new Version("2.0.0").compareTo(new Version("1.0.0")), Comparison.GreaterThan); 63 assert.strictEqual(new Version("1.1.0").compareTo(new Version("1.0.0")), Comparison.GreaterThan); 64 assert.strictEqual(new Version("1.0.1").compareTo(new Version("1.0.0")), Comparison.GreaterThan); 65 assert.strictEqual(new Version("1.0.0").compareTo(new Version("1.0.0")), Comparison.EqualTo); 66 67 // https://semver.org/#spec-item-11 68 // > When major, minor, and patch are equal, a pre-release version has lower 69 // > precedence than a normal version. 70 assert.strictEqual(new Version("1.0.0").compareTo(new Version("1.0.0-pre")), Comparison.GreaterThan); 71 assert.strictEqual(new Version("1.0.1-pre").compareTo(new Version("1.0.0")), Comparison.GreaterThan); 72 assert.strictEqual(new Version("1.0.0-pre").compareTo(new Version("1.0.0")), Comparison.LessThan); 73 74 // https://semver.org/#spec-item-11 75 // > identifiers consisting of only digits are compared numerically 76 assert.strictEqual(new Version("1.0.0-0").compareTo(new Version("1.0.0-1")), Comparison.LessThan); 77 assert.strictEqual(new Version("1.0.0-1").compareTo(new Version("1.0.0-0")), Comparison.GreaterThan); 78 assert.strictEqual(new Version("1.0.0-2").compareTo(new Version("1.0.0-10")), Comparison.LessThan); 79 assert.strictEqual(new Version("1.0.0-10").compareTo(new Version("1.0.0-2")), Comparison.GreaterThan); 80 assert.strictEqual(new Version("1.0.0-0").compareTo(new Version("1.0.0-0")), Comparison.EqualTo); 81 82 // https://semver.org/#spec-item-11 83 // > identifiers with letters or hyphens are compared lexically in ASCII sort order. 84 assert.strictEqual(new Version("1.0.0-a").compareTo(new Version("1.0.0-b")), Comparison.LessThan); 85 assert.strictEqual(new Version("1.0.0-a-2").compareTo(new Version("1.0.0-a-10")), Comparison.GreaterThan); 86 assert.strictEqual(new Version("1.0.0-b").compareTo(new Version("1.0.0-a")), Comparison.GreaterThan); 87 assert.strictEqual(new Version("1.0.0-a").compareTo(new Version("1.0.0-a")), Comparison.EqualTo); 88 assert.strictEqual(new Version("1.0.0-A").compareTo(new Version("1.0.0-a")), Comparison.LessThan); 89 90 // https://semver.org/#spec-item-11 91 // > Numeric identifiers always have lower precedence than non-numeric identifiers. 92 assert.strictEqual(new Version("1.0.0-0").compareTo(new Version("1.0.0-alpha")), Comparison.LessThan); 93 assert.strictEqual(new Version("1.0.0-alpha").compareTo(new Version("1.0.0-0")), Comparison.GreaterThan); 94 assert.strictEqual(new Version("1.0.0-0").compareTo(new Version("1.0.0-0")), Comparison.EqualTo); 95 assert.strictEqual(new Version("1.0.0-alpha").compareTo(new Version("1.0.0-alpha")), Comparison.EqualTo); 96 97 // https://semver.org/#spec-item-11 98 // > A larger set of pre-release fields has a higher precedence than a smaller set, if all 99 // > of the preceding identifiers are equal. 100 assert.strictEqual(new Version("1.0.0-alpha").compareTo(new Version("1.0.0-alpha.0")), Comparison.LessThan); 101 assert.strictEqual(new Version("1.0.0-alpha.0").compareTo(new Version("1.0.0-alpha")), Comparison.GreaterThan); 102 103 // https://semver.org/#spec-item-11 104 // > Precedence for two pre-release versions with the same major, minor, and patch version 105 // > MUST be determined by comparing each dot separated identifier from left to right until 106 // > a difference is found [...] 107 assert.strictEqual(new Version("1.0.0-a.0.b.1").compareTo(new Version("1.0.0-a.0.b.2")), Comparison.LessThan); 108 assert.strictEqual(new Version("1.0.0-a.0.b.1").compareTo(new Version("1.0.0-b.0.a.1")), Comparison.LessThan); 109 assert.strictEqual(new Version("1.0.0-a.0.b.2").compareTo(new Version("1.0.0-a.0.b.1")), Comparison.GreaterThan); 110 assert.strictEqual(new Version("1.0.0-b.0.a.1").compareTo(new Version("1.0.0-a.0.b.1")), Comparison.GreaterThan); 111 112 // https://semver.org/#spec-item-11 113 // > Build metadata does not figure into precedence 114 assert.strictEqual(new Version("1.0.0+build").compareTo(new Version("1.0.0")), Comparison.EqualTo); 115 }); 116 it("increment", () => { 117 assertVersion(new Version(1, 2, 3, "pre.4", "build.5").increment("major"), [2, 0, 0]); 118 assertVersion(new Version(1, 2, 3, "pre.4", "build.5").increment("minor"), [1, 3, 0]); 119 assertVersion(new Version(1, 2, 3, "pre.4", "build.5").increment("patch"), [1, 2, 4]); 120 }); 121 }); 122 describe("VersionRange", () => { 123 function assertRange(rangeText: string, versionText: string, inRange = true) { 124 const range = new VersionRange(rangeText); 125 const version = new Version(versionText); 126 assert.strictEqual(range.test(version), inRange, `Expected version '${version}' ${inRange ? `to be` : `to not be`} in range '${rangeText}' (${range})`); 127 } 128 theory("comparators", assertRange, [ 129 ["", "1.0.0"], 130 ["*", "1.0.0"], 131 ["1", "1.0.0"], 132 ["1", "2.0.0", false], 133 ["1.0", "1.0.0"], 134 ["1.0", "1.1.0", false], 135 ["1.0.0", "1.0.0"], 136 ["1.0.0", "1.0.1", false], 137 ["1.*", "1.0.0"], 138 ["1.*", "2.0.0", false], 139 ["1.x", "1.0.0"], 140 ["1.x", "2.0.0", false], 141 ["=1", "1.0.0"], 142 ["=1", "1.1.0"], 143 ["=1", "1.0.1"], 144 ["=1.0", "1.0.0"], 145 ["=1.0", "1.0.1"], 146 ["=1.0.0", "1.0.0"], 147 ["=*", "0.0.0"], 148 ["=*", "1.0.0"], 149 [">1", "2"], 150 [">1.0", "1.1"], 151 [">1.0.0", "1.0.1"], 152 [">1.0.0", "1.0.1-pre"], 153 [">*", "0.0.0", false], 154 [">*", "1.0.0", false], 155 [">=1", "1.0.0"], 156 [">=1.0", "1.0.0"], 157 [">=1.0.0", "1.0.0"], 158 [">=1.0.0", "1.0.1-pre"], 159 [">=*", "0.0.0"], 160 [">=*", "1.0.0"], 161 ["<2", "1.0.0"], 162 ["<2.1", "2.0.0"], 163 ["<2.0.1", "2.0.0"], 164 ["<2.0.0", "2.0.0-pre"], 165 ["<*", "0.0.0", false], 166 ["<*", "1.0.0", false], 167 ["<=2", "2.0.0"], 168 ["<=2.1", "2.1.0"], 169 ["<=2.0.1", "2.0.1"], 170 ["<=*", "0.0.0"], 171 ["<=*", "1.0.0"], 172 ]); 173 theory("conjunctions", assertRange, [ 174 [">1.0.0 <2.0.0", "1.0.1"], 175 [">1.0.0 <2.0.0", "2.0.0", false], 176 [">1.0.0 <2.0.0", "1.0.0", false], 177 [">1 >2", "3.0.0"], 178 ]); 179 theory("disjunctions", assertRange, [ 180 [">=1.0.0 <2.0.0 || >=3.0.0 <4.0.0", "1.0.0"], 181 [">=1.0.0 <2.0.0 || >=3.0.0 <4.0.0", "2.0.0", false], 182 [">=1.0.0 <2.0.0 || >=3.0.0 <4.0.0", "3.0.0"], 183 ]); 184 theory("hyphen", assertRange, [ 185 ["1.0.0 - 2.0.0", "1.0.0"], 186 ["1.0.0 - 2.0.0", "2.0.0"], 187 ["1.0.0 - 2.0.0", "3.0.0", false], 188 ]); 189 theory("tilde", assertRange, [ 190 ["~0", "0.0.0"], 191 ["~0", "0.1.0"], 192 ["~0", "0.1.2"], 193 ["~0", "0.1.9"], 194 ["~0", "1.0.0", false], 195 ["~0.1", "0.1.0"], 196 ["~0.1", "0.1.2"], 197 ["~0.1", "0.1.9"], 198 ["~0.1", "0.2.0", false], 199 ["~0.1.2", "0.1.2"], 200 ["~0.1.2", "0.1.9"], 201 ["~0.1.2", "0.2.0", false], 202 ["~1", "1.0.0"], 203 ["~1", "1.2.0"], 204 ["~1", "1.2.3"], 205 ["~1", "1.2.0"], 206 ["~1", "1.2.3"], 207 ["~1", "0.0.0", false], 208 ["~1", "2.0.0", false], 209 ["~1.2", "1.2.0"], 210 ["~1.2", "1.2.3"], 211 ["~1.2", "1.1.0", false], 212 ["~1.2", "1.3.0", false], 213 ["~1.2.3", "1.2.3"], 214 ["~1.2.3", "1.2.9"], 215 ["~1.2.3", "1.1.0", false], 216 ["~1.2.3", "1.3.0", false], 217 ]); 218 theory("caret", assertRange, [ 219 ["^0", "0.0.0"], 220 ["^0", "0.1.0"], 221 ["^0", "0.9.0"], 222 ["^0", "0.1.2"], 223 ["^0", "0.1.9"], 224 ["^0", "1.0.0", false], 225 ["^0.1", "0.1.0"], 226 ["^0.1", "0.1.2"], 227 ["^0.1", "0.1.9"], 228 ["^0.1.2", "0.1.2"], 229 ["^0.1.2", "0.1.9"], 230 ["^0.1.2", "0.0.0", false], 231 ["^0.1.2", "0.2.0", false], 232 ["^0.1.2", "1.0.0", false], 233 ["^1", "1.0.0"], 234 ["^1", "1.2.0"], 235 ["^1", "1.2.3"], 236 ["^1", "1.9.0"], 237 ["^1", "0.0.0", false], 238 ["^1", "2.0.0", false], 239 ["^1.2", "1.2.0"], 240 ["^1.2", "1.2.3"], 241 ["^1.2", "1.9.0"], 242 ["^1.2", "1.1.0", false], 243 ["^1.2", "2.0.0", false], 244 ["^1.2.3", "1.2.3"], 245 ["^1.2.3", "1.9.0"], 246 ["^1.2.3", "1.2.2", false], 247 ["^1.2.3", "2.0.0", false], 248 ]); 249 }); 250 }); 251} 252