diff --git a/base/src/test/mod.rs b/base/src/test/mod.rs
index 8e1b4ebe1..2c777dd30 100644
--- a/base/src/test/mod.rs
+++ b/base/src/test/mod.rs
@@ -18,6 +18,7 @@ mod test_fn_exact;
mod test_fn_financial;
mod test_fn_formulatext;
mod test_fn_if;
+mod test_fn_left_right_mid_lower;
mod test_fn_maxifs;
mod test_fn_minifs;
mod test_fn_or_xor;
diff --git a/base/src/test/test_fn_left_right_mid_lower.rs b/base/src/test/test_fn_left_right_mid_lower.rs
new file mode 100644
index 000000000..bee5ca08c
--- /dev/null
+++ b/base/src/test/test_fn_left_right_mid_lower.rs
@@ -0,0 +1,113 @@
+#![allow(clippy::unwrap_used)]
+
+use crate::test::util::new_empty_model;
+
+#[test]
+fn test_left() {
+ let mut model = new_empty_model();
+ model._set("A1", "Hello");
+ model._set("B1", "=LEFT(A1,3)");
+ model._set("B2", "=LEFT(A1)");
+ model._set("B3", "=LEFT(A1,0)");
+ model.evaluate();
+ assert_eq!(model._get_text("B1"), *"Hel");
+ assert_eq!(model._get_text("B2"), *"H");
+ assert_eq!(model._get_text("B3"), "");
+}
+
+#[test]
+fn test_right() {
+ let mut model = new_empty_model();
+ model._set("A1", "Hello");
+ model._set("B1", "=RIGHT(A1,2)");
+ model._set("B2", "=RIGHT(A1)");
+ model._set("B3", "=RIGHT(A1,0)");
+ model.evaluate();
+ assert_eq!(model._get_text("B1"), *"lo");
+ assert_eq!(model._get_text("B2"), *"o");
+ assert_eq!(model._get_text("B3"), "");
+}
+
+#[test]
+fn test_mid() {
+ let mut model = new_empty_model();
+ model._set("A1", "Hello");
+ model._set("B1", "=MID(A1,2,3)");
+ model._set("B2", "=MID(A1,1,2)");
+ model._set("B3", "=MID(A1,1,0)");
+ model.evaluate();
+ assert_eq!(model._get_text("B1"), *"ell");
+ assert_eq!(model._get_text("B2"), *"He");
+ assert_eq!(model._get_text("B3"), "");
+}
+
+#[test]
+fn test_lower() {
+ let mut model = new_empty_model();
+ model._set("A1", "Hello WORLD");
+ model._set("B1", "=LOWER(A1)");
+ model._set("B2", "=LOWER(\"TEST\")");
+ model.evaluate();
+ assert_eq!(model._get_text("B1"), *"hello world");
+ assert_eq!(model._get_text("B2"), *"test");
+}
+
+#[test]
+fn test_boundary_conditions() {
+ let mut model = new_empty_model();
+ model._set("A1", "Test");
+
+ model._set("B1", "=LEFT(A1,100)");
+ model._set("B2", "=RIGHT(A1,100)");
+ model._set("B3", "=MID(A1,1,100)");
+ model._set("B4", "=MID(A1,10,5)");
+ model._set("B5", "=MID(A1,3,10)");
+
+ model.evaluate();
+ assert_eq!(model._get_text("B1"), "Test");
+ assert_eq!(model._get_text("B2"), "Test");
+ assert_eq!(model._get_text("B3"), "Test");
+ assert_eq!(model._get_text("B4"), "");
+ assert_eq!(model._get_text("B5"), "st");
+}
+
+#[test]
+fn test_invalid_parameters() {
+ let mut model = new_empty_model();
+ model._set("A1", "Hello");
+
+ model._set("B1", "=LEFT(A1,-1)");
+ model._set("B2", "=RIGHT(A1,-1)");
+ model._set("B3", "=MID(A1,-1,3)");
+ model._set("B4", "=MID(A1,0,3)");
+ model._set("B5", "=MID(A1,2,-1)");
+
+ model.evaluate();
+ assert_eq!(model._get_text("B1"), "#VALUE!");
+ assert_eq!(model._get_text("B2"), "#VALUE!");
+ assert_eq!(model._get_text("B3"), "#VALUE!");
+ assert_eq!(model._get_text("B4"), "#VALUE!");
+ assert_eq!(model._get_text("B5"), "#VALUE!");
+}
+
+#[test]
+fn test_empty_strings() {
+ let mut model = new_empty_model();
+ model._set("A1", "");
+ model._set("A2", " Space ");
+
+ model._set("B1", "=LEFT(A1,5)");
+ model._set("B2", "=RIGHT(A1,5)");
+ model._set("B3", "=MID(A1,1,5)");
+ model._set("B4", "=LOWER(A1)");
+ model._set("B5", "=LEFT(A2,2)");
+ model._set("B6", "=LOWER(A2)");
+
+ model.evaluate();
+ assert_eq!(model._get_text("B1"), "");
+ assert_eq!(model._get_text("B2"), "");
+ assert_eq!(model._get_text("B3"), "");
+ assert_eq!(model._get_text("B4"), "");
+ assert_eq!(model._get_text("B5"), " ");
+ assert_eq!(model._get_text("B6"), " space ");
+}
diff --git a/docs/src/functions/text.md b/docs/src/functions/text.md
index 4217d90d6..4b714726b 100644
--- a/docs/src/functions/text.md
+++ b/docs/src/functions/text.md
@@ -25,33 +25,33 @@ You can track the progress in this [GitHub issue](https://github.com/ironcalc/Ir
| FIND | | – |
| FINDB | | – |
| FIXED | | – |
-| LEFT | | – |
+| LEFT | | [LEFT](text/left) |
| LEFTB | | – |
| LEN | | – |
| LENB | | – |
-| LOWER | | – |
-| MID | | – |
+| LOWER | | [LOWER](text/lower) |
+| MID | | [MID](text/mid) |
| MIDB | | – |
| NUMBERVALUE | | – |
| PHONETIC | | – |
| PROPER | | – |
| REPLACE | | – |
| REPLACEB | | – |
-| REPT | | – |
-| RIGHT | | – |
+| REPT | | [REPT](text/rept) |
+| RIGHT | | [RIGHT](text/right) |
| RIGHTB | | – |
-| SEARCH | | – |
+| SEARCH | | [SEARCH](text/search) |
| SEARCHB | | – |
-| SUBSTITUTE | | – |
-| T | | – |
-| TEXT | | – |
-| TEXTAFTER | | – |
-| TEXTBEFORE | | – |
-| TEXTJOIN | | – |
+| SUBSTITUTE | | [SUBSTITUTE](text/substitute) |
+| T | | [T](text/t) |
+| TEXT | | [TEXT](text/text) |
+| TEXTAFTER | | [TEXTAFTER](text/textafter) |
+| TEXTBEFORE | | [TEXTBEFORE](text/textbefore) |
+| TEXTJOIN | | [TEXTJOIN](text/textjoin) |
| TEXTSPLIT | | – |
-| TRIM | | – |
+| TRIM | | [TRIM](text/trim) |
| UNICHAR | | – |
-| UNICODE | | – |
-| UPPER | | – |
-| VALUE | | – |
-| VALUETOTEXT | | – |
+| UNICODE | | [UNICODE](text/unicode) |
+| UPPER | | [UPPER](text/upper) |
+| VALUE | | [VALUE](text/value) |
+| VALUETOTEXT | | [VALUETOTEXT](text/valuetotext) |
diff --git a/docs/src/functions/text/left.md b/docs/src/functions/text/left.md
new file mode 100644
index 000000000..97d362025
--- /dev/null
+++ b/docs/src/functions/text/left.md
@@ -0,0 +1,11 @@
+---
+layout: doc
+outline: deep
+lang: en-US
+---
+
+# LEFT
+
+::: warning
+🚧 This function is implemented but currently lacks detailed documentation. For guidance, you may refer to the equivalent functionality in [Microsoft Excel documentation](https://support.microsoft.com/en-us/office/excel-functions-by-category-5f91f4e9-7b42-46d2-9bd1-63f26a86c0eb).
+:::
\ No newline at end of file
diff --git a/docs/src/functions/text/lower.md b/docs/src/functions/text/lower.md
new file mode 100644
index 000000000..ca03f4f4d
--- /dev/null
+++ b/docs/src/functions/text/lower.md
@@ -0,0 +1,11 @@
+---
+layout: doc
+outline: deep
+lang: en-US
+---
+
+# LOWER
+
+::: warning
+🚧 This function is implemented but currently lacks detailed documentation. For guidance, you may refer to the equivalent functionality in [Microsoft Excel documentation](https://support.microsoft.com/en-us/office/excel-functions-by-category-5f91f4e9-7b42-46d2-9bd1-63f26a86c0eb).
+:::
\ No newline at end of file