summaryrefslogtreecommitdiff
path: root/tests/typ/base
diff options
context:
space:
mode:
Diffstat (limited to 'tests/typ/base')
-rw-r--r--tests/typ/base/assert.typ23
-rw-r--r--tests/typ/base/blind.typ32
-rw-r--r--tests/typ/base/calc.typ117
-rw-r--r--tests/typ/base/collection.typ115
-rw-r--r--tests/typ/base/color.typ63
-rw-r--r--tests/typ/base/data.typ58
-rw-r--r--tests/typ/base/eval.typ52
-rw-r--r--tests/typ/base/string.typ161
-rw-r--r--tests/typ/base/type.typ7
9 files changed, 628 insertions, 0 deletions
diff --git a/tests/typ/base/assert.typ b/tests/typ/base/assert.typ
new file mode 100644
index 00000000..b0c8aafd
--- /dev/null
+++ b/tests/typ/base/assert.typ
@@ -0,0 +1,23 @@
+// Test the `assert` function.
+// Ref: false
+
+---
+#assert(1 + 1 == 2)
+#assert(range(2, 5) == (2, 3, 4))
+#assert(not false)
+
+---
+// Test failing assertions.
+// Error: 9-15 assertion failed
+#assert(1 == 2)
+
+---
+// Test failing assertions.
+// Error: 9-15 expected boolean, found string
+#assert("true")
+
+---
+// Test the `type` function.
+#test(type(1), "integer")
+#test(type(ltr), "direction")
+#test(type(10 / 3), "float")
diff --git a/tests/typ/base/blind.typ b/tests/typ/base/blind.typ
new file mode 100644
index 00000000..17452dec
--- /dev/null
+++ b/tests/typ/base/blind.typ
@@ -0,0 +1,32 @@
+// Test blind text.
+
+---
+// Test basic call.
+#lorem(19)
+
+---
+// Test custom paragraphs with user code.
+#set text(8pt)
+
+{
+ let sentences = lorem(59)
+ .split(".")
+ .filter(s => s != "")
+ .map(s => s + ".")
+
+ let used = 0
+ for s in sentences {
+ if used < 2 {
+ used += 1
+ } else {
+ parbreak()
+ used = 0
+ }
+ s.trim()
+ [ ]
+ }
+}
+
+---
+// Error: 7-9 missing argument: number of words
+#lorem()
diff --git a/tests/typ/base/calc.typ b/tests/typ/base/calc.typ
new file mode 100644
index 00000000..4ccefa22
--- /dev/null
+++ b/tests/typ/base/calc.typ
@@ -0,0 +1,117 @@
+// Test math functions.
+// Ref: false
+
+---
+// Test conversion to numbers.
+#test(int(false), 0)
+#test(int(true), 1)
+#test(int(10), 10)
+#test(int("150"), 150)
+#test(int(10 / 3), 3)
+#test(float(10), 10.0)
+#test(float("31.4e-1"), 3.14)
+#test(type(float(10)), "float")
+
+---
+// Error: 6-10 cannot convert length to integer
+#int(10pt)
+
+---
+// Error: 8-13 cannot convert function to float
+#float(float)
+
+---
+// Error: 6-12 invalid integer
+#int("nope")
+
+---
+// Error: 8-15 invalid float
+#float("1.2.3")
+
+---
+// Test the `abs` function.
+#test(abs(-3), 3)
+#test(abs(3), 3)
+#test(abs(-0.0), 0.0)
+#test(abs(0.0), -0.0)
+#test(abs(-3.14), 3.14)
+#test(abs(50%), 50%)
+#test(abs(-25%), 25%)
+
+---
+// Error: 6-17 expected numeric value, found string
+#abs("no number")
+
+---
+// Error: 6-11 cannot take absolute value of a length
+#abs(-12pt)
+
+---
+// Error: 6-16 cannot take absolute value of a length
+#abs(50% - 12pt)
+
+---
+// Test the `even` and `odd` functions.
+#test(even(2), true)
+#test(odd(2), false)
+#test(odd(-1), true)
+#test(even(-11), false)
+
+---
+// Test the `mod` function.
+#test(mod(1, 1), 0)
+#test(mod(5, 3), 2)
+#test(mod(5, -3), 2)
+#test(mod(22.5, 10), 2.5)
+#test(mod(9, 4.5), 0)
+
+---
+// Error: 9-10 divisor must not be zero
+#mod(5, 0)
+
+---
+// Error: 11-14 divisor must not be zero
+#mod(3.0, 0.0)
+
+---
+// Test the `min` and `max` functions.
+#test(min(2, -4), -4)
+#test(min(3.5, 1e2, -0.1, 3), -0.1)
+#test(max(-3, 11), 11)
+#test(min("hi"), "hi")
+
+---
+// Error: 5-7 missing argument: value
+#min()
+
+---
+// Error: 9-13 cannot compare integer and string
+#min(1, "hi")
+
+---
+// Test the `range` function.
+#test(range(4), (0, 1, 2, 3))
+#test(range(1, 4), (1, 2, 3))
+#test(range(-4, 2), (-4, -3, -2, -1, 0, 1))
+#test(range(10, 5), ())
+#test(range(10, step: 3), (0, 3, 6, 9))
+#test(range(1, 4, step: 1), (1, 2, 3))
+#test(range(1, 8, step: 2), (1, 3, 5, 7))
+#test(range(5, 2, step: -1), (5, 4, 3))
+#test(range(10, 0, step: -3), (10, 7, 4, 1))
+
+---
+// Error: 7-9 missing argument: end
+#range()
+
+---
+// Error: 11-14 expected integer, found float
+#range(1, 2.0)
+
+---
+// Error: 17-22 expected integer, found string
+#range(4, step: "one")
+
+---
+// Error: 18-19 step must not be zero
+#range(10, step: 0)
diff --git a/tests/typ/base/collection.typ b/tests/typ/base/collection.typ
new file mode 100644
index 00000000..46ff97ab
--- /dev/null
+++ b/tests/typ/base/collection.typ
@@ -0,0 +1,115 @@
+// Test collection functions.
+// Ref: false
+
+---
+// Test the `len` method.
+#test(().len(), 0)
+#test(("A", "B", "C").len(), 3)
+#test("Hello World!".len(), 12)
+#test((a: 1, b: 2).len(), 2)
+
+---
+// The the `first` and `last` methods.
+#test(().first(), none)
+#test(().last(), none)
+#test((1,).first(), 1)
+#test((2,).last(), 2)
+#test((1, 2, 3).first(), 1)
+#test((1, 2, 3).last(), 3)
+
+---
+// Test the `push` and `pop` methods.
+{
+ let tasks = (a: (1, 2, 3), b: (4, 5, 6))
+ tasks("a").pop()
+ tasks("b").push(7)
+ test(tasks("a"), (1, 2))
+ test(tasks("b"), (4, 5, 6, 7))
+}
+
+---
+// Test the `insert` and `remove` methods.
+{
+ let array = (0, 1, 2, 4, 5)
+ array.insert(3, 3)
+ test(array, range(6))
+ array.remove(1)
+ test(array, (0, 2, 3, 4, 5))
+}
+
+---
+// Error: 2:17-2:19 missing argument: index
+#let numbers = ()
+{ numbers.insert() }
+
+---
+// Test the `slice` method.
+#test((1, 2, 3, 4).slice(2), (3, 4))
+#test(range(10).slice(2, 6), (2, 3, 4, 5))
+#test(range(10).slice(4, count: 3), (4, 5, 6))
+#test(range(10).slice(-5, count: 2), (5, 6))
+#test((1, 2, 3).slice(2, -2), ())
+#test((1, 2, 3).slice(-2, 2), (2,))
+#test((1, 2, 3).slice(-3, 2), (1, 2))
+#test("ABCD".split("").slice(1, -1).join("-"), "A-B-C-D")
+
+---
+// Error: 3-31 array index out of bounds (index: 12, len: 10)
+{ range(10).slice(9, count: 3) }
+
+---
+// Error: 3-25 array index out of bounds (index: -4, len: 3)
+{ (1, 2, 3).slice(0, -4) }
+
+---
+// Test the `position` method.
+#test(("Hi", "❤️", "Love").position(s => s == "❤️"), 1)
+#test(("Bye", "💘", "Apart").position(s => s == "❤️"), none)
+#test(("A", "B", "CDEF", "G").position(v => v.len() > 2), 2)
+
+---
+// Test the `rev` method.
+#test(range(3).rev(), (2, 1, 0))
+
+---
+// Test the `join` method.
+#test(().join(), none)
+#test((1,).join(), 1)
+#test(("a", "b", "c").join(), "abc")
+#test("(" + ("a", "b", "c").join(", ") + ")", "(a, b, c)")
+
+---
+// Error: 2-22 cannot join boolean with boolean
+{(true, false).join()}
+
+---
+// Error: 2-20 cannot join string with integer
+{("a", "b").join(1)}
+
+---
+// Test joining content.
+// Ref: true
+{([One], [Two], [Three]).join([, ], last: [ and ])}.
+
+---
+// Test the `sorted` method.
+#test(().sorted(), ())
+#test(((true, false) * 10).sorted(), (false,) * 10 + (true,) * 10)
+#test(("it", "the", "hi", "text").sorted(), ("hi", "it", "text", "the"))
+#test((2, 1, 3, 10, 5, 8, 6, -7, 2).sorted(), (-7, 1, 2, 2, 3, 5, 6, 8, 10))
+
+---
+// Error: 2-26 cannot order content and content
+{([Hi], [There]).sorted()}
+
+---
+// Test dictionary methods.
+#let dict = (a: 3, c: 2, b: 1)
+#test("c" in dict, true)
+#test(dict.len(), 3)
+#test(dict.values(), (3, 1, 2))
+#test(dict.pairs((k, v) => k + str(v)).join(), "a3b1c2")
+
+{ dict.remove("c") }
+#test("c" in dict, false)
+#test(dict, (a: 3, b: 1))
diff --git a/tests/typ/base/color.typ b/tests/typ/base/color.typ
new file mode 100644
index 00000000..96d76063
--- /dev/null
+++ b/tests/typ/base/color.typ
@@ -0,0 +1,63 @@
+// Test color creation functions.
+// Ref: false
+
+---
+// Compare both ways.
+#test(rgb(0%, 30%, 70%), rgb("004db3"))
+
+// Alpha channel.
+#test(rgb(255, 0, 0, 50%), rgb("ff000080"))
+
+// Test color modification methods.
+#test(rgb(25, 35, 45).lighten(10%), rgb(48, 57, 66))
+#test(rgb(40, 30, 20).darken(10%), rgb(36, 27, 18))
+#test(rgb("#133337").negate(), rgb(236, 204, 200))
+#test(white.lighten(100%), white)
+
+---
+// Test gray color conversion.
+// Ref: true
+#rect(fill: luma(0))
+#rect(fill: luma(80%))
+
+---
+// Test gray color modification.
+#test(luma(20%).lighten(50%), luma(60%))
+#test(luma(80%).darken(20%), luma(63.9%))
+#test(luma(80%).negate(), luma(20%))
+
+---
+// Test CMYK color conversion.
+// Ref: true
+#let c = cmyk(50%, 64%, 16%, 17%)
+#rect(width: 1cm, fill: cmyk(69%, 11%, 69%, 41%))
+#rect(width: 1cm, fill: c)
+#rect(width: 1cm, fill: c.negate())
+
+#for x in range(0, 11) {
+ square(width: 9pt, fill: c.lighten(x * 10%))
+}
+#for x in range(0, 11) {
+ square(width: 9pt, fill: c.darken(x * 10%))
+}
+
+---
+// Error for values that are out of range.
+// Error: 11-14 must be between 0 and 255
+#test(rgb(-30, 15, 50))
+
+---
+// Error: 6-11 string contains non-hexadecimal letters
+#rgb("lol")
+
+---
+// Error: 5-7 missing argument: red component
+#rgb()
+
+---
+// Error: 5-11 missing argument: blue component
+#rgb(0, 1)
+
+---
+// Error: 21-26 expected integer or ratio, found boolean
+#rgb(10%, 20%, 30%, false)
diff --git a/tests/typ/base/data.typ b/tests/typ/base/data.typ
new file mode 100644
index 00000000..96b12ff5
--- /dev/null
+++ b/tests/typ/base/data.typ
@@ -0,0 +1,58 @@
+// Test reading structured data.
+// Ref: false
+
+---
+// Test reading CSV data.
+// Ref: true
+#set page(width: auto)
+#let data = csv("/res/zoo.csv")
+#let cells = data(0).map(strong) + data.slice(1).flatten()
+#table(columns: data(0).len(), ..cells)
+
+---
+// Error: 6-16 file not found (searched at typ/base/nope.csv)
+#csv("nope.csv")
+
+---
+// Error: 6-20 failed to parse csv file: found 3 instead of 2 fields in line 3
+#csv("/res/bad.csv")
+
+---
+// Test reading JSON data.
+#let data = json("/res/zoo.json")
+#test(data.len(), 3)
+#test(data(0).name, "Debby")
+#test(data(2).weight, 150)
+
+---
+// Error: 7-22 failed to parse json file: syntax error in line 3
+#json("/res/bad.json")
+
+---
+// Test reading XML data.
+#let data = xml("/res/data.xml")
+#test(data, ((
+ tag: "data",
+ attrs: (:),
+ children: (
+ "\n ",
+ (tag: "hello", attrs: (name: "hi"), children: ("1",)),
+ "\n ",
+ (
+ tag: "data",
+ attrs: (:),
+ children: (
+ "\n ",
+ (tag: "hello", attrs: (:), children: ("World",)),
+ "\n ",
+ (tag: "hello", attrs: (:), children: ("World",)),
+ "\n ",
+ ),
+ ),
+ "\n",
+ ),
+),))
+
+---
+// Error: 6-20 failed to parse xml file: found closing tag 'data' instead of 'hello' in line 3
+#xml("/res/bad.xml")
diff --git a/tests/typ/base/eval.typ b/tests/typ/base/eval.typ
new file mode 100644
index 00000000..86b1f0c4
--- /dev/null
+++ b/tests/typ/base/eval.typ
@@ -0,0 +1,52 @@
+// Test the `eval` function.
+
+---
+#eval("_Hello" + " World!_")
+
+---
+// Error: 7-13 expected identifier
+#eval("#let")
+
+---
+#set raw(around: none)
+#show it: raw as text("IBM Plex Sans", eval(it.text))
+
+Interacting
+```
+#set text(blue)
+Blue #move(dy: -0.15em)[🌊]
+```
+
+---
+// Error: 7-19 cannot continue outside of loop
+#eval("{continue}")
+
+---
+// Error: 7-33 cannot access file system from here
+#eval("#include \"../coma.typ\"")
+
+---
+// Error: 7-35 cannot access file system from here
+#eval("#image(\"/res/tiger.jpg\")")
+
+---
+// Error: 23-30 cannot access file system from here
+#show it: raw as eval(it.text)
+
+```
+#show strong as image("/res/tiger.jpg")
+*No absolute tiger!*
+```
+
+---
+// Error: 23-30 cannot access file system from here
+#show it: raw as eval(it.text)
+
+```
+#show emph as image("../../res/giraffe.jpg")
+_No relative giraffe!_
+```
+
+---
+// Error: 7-16 expected comma
+#eval("{(1 2)}")
diff --git a/tests/typ/base/string.typ b/tests/typ/base/string.typ
new file mode 100644
index 00000000..3104a3ea
--- /dev/null
+++ b/tests/typ/base/string.typ
@@ -0,0 +1,161 @@
+// Test string related methods.
+// Ref: false
+
+---
+// Test conversion to string.
+#test(str(123), "123")
+#test(str(50.14), "50.14")
+#test(str(10 / 3).len() > 10, true)
+#test(repr(ltr), "ltr")
+#test(repr((1, 2, false, )), "(1, 2, false)")
+
+---
+// Error: 6-8 cannot convert content to string
+#str([])
+
+---
+// Test the `slice` method.
+#test("abc".slice(1, 2), "b")
+#test("abc🏡def".slice(2, 7), "c🏡")
+#test("abc🏡def".slice(2, -2), "c🏡d")
+#test("abc🏡def".slice(-3, -1), "de")
+
+---
+// Test the `contains` method.
+#test("abc".contains("b"), true)
+#test("b" in "abc", true)
+#test("1234f".contains(regex("\d")), true)
+#test(regex("\d") in "1234f", true)
+#test("abc".contains("d"), false)
+#test("1234g" in "1234f", false)
+#test("abc".contains(regex("^[abc]$")), false)
+#test("abc".contains(regex("^[abc]+$")), true)
+
+---
+// Test the `starts-with` and `ends-with` methods.
+#test("Typst".starts-with("Ty"), true)
+#test("Typst".starts-with(regex("[Tt]ys")), false)
+#test("Typst".starts-with("st"), false)
+#test("Typst".ends-with("st"), true)
+#test("Typst".ends-with(regex("\d*")), true)
+#test("Typst".ends-with(regex("\d+")), false)
+#test("Typ12".ends-with(regex("\d+")), true)
+
+---
+// Test the `find` and `position` methods.
+#let date = regex("\d{2}:\d{2}")
+#test("Hello World".find("World"), "World")
+#test("Hello World".position("World"), 6)
+#test("It's 12:13 now".find(date), "12:13")
+#test("It's 12:13 now".position(date), 5)
+
+---
+// Test the `match` method.
+#test("Is there a".match("for this?"), none)
+#test(
+ "The time of my life.".match(regex("[mit]+e")),
+ (start: 4, end: 8, text: "time", captures: ()),
+)
+
+// Test the `matches` method.
+#test("Hello there".matches("\d"), ())
+#test("Day by Day.".matches("Day"), (
+ (start: 0, end: 3, text: "Day", captures: ()),
+ (start: 7, end: 10, text: "Day", captures: ()),
+))
+
+// Compute the sum of all timestamps in the text.
+#let timesum(text) = {
+ let time = 0
+ for match in text.matches(regex("(\d+):(\d+)")) {
+ let caps = match.captures
+ time += 60 * int(caps(0)) + int(caps(1))
+ }
+ str(int(time / 60)) + ":" + str(mod(time, 60))
+}
+
+#test(timesum(""), "0:0")
+#test(timesum("2:70"), "3:10")
+#test(timesum("1:20, 2:10, 0:40"), "4:10")
+
+---
+// Test the `replace` method.
+#test("ABC".replace("", "-"), "-A-B-C-")
+#test("Ok".replace("Ok", "Nope", count: 0), "Ok")
+#test("to add?".replace("", "How ", count: 1), "How to add?")
+#test("AB C DEF GH J".replace(" ", ",", count: 2), "AB,C,DEF GH J")
+#test("Walcemo"
+ .replace("o", "k")
+ .replace("e", "o")
+ .replace("k", "e")
+ .replace("a", "e"),
+ "Welcome"
+)
+#test("123".replace(regex("\d$"), "_"), "12_")
+#test("123".replace(regex("\d{1,2}$"), "__"), "1__")
+
+---
+// Test the `trim` method.
+#let str = "Typst, LaTeX, Word, InDesign"
+#let array = ("Typst", "LaTeX", "Word", "InDesign")
+#test(str.split(",").map(s => s.trim()), array)
+#test("".trim(), "")
+#test(" abc ".trim(at: start), "abc ")
+#test(" abc ".trim(at: end, repeat: true), " abc")
+#test(" abc".trim(at: start, repeat: false), "abc")
+#test("aabcaa".trim("a", repeat: false), "abca")
+#test("aabca".trim("a", at: start), "bca")
+#test("aabcaa".trim("a", at: end, repeat: false), "aabca")
+#test("".trim(regex(".")), "")
+#test("123abc456".trim(regex("\d")), "abc")
+#test("123abc456".trim(regex("\d"), repeat: false), "23abc45")
+#test("123a4b5c678".trim(regex("\d"), repeat: true), "a4b5c")
+#test("123a4b5c678".trim(regex("\d"), repeat: false), "23a4b5c67")
+#test("123abc456".trim(regex("\d"), at: start), "abc456")
+#test("123abc456".trim(regex("\d"), at: end), "123abc")
+#test("123abc456".trim(regex("\d+"), at: end, repeat: false), "123abc")
+#test("123abc456".trim(regex("\d{1,2}$"), repeat: false), "123abc4")
+#test("hello world".trim(regex(".")), "")
+
+---
+// Error: 17-21 expected either `start` or `end`
+{"abc".trim(at: left)}
+
+---
+// Test the `split` method.
+#test("abc".split(""), ("", "a", "b", "c", ""))
+#test("abc".split("b"), ("a", "c"))
+#test("a123c".split(regex("\d")), ("a", "", "", "c"))
+#test("a123c".split(regex("\d+")), ("a", "c"))
+
+---
+// Test the `upper` and `lower` functions.
+#let memes = "ArE mEmEs gReAt?";
+#test(lower(memes), "are memes great?")
+#test(upper(memes), "ARE MEMES GREAT?")
+#test(upper("Ελλάδα"), "ΕΛΛΆΔΑ")
+
+---
+// Test integrated lower, upper and symbols.
+// Ref: true
+
+#upper("Abc 8")
+#upper[def]
+
+#lower("SCREAMING MUST BE SILENCED in " + roman(1672) + " years")
+
+#for i in range(9) {
+ symbol(i)
+ [ and ]
+ roman(i)
+ [ for #i]
+ parbreak()
+}
+
+---
+// Error: 8-9 expected string or content, found integer
+#upper(1)
+
+---
+// Error: 9-11 must be at least zero
+#symbol(-1)
diff --git a/tests/typ/base/type.typ b/tests/typ/base/type.typ
new file mode 100644
index 00000000..37cf8623
--- /dev/null
+++ b/tests/typ/base/type.typ
@@ -0,0 +1,7 @@
+// Test the `type` function.
+// Ref: false
+
+---
+#test(type(1), "integer")
+#test(type(ltr), "direction")
+#test(type(10 / 3), "float")