diff --git a/experimenter/experimenter/nimbus_ui/constants.py b/experimenter/experimenter/nimbus_ui/constants.py index 1f23e1a99..db5678a9a 100644 --- a/experimenter/experimenter/nimbus_ui/constants.py +++ b/experimenter/experimenter/nimbus_ui/constants.py @@ -275,6 +275,15 @@ class NimbusUIConstants: "All statistically significant changes that have occurred in the experiment" ) NOTABLE_CHANGES_ABSENT_TEXT = "There are no notable changes in this experiment" + METRIC_ERRORS_TOOLTIP = "No data available" + METRIC_ERRORS_TITLE = "Metric unavailable" + METRIC_ERRORS_TEXT = "Other metrics may not be affected." + METRIC_NO_DATA_TOOLTIP = "Not available yet" + METRIC_NO_DATA_TITLE = "Not available yet" + METRIC_NO_DATA_TEXT = ( + "The analysis window for this metric has not been computed yet. " + "Results will appear once it has." + ) FEATURE_MONITORING_CARD_TITLE = "Feature Monitoring" FEATURE_MONITORING_OPEN_DASHBOARD_TEXT = "Open in Grafana" FEATURE_MONITORING_DASHBOARD_INFO = ( diff --git a/experimenter/experimenter/nimbus_ui/templates/nimbus_experiments/results-inner.html b/experimenter/experimenter/nimbus_ui/templates/nimbus_experiments/results-inner.html index a5bd292b3..7ed798e13 100644 --- a/experimenter/experimenter/nimbus_ui/templates/nimbus_experiments/results-inner.html +++ b/experimenter/experimenter/nimbus_ui/templates/nimbus_experiments/results-inner.html @@ -272,9 +272,9 @@

{{ area }}

{{ metric.friendly_name }} - + title="{% if metric.has_errors %}{{ NimbusUIConstants.METRIC_ERRORS_TOOLTIP }}{% elif not metric.has_data %}{{ NimbusUIConstants.METRIC_NO_DATA_TOOLTIP }}{% elif metric.overall_change == 'positive' %}Positive changes{% elif metric.overall_change == 'negative' %}Negative changes{% elif metric.overall_change == 'mixed' %}Mixed changes{% elif metric.overall_change == 'neutral' %}No notable change{% endif %}"> {% if not forloop.last %}·{% endif %} @@ -389,14 +389,24 @@

{{ area }}

{% if metric.has_errors or not metric.has_data %} {% if forloop.parentloop.parentloop.first %}
-
- -

Metric unavailable

-

Other metrics may not be affected.

- Contact Experimenter Support -
+ {% if metric.has_errors %} +
+ +

{{ NimbusUIConstants.METRIC_ERRORS_TITLE }}

+

{{ NimbusUIConstants.METRIC_ERRORS_TEXT }}

+ Contact Experimenter Support +
+ {% else %} +
+ +

{{ NimbusUIConstants.METRIC_NO_DATA_TITLE }}

+

{{ NimbusUIConstants.METRIC_NO_DATA_TEXT }}

+
+ {% endif %}
{% else %} diff --git a/experimenter/experimenter/nimbus_ui/tests/test_views.py b/experimenter/experimenter/nimbus_ui/tests/test_views.py index 376dd2132..930fc35c8 100644 --- a/experimenter/experimenter/nimbus_ui/tests/test_views.py +++ b/experimenter/experimenter/nimbus_ui/tests/test_views.py @@ -3864,6 +3864,94 @@ def test_results_template_fragment_used_with_htmx(self): self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "nimbus_experiments/results-fragment.html") + def create_experiment_with_kpi_results(self, errors): + experiment = NimbusExperimentFactory.create_with_lifecycle( + NimbusExperimentFactory.Lifecycles.ENDING_APPROVE_APPROVE, + application=NimbusExperiment.Application.DESKTOP, + primary_outcomes=[], + secondary_outcomes=[], + is_rollout=False, + ) + experiment.delete_branches() + experiment.reference_branch = NimbusBranchFactory.create( + experiment=experiment, name="Control", slug="control" + ) + NimbusBranchFactory.create( + experiment=experiment, name="Treatment A", slug="treatment-a" + ) + empty_metric = { + "absolute": {"all": [], "first": {}}, + "difference": {"control": {"all": [], "first": {}}}, + "relative_uplift": {"control": {"all": [], "first": {}}}, + } + branch_data = { + "branch_data": { + "other_metrics": {NimbusConstants.RETENTION: empty_metric}, + } + } + experiment.results_data = { + "v3": { + "metadata": { + "metrics": { + NimbusConstants.RETENTION: {"friendly_name": "Retention"}, + } + }, + "errors": errors, + "overall": { + "enrollments": { + "all": { + "control": branch_data, + "treatment-a": branch_data, + } + } + }, + } + } + experiment.save() + return experiment + + def test_results_view_renders_error_state_for_metric_with_errors(self): + experiment = self.create_experiment_with_kpi_results( + { + NimbusConstants.RETENTION: [ + {"analysis_basis": "enrollments", "segment": "all"} + ] + } + ) + + response = self.client.get( + reverse( + "nimbus-ui-results", + kwargs={"slug": experiment.slug}, + query={"reference_branch": "control"}, + ), + ) + + self.assertEqual(response.status_code, 200) + self.assertContains(response, "fa-triangle-exclamation text-warning") + self.assertContains(response, NimbusUIConstants.METRIC_ERRORS_TOOLTIP) + self.assertContains(response, NimbusUIConstants.METRIC_ERRORS_TITLE) + self.assertContains(response, escape(NimbusUIConstants.METRIC_ERRORS_TEXT)) + self.assertContains(response, "Contact Experimenter Support") + + def test_results_view_renders_no_data_state_for_metric_without_data(self): + experiment = self.create_experiment_with_kpi_results({}) + + response = self.client.get( + reverse( + "nimbus-ui-results", + kwargs={"slug": experiment.slug}, + query={"reference_branch": "control"}, + ), + ) + + self.assertEqual(response.status_code, 200) + self.assertContains(response, "fa-circle-info text-info") + self.assertContains(response, NimbusUIConstants.METRIC_NO_DATA_TOOLTIP) + self.assertContains(response, NimbusUIConstants.METRIC_NO_DATA_TEXT) + self.assertNotContains(response, NimbusUIConstants.METRIC_ERRORS_TITLE) + self.assertNotContains(response, "Contact Experimenter Support") + @parameterized.expand( [ (