Color picker test framework class
diff --git a/lib/streamlit/testing/element_tree.py b/lib/streamlit/testing/element_tree.py
index a9c3018f210d..732983f72829 100644
--- a/lib/streamlit/testing/element_tree.py
+++ b/lib/streamlit/testing/element_tree.py
@@ -27,6 +27,7 @@
 from streamlit.proto.Button_pb2 import Button as ButtonProto
 from streamlit.proto.Checkbox_pb2 import Checkbox as CheckboxProto
 from streamlit.proto.Code_pb2 import Code as CodeProto
+from streamlit.proto.ColorPicker_pb2 import ColorPicker as ColorPickerProto
 from streamlit.proto.Element_pb2 import Element as ElementProto
 from streamlit.proto.Exception_pb2 import Exception as ExceptionProto
 from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
@@ -257,6 +258,64 @@ def set_value(self, v: Any):
 T = TypeVar("T")
 
 
+@dataclass(repr=False)
+class ColorPicker(Element, Widget):
+    _value: str | None
+
+    proto: ColorPickerProto
+    type: str
+    id: str
+    label: str
+    help: str
+    form_id: str
+    disabled: bool
+    key: str | None
+
+    root: ElementTree = field(repr=False)
+
+    def __init__(self, proto: ColorPickerProto, root: ElementTree):
+        self.proto = proto
+        self.root = root
+        self._value = None
+
+        self.type = "color_picker"
+        self.id = proto.id
+        self.label = proto.label
+        self.help = proto.help
+        self.form_id = proto.form_id
+        self.disabled = proto.disabled
+        self.key = user_key_from_widget_id(self.id)
+
+    @property
+    def value(self) -> str:
+        """The currently selected value from the options."""
+        if self._value is not None:
+            return self._value
+        else:
+            state = self.root.session_state
+            assert state
+            return cast(str, state[self.id])
+
+    def widget_state(self) -> WidgetState:
+        """Protobuf message representing the state of the widget, including
+        any interactions that have happened.
+        Should be the same as the frontend would produce for those interactions.
+        """
+        ws = WidgetState()
+        ws.id = self.id
+        ws.string_value = self.value
+        return ws
+
+    def set_value(self, v: str) -> ColorPicker:
+        self._value = v
+        return self
+
+    def pick(self, v: str) -> ColorPicker:
+        if not v.startswith("#"):
+            v = f"#{v}"
+        return self.set_value(v)
+
+
 @dataclass(init=False, repr=False)
 class Radio(Element, Widget, Generic[T]):
     _value: T | None
@@ -1019,6 +1078,10 @@ def get(self, element_type: Literal["text_input"]) -> Sequence[TextInput]:
     def get(self, element_type: Literal["text_area"]) -> Sequence[TextArea]:
         ...
 
+    @overload
+    def get(self, element_type: Literal["color_picker"]) -> Sequence[ColorPicker]:
+        ...
+
     @overload
     def get(self, element_type: Literal["number_input"]) -> Sequence[NumberInput]:
         ...
@@ -1180,6 +1243,8 @@ def parse_tree_from_messages(messages: list[ForwardMsg]) -> ElementTree:
                 new_node = NumberInput(elt.number_input, root=root)
             elif elt.WhichOneof("type") == "code":
                 new_node = Code(elt.code, root=root)
+            elif elt.WhichOneof("type") == "color_picker":
+                new_node = ColorPicker(elt.color_picker, root=root)
             else:
                 new_node = Element(elt, root=root)
         elif delta.WhichOneof("type") == "add_block":
diff --git a/lib/tests/streamlit/testing/element_tree_test.py b/lib/tests/streamlit/testing/element_tree_test.py
index e6376a37cce1..61363b3f93f6 100644
--- a/lib/tests/streamlit/testing/element_tree_test.py
+++ b/lib/tests/streamlit/testing/element_tree_test.py
@@ -509,3 +509,24 @@ def test_value(self):
         )
         assert sr4.get("number_input")[0].value == -10
         assert sr4.get("number_input")[1].value == -1.0
+
+
+class ColorPickerTest(InteractiveScriptTests):
+    def test_value(self):
+        script = self.script_from_string(
+            "color_picker.py",
+            """
+            import streamlit as st
+
+            st.color_picker("what is your favorite color?")
+            st.color_picker("short hex", value="#ABC")
+            st.color_picker("invalid", value="blue")
+            """,
+        )
+        sr = script.run()
+        assert len(sr.get("color_picker")) == 2
+        assert [c.value for c in sr.get("color_picker")] == ["#000000", "#ABC"]
+        assert "blue" in sr.get("exception")[0].value
+
+        sr2 = sr.get("color_picker")[0].pick("#123456").run()
+        assert sr2.get("color_picker")[0].value == "#123456"

Same deal as every single other one