Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(replays): Add click.react_component_name as searchable field #62027

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 28 additions & 26 deletions src/sentry/replays/blueprints/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,21 @@ This document is structured by resource with each resource having actions that c

Additionally, you can filter by these hidden fields.

| Field | Type | Description |
| ----------------- | ------------- | -------------------------------------------------------------- |
| click.alt | string | The alt attribute of the HTML element. |
| click.class | array[string] | An array of HTML element classes. |
| click.id | string | The ID of an HTML element. |
| click.label | string | The aria-label attribute of an HTML element. |
| click.role | string | The role of an HTML element. |
| click.tag | string | Valid HTML5 tag name. |
| click.testid | string | The data-testid of an HTML element. (omitted from public docs) |
| click.textContent | string | The text-content of an HTML element. |
| click.title | string | The title attribute of an HTML element. |
| click.selector | string | A valid CSS selector. |
| dead.selector | string | A valid CSS selector. |
| rage.selector | string | A valid CSS selector. |
| Field | Type | Description |
| -------------------- | ------------- | -------------------------------------------------------------- |
| click.alt | string | The alt attribute of the HTML element. |
| click.class | array[string] | An array of HTML element classes. |
| click.id | string | The ID of an HTML element. |
| click.label | string | The aria-label attribute of an HTML element. |
| click.component_name | string | The value of the data-sentry-component attribute. |
| click.role | string | The role of an HTML element. |
| click.tag | string | Valid HTML5 tag name. |
| click.testid | string | The data-testid of an HTML element. (omitted from public docs) |
| click.textContent | string | The text-content of an HTML element. |
| click.title | string | The title attribute of an HTML element. |
| click.selector | string | A valid CSS selector. |
| dead.selector | string | A valid CSS selector. |
| rage.selector | string | A valid CSS selector. |

### Browse Replays [GET]

Expand Down Expand Up @@ -553,18 +554,19 @@ Parameters:

Queryable fields:

| Field | Type | Description |
| ----------------- | ------------- | -------------------------------------------------------------- |
| click.alt | string | The alt attribute of the HTML element. |
| click.class | array[string] | An array of HTML element classes. |
| click.id | string | The ID of an HTML element. |
| click.label | string | The aria-label attribute of an HTML element. |
| click.role | string | The role of an HTML element. |
| click.selector | string | A valid CSS selector. |
| click.tag | string | Valid HTML5 tag name. |
| click.testid | string | The data-testid of an HTML element. (omitted from public docs) |
| click.textContent | string | The text-content of an HTML element. |
| click.title | string | The title attribute of an HTML element. |
| Field | Type | Description |
| -------------------- | ------------- | -------------------------------------------------------------- |
| click.alt | string | The alt attribute of the HTML element. |
| click.class | array[string] | An array of HTML element classes. |
| click.id | string | The ID of an HTML element. |
| click.label | string | The aria-label attribute of an HTML element. |
| click.component_name | string | The value of the data-sentry-component attribute. |
| click.role | string | The role of an HTML element. |
| click.selector | string | A valid CSS selector. |
| click.tag | string | Valid HTML5 tag name. |
| click.testid | string | The data-testid of an HTML element. (omitted from public docs) |
| click.textContent | string | The text-content of an HTML element. |
| click.title | string | The title attribute of an HTML element. |

Queryable fields for rage and dead clicks:

Expand Down
5 changes: 4 additions & 1 deletion src/sentry/replays/lib/selector/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def __init__(self):
self.aria_label: Optional[str] = None
self.classes: List[str] = []
self.id: Optional[str] = None
self.component_name: Optional[str] = None
self.role: Optional[str] = None
self.tag: Optional[str] = None
self.testid: Optional[str] = None
Expand Down Expand Up @@ -79,6 +80,8 @@ def visit_attribute(query: QueryType, attribute: Attrib) -> None:
query.alt = attribute.value
elif attrib == "aria-label":
query.aria_label = attribute.value
elif attrib == "data-sentry-component":
query.component_name = attribute.value
elif attrib == "role":
query.role = attribute.value
elif attrib == "data-testid":
Expand All @@ -90,7 +93,7 @@ def visit_attribute(query: QueryType, attribute: Attrib) -> None:
else:
raise ParseError(
"Invalid attribute specified. Only alt, aria-label, role, data-testid, data-test-id, "
"and title are supported."
"data-sentry-component, and title are supported."
)


