This commit is contained in:
Joey Orlando 2024-08-19 16:23:22 -04:00 committed by GitHub
commit 39cb6f2a0b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 66 additions and 248 deletions

View file

@ -144,12 +144,14 @@ jobs:
git clone https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com/grafana/ops-devenv.git
git clone https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com/grafana/gops-labels.git
- name: Tilt CI - standard and expensive E2E tests
- name: Tilt CI - Expensive E2E tests
if: inputs.run-expensive-tests
shell: bash
env:
E2E_TESTS_CMD: "cd ../../grafana-plugin && yarn test:e2e-expensive"
GRAFANA_VERSION: ${{ inputs.grafana_version }}
GF_FEATURE_TOGGLES_ENABLE: "externalServiceAccounts"
ONCALL_API_URL: "http://oncall-dev-engine:8080"
GRAFANA_ADMIN_USERNAME: "irm"
GRAFANA_ADMIN_PASSWORD: "irm"
BROWSERS: ${{ inputs.browsers }}

View file

@ -24,8 +24,8 @@ jobs:
# - 9.3.16
# - 9.4.13
# - 9.5.7
- 10.0.11
- 10.1.7
- 10.3.3
# TODO: fix issues with running e2e tests against Grafana v10.2.x and v10.3.x
# - 10.2.4
# - latest
@ -55,7 +55,7 @@ jobs:
#
- uses: slackapi/slack-github-action@v1.24.0
with:
channel-id: gops-oncall-dev
channel-id: gops-irm-dev
# yamllint disable rule:line-length
payload: |
{

View file

@ -239,9 +239,16 @@ jobs:
end-to-end-tests:
name: Standard e2e tests
uses: ./.github/workflows/e2e-tests.yml
strategy:
matrix:
grafana_version:
- 10.1.7
- 10.3.3
# TODO: fix issues with running e2e tests against Grafana v10.2.x and latest
# - 10.2.4
# - latest
fail-fast: false
with:
# TODO: fix issues with running e2e tests against Grafana v10.2.x and v10.3.x
grafana_version: 10.1.7
# grafana_version: 10.3.3
grafana_version: ${{ matrix.grafana_version }}
run-expensive-tests: false
browsers: "chromium"

View file

@ -1,6 +1,7 @@
load('ext://uibutton', 'cmd_button', 'location', 'text_input', 'bool_input')
load("ext://configmap", "configmap_create")
grafana_url = os.getenv("GRAFANA_URL", "http://grafana:3000")
running_under_parent_tiltfile = os.getenv("TILT_PARENT", "false") == "true"
twilio_values=[
"oncall.twilio.accountSid=" + os.getenv("TWILIO_ACCOUNT_SID", ""),
@ -29,6 +30,14 @@ def plugin_json():
return plugin_file
return 'NOT_A_PLUGIN'
def extra_env():
return {
"GF_APP_URL": grafana_url,
"GF_SERVER_ROOT_URL": grafana_url,
"GF_FEATURE_TOGGLES_ENABLE": "externalServiceAccounts",
"ONCALL_API_URL": "http://oncall-dev-engine:8080"
}
allow_k8s_contexts(["kind-kind"])
@ -83,8 +92,6 @@ def load_grafana():
# The user/pass that you will login to Grafana with
grafana_admin_user_pass = os.getenv("GRAFANA_ADMIN_USER_PASS", "oncall")
grafana_version = os.getenv("GRAFANA_VERSION", "latest")
grafana_url = os.getenv("GRAFANA_URL", "http://grafana:3000")
if 'plugin' in profiles:
k8s_resource(

View file

@ -201,7 +201,6 @@ class AlertGroup(AlertGroupSlackRenderingMixin, EscalationSnapshotMixin, models.
resolved_by_user: typing.Optional["User"]
root_alert_group: typing.Optional["AlertGroup"]
silenced_by_user: typing.Optional["User"]
slack_log_message: typing.Optional["SlackMessage"]
slack_messages: "RelatedManager['SlackMessage']"
users: "RelatedManager['User']"
labels: "RelatedManager['AlertGroupAssociatedLabel']"
@ -396,6 +395,7 @@ class AlertGroup(AlertGroupSlackRenderingMixin, EscalationSnapshotMixin, models.
related_name="wiped_alert_groups",
)
# TODO: drop this column in future release
slack_log_message = models.OneToOneField(
"slack.SlackMessage",
on_delete=models.SET_NULL,

View file

@ -36,10 +36,6 @@ alert_group_action_triggered_signal.connect(
AlertGroupSlackRepresentative.on_alert_group_action_triggered,
)
alert_group_update_log_report_signal.connect(
AlertGroupSlackRepresentative.on_alert_group_update_log_report,
)
alert_group_update_resolution_note_signal.connect(
AlertGroupSlackRepresentative.on_alert_group_update_resolution_note,
)

View file

@ -90,19 +90,7 @@ def on_alert_group_action_triggered_async(log_record_id):
autoretry_for=(Exception,), retry_backoff=True, max_retries=1 if settings.DEBUG else None
)
def on_alert_group_update_log_report_async(alert_group_id):
from apps.alerts.models import AlertGroup
alert_group = AlertGroup.objects.get(pk=alert_group_id)
logger.debug(f"Start on_alert_group_update_log_report for alert_group {alert_group_id}")
organization = alert_group.channel.organization
if alert_group.slack_message and organization.slack_team_identity:
logger.debug(f"Process on_alert_group_update_log_report for alert_group {alert_group_id}")
UpdateLogReportMessageStep = ScenarioStep.get_step("distribute_alerts", "UpdateLogReportMessageStep")
step = UpdateLogReportMessageStep(organization.slack_team_identity, organization)
step.process_signal(alert_group)
else:
logger.debug(f"Drop on_alert_group_update_log_report for alert_group {alert_group_id}")
logger.debug(f"Finish on_alert_group_update_log_report for alert_group {alert_group_id}")
return "Deprecated, will be removed after queue cleanup"
class AlertGroupSlackRepresentative(AlertGroupAbstractRepresentative):
@ -173,32 +161,6 @@ class AlertGroupSlackRepresentative(AlertGroupAbstractRepresentative):
logger.debug(f"SLACK on_alert_group_action_triggered: async {log_record_id} {force_sync}")
on_alert_group_action_triggered_async.apply_async((log_record_id,))
@classmethod
def on_alert_group_update_log_report(cls, **kwargs):
from apps.alerts.models import AlertGroup
alert_group = kwargs["alert_group"]
if isinstance(alert_group, AlertGroup):
alert_group_id = alert_group.pk
else:
alert_group_id = alert_group
try:
alert_group = AlertGroup.objects.get(pk=alert_group_id)
except AlertGroup.DoesNotExist as e:
logger.warning(f"SLACK update log report: alert group {alert_group_id} has been deleted")
raise e
logger.debug(
f"Received alert_group_update_log_report signal in SLACK representative for alert_group {alert_group_id}"
)
if alert_group.notify_in_slack_enabled is False:
logger.debug(f"Skipping alert_group {alert_group_id} since notify_in_slack is disabled")
return
on_alert_group_update_log_report_async.apply_async((alert_group_id,))
@classmethod
def on_alert_group_update_resolution_note(cls, **kwargs):
alert_group = kwargs["alert_group"]

View file

@ -4,7 +4,6 @@ import typing
from datetime import datetime
from django.core.cache import cache
from django.utils import timezone
from apps.alerts.constants import ActionSource
from apps.alerts.incident_appearance.renderers.constants import DEFAULT_BACKUP_TITLE
@ -14,25 +13,17 @@ from apps.api.permissions import RBACPermission
from apps.slack.chatops_proxy_routing import make_private_metadata, make_value
from apps.slack.constants import CACHE_UPDATE_INCIDENT_SLACK_MESSAGE_LIFETIME
from apps.slack.errors import (
SlackAPICantUpdateMessageError,
SlackAPIChannelArchivedError,
SlackAPIChannelInactiveError,
SlackAPIChannelNotFoundError,
SlackAPIError,
SlackAPIInvalidAuthError,
SlackAPIMessageNotFoundError,
SlackAPIRatelimitError,
SlackAPIRestrictedActionError,
SlackAPITokenError,
)
from apps.slack.scenarios import scenario_step
from apps.slack.scenarios.slack_renderer import AlertGroupLogSlackRenderer
from apps.slack.slack_formatter import SlackFormatter
from apps.slack.tasks import (
post_or_update_log_report_message_task,
send_message_to_thread_if_bot_not_in_channel,
update_incident_slack_message,
)
from apps.slack.tasks import send_message_to_thread_if_bot_not_in_channel, update_incident_slack_message
from apps.slack.types import (
Block,
BlockActionType,
@ -95,7 +86,6 @@ class AlertShootingStep(scenario_step.ScenarioStep):
else:
# check if alert group was posted to slack before posting message to thread
if not alert.group.skip_escalation_in_slack:
self._send_log_report_message(alert.group, channel_id)
self._send_message_to_thread_if_bot_not_in_channel(alert.group, channel_id)
else:
# check if alert group was posted to slack before updating its message
@ -208,11 +198,6 @@ class AlertShootingStep(scenario_step.ScenarioStep):
blocks=blocks,
)
def _send_log_report_message(self, alert_group: AlertGroup, channel_id: str) -> None:
post_or_update_log_report_message_task.apply_async(
(alert_group.pk, self.slack_team_identity.pk),
)
def _send_message_to_thread_if_bot_not_in_channel(self, alert_group: AlertGroup, channel_id: str) -> None:
send_message_to_thread_if_bot_not_in_channel.apply_async(
(alert_group.pk, self.slack_team_identity.pk, channel_id),
@ -895,76 +880,6 @@ class DeleteGroupStep(scenario_step.ScenarioStep):
message.delete()
class UpdateLogReportMessageStep(scenario_step.ScenarioStep):
def process_signal(self, alert_group: AlertGroup) -> None:
if alert_group.skip_escalation_in_slack or alert_group.channel.is_rate_limited_in_slack:
return
self.update_log_message(alert_group)
def update_log_message(self, alert_group: AlertGroup) -> None:
slack_message = alert_group.slack_message
if slack_message is None:
logger.info(
f"Cannot update log message for alert_group {alert_group.pk} because SlackMessage doesn't exist"
)
return None
slack_log_message = alert_group.slack_log_message
if slack_log_message is not None:
# prevent too frequent updates
if timezone.now() <= slack_log_message.last_updated + timezone.timedelta(seconds=5):
return
attachments = AlertGroupLogSlackRenderer.render_incident_log_report_for_slack(alert_group)
logger.debug(
f"Update log message for alert_group {alert_group.pk}, slack_log_message {slack_log_message.pk}"
)
try:
self._slack_client.chat_update(
channel=slack_message.channel_id,
text="Alert Group log",
ts=slack_log_message.slack_id,
attachments=attachments,
)
except SlackAPIRatelimitError as e:
if not alert_group.channel.is_rate_limited_in_slack:
alert_group.channel.start_send_rate_limit_message_task(e.retry_after)
except SlackAPIMessageNotFoundError:
alert_group.slack_log_message = None
alert_group.save(update_fields=["slack_log_message"])
except (
SlackAPITokenError,
SlackAPIChannelNotFoundError,
SlackAPIChannelArchivedError,
SlackAPIChannelInactiveError,
SlackAPIInvalidAuthError,
SlackAPICantUpdateMessageError,
):
pass
else:
slack_log_message.last_updated = timezone.now()
slack_log_message.save(update_fields=["last_updated"])
logger.debug(
f"Finished update log message for alert_group {alert_group.pk}, "
f"slack_log_message {slack_log_message.pk}"
)
# check how much time has passed since slack message was created
# to prevent eternal loop of restarting update log message task
elif timezone.now() <= slack_message.created_at + timezone.timedelta(minutes=5):
logger.debug(
f"Update log message failed for alert_group {alert_group.pk}: "
f"log message does not exist yet. Restarting post_or_update_log_report_message_task..."
)
post_or_update_log_report_message_task.apply_async(
(alert_group.pk, self.slack_team_identity.pk, True),
countdown=3,
)
else:
logger.debug(f"Update log message failed for alert_group {alert_group.pk}: " f"log message does not exist.")
STEPS_ROUTING: ScenarioRoute.RoutingSteps = [
{
"payload_type": PayloadType.INTERACTIVE_MESSAGE,

View file

@ -39,17 +39,3 @@ class AlertGroupLogSlackRenderer:
for plan_line in escalation_policies_plan[time]:
result += f"*{humanize.naturaldelta(time)}:* {plan_line}\n"
return result
@staticmethod
def render_incident_log_report_for_slack(alert_group: "AlertGroup"):
attachments = []
past = AlertGroupLogSlackRenderer.render_alert_group_past_log_report_text(alert_group)
future = AlertGroupLogSlackRenderer.render_alert_group_future_log_report_text(alert_group)
text = past + future
if len(text) > 0:
attachments.append(
{
"text": text,
}
)
return attachments

View file

@ -20,7 +20,6 @@ from apps.slack.errors import (
SlackAPITokenError,
SlackAPIUsergroupNotFoundError,
)
from apps.slack.scenarios.scenario_step import ScenarioStep
from apps.slack.utils import (
get_cache_key_update_incident_slack_message,
get_populate_slack_channel_task_id_key,
@ -289,27 +288,7 @@ def populate_slack_user_identities(organization_pk):
autoretry_for=(Exception,), retry_backoff=True, max_retries=1 if settings.DEBUG else None
)
def post_or_update_log_report_message_task(alert_group_pk, slack_team_identity_pk, update=False):
logger.debug(f"Start post_or_update_log_report_message_task for alert_group {alert_group_pk}")
from apps.alerts.models import AlertGroup
from apps.slack.models import SlackTeamIdentity
UpdateLogReportMessageStep = ScenarioStep.get_step("distribute_alerts", "UpdateLogReportMessageStep")
slack_team_identity = SlackTeamIdentity.objects.get(pk=slack_team_identity_pk)
alert_group = AlertGroup.objects.get(pk=alert_group_pk)
step = UpdateLogReportMessageStep(slack_team_identity, alert_group.channel.organization)
if alert_group.skip_escalation_in_slack or alert_group.channel.is_rate_limited_in_slack:
return
if update: # flag to prevent multiple posting log message to slack
step.update_log_message(alert_group)
else:
# don't post a new message, as it is available from the button
# this is an intermediate step, so we will only update posted messages but not post new ones
# once majority of messages are updated, we can remove this step (https://github.com/grafana/oncall/pull/4686)
pass
logger.debug(f"Finish post_or_update_log_report_message_task for alert_group {alert_group_pk}")
return "Deprecated, will be removed after queue cleanup"
@shared_dedicated_queue_retry_task(

View file

@ -1,12 +1,11 @@
from unittest.mock import patch
import pytest
from django.utils import timezone
from apps.alerts.models import AlertGroup
from apps.slack.errors import SlackAPICantUpdateMessageError, SlackAPIRestrictedActionError
from apps.slack.errors import SlackAPIRestrictedActionError
from apps.slack.models import SlackMessage
from apps.slack.scenarios.distribute_alerts import AlertShootingStep, UpdateLogReportMessageStep
from apps.slack.scenarios.distribute_alerts import AlertShootingStep
from apps.slack.scenarios.scenario_step import ScenarioStep
from apps.slack.tests.conftest import build_slack_response
@ -65,37 +64,3 @@ def test_alert_shooting_no_channel_filter(
mock_post_alert_group_to_slack.assert_called_once()
assert mock_post_alert_group_to_slack.call_args[1]["channel_id"] == "DEFAULT_CHANNEL_ID"
@pytest.mark.django_db
def test_update_log_report_cant_update(
make_slack_team_identity,
make_organization,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_slack_message,
):
slack_team_identity = make_slack_team_identity()
organization = make_organization(
slack_team_identity=slack_team_identity, general_log_channel_id="DEFAULT_CHANNEL_ID"
)
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel, channel_filter=None)
# alert = make_alert(alert_group, raw_request_data={})
log_message = make_slack_message(
alert_group=alert_group,
channel_id="RANDOM_CHANNEL_ID",
slack_id="RANDOM_MESSAGE_ID",
last_updated=timezone.now() - timezone.timedelta(minutes=5),
)
alert_group.slack_log_message = log_message
step = UpdateLogReportMessageStep(slack_team_identity, organization)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
mock_slack_api_call.side_effect = SlackAPICantUpdateMessageError(
response=build_slack_response({"error": "cant_update_message"})
)
# not raising error, will not retry
step.update_log_message(alert_group)

View file

@ -156,9 +156,6 @@ CELERY_TASK_ROUTES = {
"apps.grafana_plugin.tasks.sync_v2.sync_organizations_v2": {"queue": "long"},
# SLACK
"apps.integrations.tasks.notify_about_integration_ratelimit_in_slack": {"queue": "slack"},
"apps.slack.helpers.alert_group_representative.on_alert_group_action_triggered_async": {"queue": "slack"},
"apps.slack.helpers.alert_group_representative.on_alert_group_update_log_report_async": {"queue": "slack"},
"apps.slack.helpers.alert_group_representative.on_create_alert_slack_representative_async": {"queue": "slack"},
"apps.slack.tasks.clean_slack_channel_leftovers": {"queue": "slack"},
"apps.slack.tasks.check_slack_message_exists_before_post_message_to_thread": {"queue": "slack"},
"apps.slack.tasks.clean_slack_integration_leftovers": {"queue": "slack"},
@ -167,7 +164,6 @@ CELERY_TASK_ROUTES = {
"apps.slack.tasks.populate_slack_user_identities": {"queue": "slack"},
"apps.slack.tasks.populate_slack_usergroups": {"queue": "slack"},
"apps.slack.tasks.populate_slack_usergroups_for_team": {"queue": "slack"},
"apps.slack.tasks.post_or_update_log_report_message_task": {"queue": "slack"},
"apps.slack.tasks.post_slack_rate_limit_message": {"queue": "slack"},
"apps.slack.tasks.send_message_to_thread_if_bot_not_in_channel": {"queue": "slack"},
"apps.slack.tasks.start_update_slack_user_group_for_schedules": {"queue": "slack"},
@ -178,6 +174,8 @@ CELERY_TASK_ROUTES = {
"queue": "slack"
},
"apps.slack.representatives.alert_group_representative.on_alert_group_action_triggered_async": {"queue": "slack"},
# TODO: remove post_or_update_log_report_message_task and on_alert_group_update_log_report_async in subsequent PR
"apps.slack.tasks.post_or_update_log_report_message_task": {"queue": "slack"},
"apps.slack.representatives.alert_group_representative.on_alert_group_update_log_report_async": {"queue": "slack"},
# TELEGRAM
"apps.telegram.tasks.edit_message": {"queue": "telegram"},

View file

@ -1,6 +1,5 @@
import semver from 'semver';
import { test, expect } from '../fixtures';
import { isGrafanaVersionLowerThan } from '../utils/constants';
import { clickButton, fillInInput } from '../utils/forms';
import { goToOnCallPage } from '../utils/navigation';
@ -22,9 +21,7 @@ test('we can directly page a user', async ({ adminRolePage }) => {
const addRespondersPopup = page.getByTestId('add-responders-popup');
await addRespondersPopup[semver.lt(process.env.CURRENT_GRAFANA_VERSION, '10.3.0') ? 'getByText' : 'getByLabel'](
'Users'
).click();
await addRespondersPopup[isGrafanaVersionLowerThan('10.3.0') ? 'getByText' : 'getByLabel']('Users').click();
await addRespondersPopup.getByText(adminRolePage.userName).first().click();
// If user is not on call, confirm invitation

View file

@ -6,7 +6,6 @@ import {
type APIRequestContext,
Page,
} from '@playwright/test';
import semver from 'semver';
import { VIEWER_USER_STORAGE_STATE, EDITOR_USER_STORAGE_STATE, ADMIN_USER_STORAGE_STATE } from '../playwright.config';
@ -21,6 +20,7 @@ import {
IS_CLOUD,
IS_OPEN_SOURCE,
OrgRole,
isGrafanaVersionLowerThan,
} from './utils/constants';
import { goToOnCallPage } from './utils/navigation';
@ -62,7 +62,7 @@ const idempotentlyInitializePlugin = async (page: Page) => {
if (await openPluginConfigurationButton.isVisible()) {
await openPluginConfigurationButton.click();
// Before 10.3 Admin user needs to create service account manually
if (semver.lt(process.env.CURRENT_GRAFANA_VERSION, '10.3.0')) {
if (isGrafanaVersionLowerThan('10.3.0')) {
await page.getByTestId('recreate-service-account').click();
}
await page.getByTestId('connect-plugin').click();

View file

@ -1,7 +1,6 @@
import semver from 'semver';
import { test, expect } from '../fixtures';
import { resolveFiringAlert } from '../utils/alertGroup';
import { isGrafanaVersionLowerThan } from '../utils/constants';
import { createEscalationChain, EscalationStep } from '../utils/escalationChain';
import { clickButton, generateRandomValue } from '../utils/forms';
import { createIntegrationAndSendDemoAlert } from '../utils/integrations';
@ -15,10 +14,7 @@ import { createOnCallSchedule } from '../utils/schedule';
* and use the currentGrafanaVersion fixture once this bugged is patched in playwright
* https://github.com/microsoft/playwright/issues/29608
*/
test.skip(
() => semver.lt(process.env.CURRENT_GRAFANA_VERSION, '10.0.0'),
'Insights is only available in Grafana 10.0.0 and above'
);
test.skip(() => isGrafanaVersionLowerThan('10.0.0'), 'Insights is only available in Grafana 10.0.0 and above');
/**
* skipping as these tests are currently flaky

View file

@ -1,8 +1,14 @@
import { test, expect } from '../fixtures';
import { isGrafanaVersionGreaterThan } from '../utils/constants';
import { clickButton, generateRandomValidLabel, openDropdown } from '../utils/forms';
import { openCreateIntegrationModal } from '../utils/integrations';
import { goToOnCallPage } from '../utils/navigation';
test.skip(
() => isGrafanaVersionGreaterThan('10.3.0'),
'Above 10.3 labels need enterprise version to validate permissions'
);
test('New label keys and labels can be created @expensive', async ({ adminRolePage }) => {
const { page } = adminRolePage;
await goToOnCallPage(page, 'integrations');

View file

@ -1,9 +1,7 @@
import semver from 'semver';
import { waitInMs } from 'utils/async';
import { test, expect, Page } from '../fixtures';
import { OrgRole } from '../utils/constants';
import { OrgRole, isGrafanaVersionLowerThan } from '../utils/constants';
import { goToGrafanaPage, goToOnCallPage } from '../utils/navigation';
import { createGrafanaUser, loginAndWaitTillGrafanaIsLoaded } from '../utils/users';
@ -48,10 +46,7 @@ test.describe('Plugin initialization', () => {
}) => {
test.slow();
test.skip(
semver.lt(process.env.CURRENT_GRAFANA_VERSION, '10.3.0'),
'Extension is only available in Grafana 10.3.0 and above'
);
test.skip(isGrafanaVersionLowerThan('10.3.0'), 'Extension is only available in Grafana 10.3.0 and above');
// Create new editor user
const USER_NAME = `editor-${new Date().getTime()}`;

View file

@ -1,10 +1,9 @@
import semver from 'semver';
import { scheduleViewToDaysInOneRow } from 'models/schedule/schedule.helpers';
import { ScheduleView } from 'models/schedule/schedule.types';
import { HTML_ID } from 'utils/DOM';
import { expect, Page, test } from '../fixtures';
import { isGrafanaVersionLowerThan } from '../utils/constants';
import { generateRandomValue } from '../utils/forms';
import { createOnCallSchedule } from '../utils/schedule';
@ -13,7 +12,7 @@ const getNumberOfWeekdaysInFinalSchedule = async (page: Page) =>
const getScheduleViewRadioButtonLocator = (page: Page, view: ScheduleView) =>
page
.getByTestId('schedule-view-picker')
[semver.lt(process.env.CURRENT_GRAFANA_VERSION, '10.2.0') ? 'getByText' : 'getByLabel'](view, { exact: true });
[isGrafanaVersionLowerThan('10.2.0') ? 'getByText' : 'getByLabel'](view, { exact: true });
test('schedule view (week/2 weeks/month) toggler works', async ({ adminRolePage }) => {
const { page, userName } = adminRolePage;

View file

@ -1,6 +1,5 @@
import semver from 'semver';
import { test, expect } from '../fixtures';
import { isGrafanaVersionLowerThan } from '../utils/constants';
import { goToOnCallPage } from '../utils/navigation';
import { verifyThatUserCanViewOtherUsers, accessProfileTabs } from '../utils/users';
@ -26,7 +25,7 @@ test.describe('Users screen actions', () => {
const tabsToCheck = ['tab-phone-verification', 'tab-slack', 'tab-telegram'];
// After 10.3 it's been moved to global user profile
if (semver.lt(process.env.CURRENT_GRAFANA_VERSION, '10.3.0')) {
if (isGrafanaVersionLowerThan('10.3.0')) {
tabsToCheck.unshift('tab-mobile-app');
}
@ -74,10 +73,10 @@ test.describe('Users screen actions', () => {
await page.waitForTimeout(2000);
await page
.locator('div')
.filter({ hasText: /^Search or filter results\.\.\.$/ })
.nth(1)
.click();
.locator('div')
.filter({ hasText: /^Search or filter results\.\.\.$/ })
.nth(1)
.click();
await page.keyboard.insertText(userName);
await page.keyboard.press('Enter');
await page.waitForTimeout(2000);

View file

@ -1,3 +1,5 @@
import semver from 'semver';
export const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
export const MAILSLURP_API_KEY = process.env.MAILSLURP_API_KEY;
@ -19,3 +21,6 @@ export enum OrgRole {
}
export const MOSCOW_TIMEZONE = 'Europe/Moscow';
export const isGrafanaVersionGreaterThan = (version: string) => semver.gt(process.env.CURRENT_GRAFANA_VERSION, version);
export const isGrafanaVersionLowerThan = (version: string) => semver.lt(process.env.CURRENT_GRAFANA_VERSION, version);

View file

@ -19,7 +19,7 @@ export const verifyUserPhoneNumber = async (page: Page): Promise<void> => {
await openUserSettingsModal(page);
// go to the Phone Verification tab
await page.locator('a[aria-label="Tab Phone Verification"]').click();
await page.getByTestId('tab-phone-verification').click();
// check to see if we've already verified our phone number.. no need to do it more than once
if (await getForgetPhoneNumberButton(page).isVisible()) {

View file

@ -18,7 +18,7 @@
"test:report": "HTML_REPORT_ENABLED=true yarn test",
"test:silent": "yarn test --silent",
"test:e2e": "yarn playwright test --grep-invert @expensive",
"test:e2e-expensive": "yarn playwright test",
"test:e2e-expensive": "yarn playwright test --grep @expensive",
"test:e2e:watch": "yarn test:e2e --ui",
"test:e2e-expensive:watch": "yarn test:e2e-expensive --ui",
"test:e2e:gen": "yarn playwright codegen http://localhost:3000",

View file

@ -674,7 +674,11 @@
{
"action": "users.roles:read",
"scope": "users:*"
}
},
{ "action": "grafana-labels-app.label:read" },
{ "action": "grafana-labels-app.label:write" },
{ "action": "grafana-labels-app.label:create" },
{ "action": "grafana-labels-app.label:delete" }
]
},
"dependencies": {