Expand Down
1 change: 1 addition & 0 deletions src/sentry/replays/post_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ def _archived_row(replay_id: str, project_id: int) -> dict[str, Any]:
"click_alt": "click.alt",
"click_aria_label": "click.label",
"click_classes": "click.classes",
"click_component_name": "click.component_name",
"click_id": "click.id",
"click_role": "click.role",
"click_tag": "click.tag",
Expand Down
5 changes: 5 additions & 0 deletions src/sentry/replays/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ def _empty_uuids_lambda():
"click.testid": ["click.testid"],
"click.textContent": ["click.text"],
"click.title": ["click.title"],
"click.component_name": ["click.component_name"],
"click.selector": [
"click.alt",
"click.aria_label",
Expand All @@ -537,6 +538,7 @@ def _empty_uuids_lambda():
"click.testid",
"click.text",
"click.title",
"click.component_name",
],
"warning_id": ["warning_ids"],
"info_id": ["info_ids"],
Expand Down Expand Up @@ -670,6 +672,9 @@ def _empty_uuids_lambda():
),
"click.text": Function("groupArray", parameters=[Column("click_text")], alias="click_text"),
"click.title": Function("groupArray", parameters=[Column("click_title")], alias="click_title"),
"click.component_name": Function(
"groupArray", parameters=[Column("click_component_name")], alias="click_component_name"
),
"error_ids": _collect_new_errors(),
"warning_ids": _collect_event_ids("warning_ids", ["warning_id"]),
"info_ids": _collect_event_ids("info_ids", ["info_id", "debug_id"]),
Expand Down
1 change: 1 addition & 0 deletions src/sentry/replays/testutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ def mock_replay_click(
"id": kwargs.pop("id", ""),
"class": kwargs.pop("class_", []),
"text": kwargs.pop("text", ""),
"component_name": kwargs.pop("component_name", ""),
"role": kwargs.pop("role", ""),
"alt": kwargs.pop("alt", ""),
"testid": kwargs.pop("testid", ""),
Expand Down
2 changes: 2 additions & 0 deletions src/sentry/replays/usecases/query/conditions/selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ def search_selector(queries: list[QueryType]) -> Function:
if query.classes:
has_all = Function("hasAll", parameters=[Column("click_class"), query.classes])
cmp_functions.append(equals(has_all, 1))
if query.component_name:
cmp_functions.append(equals(Column("click_component_name"), query.component_name))
if query.id:
cmp_functions.append(equals(Column("click_id"), query.id))
if query.role:
Expand Down
1 change: 1 addition & 0 deletions src/sentry/replays/usecases/query/configs/aggregate.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def array_string_field(column_name: str) -> StringColumnField:
"browser.version": string_field("browser_version"),
"click.alt": click_field("click_alt"),
"click.class": array_click_field("click_class"),
"click.component_name": click_field("click_component_name"),
"click.id": click_field("click_id"),
"click.label": click_field("click_aria_label"),
"click.role": click_field("click_role"),
Expand Down
1 change: 1 addition & 0 deletions src/sentry/replays/usecases/query/configs/scalar.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def string_field(column_name: str) -> StringColumnField:
click_search_config: dict[str, FieldProtocol] = {
"click.alt": string_field("click_alt"),
"click.class": StringColumnField("click_class", parse_str, StringArray),
"click.component_name": string_field("click_component_name"),
"click.id": string_field("click_id"),
"click.label": string_field("click_aria_label"),
"click.role": string_field("click_role"),
Expand Down
11 changes: 10 additions & 1 deletion tests/sentry/replays/test_organization_replay_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def test_get_replays(self):
tag="div",
id="myid",
class_=["class1", "class2"],
component_name="SignUpForm",
role="button",
testid="1",
alt="Alt",
Expand Down Expand Up @@ -139,6 +140,7 @@ def test_get_replays(self):
"click.alt": "Alt",
"click.classes": ["class1", "class2"],
"click.id": "myid",
"click.component_name": "SignUpForm",
"click.role": "button",
"click.tag": "div",
"click.testid": "1",
Expand Down Expand Up @@ -1038,6 +1040,7 @@ def test_get_replays_filter_clicks(self):
tag="div",
id="myid",
class_=["class1", "class2", "class:hover"],
component_name="SignUpForm",
role="button",
testid="1",
alt="Alt",
Expand Down Expand Up @@ -1068,6 +1071,7 @@ def test_get_replays_filter_clicks(self):
"click.class:class3",
"click.id:myid",
"click.label:AriaLabel",
"click.component_name:SignUpForm",
"click.role:button",
"click.tag:div",
"click.tag:button",
Expand All @@ -1077,6 +1081,7 @@ def test_get_replays_filter_clicks(self):
"click.selector:div#myid",
"click.selector:div[alt=Alt]",
"click.selector:div[title=MyTitle]",
"click.selector:div[data-sentry-component=SignUpForm]",
"click.selector:div[data-testid='1']",
"click.selector:div[data-test-id='1']",
"click.selector:div[role=button]",
Expand All @@ -1100,6 +1105,7 @@ def test_get_replays_filter_clicks(self):
"click.class:class4",
"click.id:other",
"click.label:NotAriaLabel",
"click.component_name:NotSignUpForm",
"click.role:form",
"click.tag:header",
"click.testid:2",
Expand Down Expand Up @@ -1140,6 +1146,7 @@ def test_get_replays_click_fields(self):
tag="div",
id="myid",
class_=["class1", "class2"],
component_name="SignUpForm",
role="button",
testid="1",
alt="Alt",
Expand Down Expand Up @@ -1171,6 +1178,7 @@ def test_get_replays_click_fields(self):
"click.alt": "Alt",
"click.classes": ["class1", "class3"],
"click.id": "myid",
"click.component_name": "SignUpForm",
"click.role": "button",
"click.tag": "button",
"click.testid": "1",
Expand All @@ -1182,6 +1190,7 @@ def test_get_replays_click_fields(self):
"click.alt": None,
"click.classes": ["class1", "class2"],
"click.id": "myid",
"click.component_name": None,
"click.role": None,
"click.tag": "div",
"click.testid": None,
Expand Down Expand Up @@ -1254,7 +1263,7 @@ def test_get_replays_filter_clicks_unsupported_attribute_selector(self):
assert response.status_code == 400, query
assert response.content == (
b'{"detail":"Invalid attribute specified. Only alt, aria-label, role, '
b'data-testid, data-test-id, and title are supported."}'
b'data-testid, data-test-id, data-sentry-component, and title are supported."}'
), query

def test_get_replays_filter_clicks_unsupported_operators(self):
Expand Down
Loading