diff --git a/.github/workflows/linting-and-tests.yml b/.github/workflows/linting-and-tests.yml index 0a49a4ff..777634c6 100644 --- a/.github/workflows/linting-and-tests.yml +++ b/.github/workflows/linting-and-tests.yml @@ -479,7 +479,7 @@ jobs: GRAFANA_VIEWER_PASSWORD: viewer MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }} working-directory: ./grafana-plugin - run: yarn test:integration + run: yarn test:e2e # spit out the engine, celery, and grafana logs, if the the e2e tests have failed # can be helpful for debugging failing tests diff --git a/dev/README.md b/dev/README.md index fd33bc47..99edd275 100644 --- a/dev/README.md +++ b/dev/README.md @@ -7,7 +7,7 @@ - [Enabling RBAC for OnCall for local development](#enabling-rbac-for-oncall-for-local-development) - [Django Silk Profiling](#django-silk-profiling) - [Running backend services outside Docker](#running-backend-services-outside-docker) -- [UI Integration Tests](#ui-integration-tests) +- [UI E2E Tests](#ui-e2e-tests) - [Useful `make` commands](#useful-make-commands) - [Setting environment variables](#setting-environment-variables) - [Slack application setup](#slack-application-setup) @@ -189,7 +189,7 @@ By default everything runs inside Docker. If you would like to run the backend s - `make run-backend-server` - runs the HTTP server - `make run-backend-celery` - runs Celery workers -## UI Integration Tests +## UI E2E Tests We've developed a suite of "end-to-end" integration tests using [Playwright](https://playwright.dev/). These tests are run on pull request CI builds. New features should ideally include a new/modified integration test. @@ -198,10 +198,10 @@ To run these tests locally simply do the following: ```bash npx playwright install # install playwright dependencies -cp ./grafana-plugin/integration-tests/.env.example ./grafana-plugin/integration-tests/.env +cp ./grafana-plugin/e2e-tests/.env.example ./grafana-plugin/e2e-tests/.env # you may need to tweak the values in ./grafana-plugin/.env according to your local setup cd grafana-plugin -yarn test:integration +yarn test:e2e ``` ## Useful `make` commands diff --git a/grafana-plugin/.gitignore b/grafana-plugin/.gitignore index 43e7ea8a..ab331585 100644 --- a/grafana-plugin/.gitignore +++ b/grafana-plugin/.gitignore @@ -19,4 +19,4 @@ frontend_enterprise /test-results/ /playwright-report/ /playwright/.cache/ -/integration-tests/storageState.json +/e2e-tests/storageState.json diff --git a/grafana-plugin/integration-tests/.auth/.gitignore b/grafana-plugin/e2e-tests/.auth/.gitignore similarity index 100% rename from grafana-plugin/integration-tests/.auth/.gitignore rename to grafana-plugin/e2e-tests/.auth/.gitignore diff --git a/grafana-plugin/integration-tests/.env.example b/grafana-plugin/e2e-tests/.env.example similarity index 100% rename from grafana-plugin/integration-tests/.env.example rename to grafana-plugin/e2e-tests/.env.example diff --git a/grafana-plugin/integration-tests/alerts/directPaging.test.ts b/grafana-plugin/e2e-tests/alerts/directPaging.test.ts similarity index 100% rename from grafana-plugin/integration-tests/alerts/directPaging.test.ts rename to grafana-plugin/e2e-tests/alerts/directPaging.test.ts diff --git a/grafana-plugin/integration-tests/alerts/onCallSchedule.test.ts b/grafana-plugin/e2e-tests/alerts/onCallSchedule.test.ts similarity index 100% rename from grafana-plugin/integration-tests/alerts/onCallSchedule.test.ts rename to grafana-plugin/e2e-tests/alerts/onCallSchedule.test.ts diff --git a/grafana-plugin/integration-tests/alerts/sms.test.ts b/grafana-plugin/e2e-tests/alerts/sms.test.ts similarity index 100% rename from grafana-plugin/integration-tests/alerts/sms.test.ts rename to grafana-plugin/e2e-tests/alerts/sms.test.ts diff --git a/grafana-plugin/integration-tests/escalationChains/searching.test.ts b/grafana-plugin/e2e-tests/escalationChains/searching.test.ts similarity index 100% rename from grafana-plugin/integration-tests/escalationChains/searching.test.ts rename to grafana-plugin/e2e-tests/escalationChains/searching.test.ts diff --git a/grafana-plugin/integration-tests/fixtures.ts b/grafana-plugin/e2e-tests/fixtures.ts similarity index 100% rename from grafana-plugin/integration-tests/fixtures.ts rename to grafana-plugin/e2e-tests/fixtures.ts diff --git a/grafana-plugin/integration-tests/globalSetup.ts b/grafana-plugin/e2e-tests/globalSetup.ts similarity index 100% rename from grafana-plugin/integration-tests/globalSetup.ts rename to grafana-plugin/e2e-tests/globalSetup.ts diff --git a/grafana-plugin/integration-tests/integrations/heartbeat.test.ts b/grafana-plugin/e2e-tests/integrations/heartbeat.test.ts similarity index 100% rename from grafana-plugin/integration-tests/integrations/heartbeat.test.ts rename to grafana-plugin/e2e-tests/integrations/heartbeat.test.ts diff --git a/grafana-plugin/integration-tests/integrations/maintenanceMode.test.ts b/grafana-plugin/e2e-tests/integrations/maintenanceMode.test.ts similarity index 100% rename from grafana-plugin/integration-tests/integrations/maintenanceMode.test.ts rename to grafana-plugin/e2e-tests/integrations/maintenanceMode.test.ts diff --git a/grafana-plugin/integration-tests/integrations/uniqueIntegrationNames.test.ts b/grafana-plugin/e2e-tests/integrations/uniqueIntegrationNames.test.ts similarity index 100% rename from grafana-plugin/integration-tests/integrations/uniqueIntegrationNames.test.ts rename to grafana-plugin/e2e-tests/integrations/uniqueIntegrationNames.test.ts diff --git a/grafana-plugin/integration-tests/schedules/addOverride.test.ts b/grafana-plugin/e2e-tests/schedules/addOverride.test.ts similarity index 100% rename from grafana-plugin/integration-tests/schedules/addOverride.test.ts rename to grafana-plugin/e2e-tests/schedules/addOverride.test.ts diff --git a/grafana-plugin/integration-tests/schedules/quality.test.ts b/grafana-plugin/e2e-tests/schedules/quality.test.ts similarity index 100% rename from grafana-plugin/integration-tests/schedules/quality.test.ts rename to grafana-plugin/e2e-tests/schedules/quality.test.ts diff --git a/grafana-plugin/e2e-tests/users/usersActions.test.ts b/grafana-plugin/e2e-tests/users/usersActions.test.ts new file mode 100644 index 00000000..41b55aa4 --- /dev/null +++ b/grafana-plugin/e2e-tests/users/usersActions.test.ts @@ -0,0 +1,120 @@ +import { test, expect, Page } from '../fixtures'; +import { goToOnCallPage } from '../utils/navigation'; + +test.describe('Users screen actions', () => { + test("Admin is allowed to edit other users' profile", async ({ adminRolePage }) => { + await _testButtons(adminRolePage.page, 'button.edit-other-profile-button[disabled]'); + }); + + test('Admin is allowed to view the list of users', async ({ adminRolePage }) => { + await _viewUsers(adminRolePage.page); + }); + + test('Viewer is not allowed to view the list of users', async ({ viewerRolePage }) => { + await _viewUsers(viewerRolePage.page, false); + }); + + test('Viewer cannot access restricted tabs from View My Profile', async ({ viewerRolePage }) => { + const { page } = viewerRolePage; + + await _accessProfileTabs(page, ['tab-mobile-app', 'tab-phone-verification', 'tab-slack', 'tab-telegram'], false); + }); + + test('Editor is allowed to view the list of users', async ({ editorRolePage }) => { + await _viewUsers(editorRolePage.page); + }); + + test("Editor cannot view other users' data", async ({ editorRolePage }) => { + const { page } = editorRolePage; + + await goToOnCallPage(page, 'users'); + await page.waitForSelector('.current-user'); + + // check if these fields are Masked or Not (******) + const fieldIds = ['users-email', 'users-phone-number']; + + for (let i = 0; i < fieldIds.length - 1; ++i) { + const currentUsername = page.locator(`.current-user [data-testid="${fieldIds[i]}"]`); + + expect((await currentUsername.all()).length).toBe(1); // match for current user + (await currentUsername.all()).forEach((val) => expect(val).not.toHaveText('******')); + + const otherUsername = page.locator(`.other-user [data-testid="${fieldIds[i]}"]`); + + expect((await otherUsername.all()).length).toBeGreaterThan(1); // match for other users (>= 1) + (await otherUsername.all()).forEach((val) => expect(val).toHaveText('******')); + } + }); + + test('Editor can access tabs from View My Profile', async ({ editorRolePage }) => { + const { page } = editorRolePage; + + // the other tabs depend on Cloud, skip for now + await _accessProfileTabs(page, ['tab-slack', 'tab-telegram'], true); + }); + + test("Editor is not allowed to edit other users' profile", async ({ editorRolePage }) => { + await _testButtons(editorRolePage.page, 'button.edit-other-profile-button:not([disabled])'); + }); + + /* + * Helper methods + */ + + async function _testButtons(page: Page, selector: string) { + await goToOnCallPage(page, 'users'); + + const usersTableElement = page.getByTestId('users-table'); + await usersTableElement.waitFor({ state: 'visible' }); + + const buttonsList = await page.locator(selector); + + expect(buttonsList).toHaveCount(0); + } + + async function _accessProfileTabs(page: Page, tabs: string[], hasAccess: boolean) { + await goToOnCallPage(page, 'users'); + + await page.getByTestId('users-view-my-profile').click(); + + // the next queries could or could not resolve + // therefore we wait a generic 1000ms duration and assert based on visibility + await page.waitForTimeout(1000); + + for (let i = 0; i < tabs.length - 1; ++i) { + const tab = page.getByTestId(tabs[i]); + + if (await tab.isVisible()) { + await tab.click(); + + const query = page.getByText( + 'You do not have permission to perform this action. Ask an admin to upgrade your permissions.' + ); + + if (hasAccess) { + await expect(query).toBeHidden(); + } else { + await expect(query).toBeVisible(); + } + } + } + } + + async function _viewUsers(page: Page, isAllowedToView = true): Promise { + await goToOnCallPage(page, 'users'); + + if (isAllowedToView) { + const usersTableElement = page.getByTestId('users-table'); + await usersTableElement.waitFor({ state: 'visible' }); + + const userRowsContext = await usersTableElement.locator('tbody > tr').allTextContents(); + expect(userRowsContext.length).toBeGreaterThan(0); + } else { + const missingPermissionsMessageElement = page.getByTestId('view-users-missing-permission-message'); + await missingPermissionsMessageElement.waitFor({ state: 'visible' }); + + const missingPermissionMessage = await missingPermissionsMessageElement.textContent(); + expect(missingPermissionMessage).toMatch(/You are missing the .* to be able to view OnCall users/); + } + } +}); diff --git a/grafana-plugin/integration-tests/utils/alertGroup.ts b/grafana-plugin/e2e-tests/utils/alertGroup.ts similarity index 100% rename from grafana-plugin/integration-tests/utils/alertGroup.ts rename to grafana-plugin/e2e-tests/utils/alertGroup.ts diff --git a/grafana-plugin/integration-tests/utils/clients/grafana.ts b/grafana-plugin/e2e-tests/utils/clients/grafana.ts similarity index 100% rename from grafana-plugin/integration-tests/utils/clients/grafana.ts rename to grafana-plugin/e2e-tests/utils/clients/grafana.ts diff --git a/grafana-plugin/integration-tests/utils/constants.ts b/grafana-plugin/e2e-tests/utils/constants.ts similarity index 100% rename from grafana-plugin/integration-tests/utils/constants.ts rename to grafana-plugin/e2e-tests/utils/constants.ts diff --git a/grafana-plugin/integration-tests/utils/escalationChain.ts b/grafana-plugin/e2e-tests/utils/escalationChain.ts similarity index 100% rename from grafana-plugin/integration-tests/utils/escalationChain.ts rename to grafana-plugin/e2e-tests/utils/escalationChain.ts diff --git a/grafana-plugin/integration-tests/utils/forms.ts b/grafana-plugin/e2e-tests/utils/forms.ts similarity index 100% rename from grafana-plugin/integration-tests/utils/forms.ts rename to grafana-plugin/e2e-tests/utils/forms.ts diff --git a/grafana-plugin/integration-tests/utils/integrations.ts b/grafana-plugin/e2e-tests/utils/integrations.ts similarity index 100% rename from grafana-plugin/integration-tests/utils/integrations.ts rename to grafana-plugin/e2e-tests/utils/integrations.ts diff --git a/grafana-plugin/integration-tests/utils/modals.ts b/grafana-plugin/e2e-tests/utils/modals.ts similarity index 100% rename from grafana-plugin/integration-tests/utils/modals.ts rename to grafana-plugin/e2e-tests/utils/modals.ts diff --git a/grafana-plugin/integration-tests/utils/navigation.ts b/grafana-plugin/e2e-tests/utils/navigation.ts similarity index 100% rename from grafana-plugin/integration-tests/utils/navigation.ts rename to grafana-plugin/e2e-tests/utils/navigation.ts diff --git a/grafana-plugin/integration-tests/utils/phone.ts b/grafana-plugin/e2e-tests/utils/phone.ts similarity index 100% rename from grafana-plugin/integration-tests/utils/phone.ts rename to grafana-plugin/e2e-tests/utils/phone.ts diff --git a/grafana-plugin/integration-tests/utils/schedule.ts b/grafana-plugin/e2e-tests/utils/schedule.ts similarity index 100% rename from grafana-plugin/integration-tests/utils/schedule.ts rename to grafana-plugin/e2e-tests/utils/schedule.ts diff --git a/grafana-plugin/integration-tests/utils/userSettings.ts b/grafana-plugin/e2e-tests/utils/userSettings.ts similarity index 100% rename from grafana-plugin/integration-tests/utils/userSettings.ts rename to grafana-plugin/e2e-tests/utils/userSettings.ts diff --git a/grafana-plugin/integration-tests/users/viewUsers.test.ts b/grafana-plugin/integration-tests/users/viewUsers.test.ts deleted file mode 100644 index f545097a..00000000 --- a/grafana-plugin/integration-tests/users/viewUsers.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { test, expect, Page } from '../fixtures'; -import { goToOnCallPage } from '../utils/navigation'; - -test.describe('view list of users', () => { - const testFlow = async (page: Page, isAllowedToView = true): Promise => { - await goToOnCallPage(page, 'users'); - - if (isAllowedToView) { - const usersTableElement = page.getByTestId('users-table'); - await usersTableElement.waitFor({ state: 'visible' }); - - const userRowsContext = await usersTableElement.locator('tbody > tr').allTextContents(); - expect(userRowsContext.length).toBeGreaterThan(0); - } else { - const missingPermissionsMessageElement = page.getByTestId('view-users-missing-permission-message'); - await missingPermissionsMessageElement.waitFor({ state: 'visible' }); - - const missingPermissionMessage = await missingPermissionsMessageElement.textContent(); - expect(missingPermissionMessage).toMatch(/You are missing the .* to be able to view OnCall users/); - } - }; - - test('admin is allowed to', async ({ adminRolePage }) => { - await testFlow(adminRolePage.page); - }); - - test('editor is allowed to', async ({ editorRolePage }) => { - await testFlow(editorRolePage.page); - }); - - test('viewer is not allowed to', async ({ viewerRolePage }) => { - await testFlow(viewerRolePage.page, false); - }); -}); diff --git a/grafana-plugin/jest.config.js b/grafana-plugin/jest.config.js index f63746a3..e100b0c3 100644 --- a/grafana-plugin/jest.config.js +++ b/grafana-plugin/jest.config.js @@ -21,5 +21,5 @@ module.exports = { setupFilesAfterEnv: ['/jest.setup.ts'], testTimeout: 10000, - testPathIgnorePatterns: ['/node_modules/', '/integration-tests/'], + testPathIgnorePatterns: ['/node_modules/', '/e2e-tests/'], }; diff --git a/grafana-plugin/package.json b/grafana-plugin/package.json index d6c67721..5ae43a3d 100644 --- a/grafana-plugin/package.json +++ b/grafana-plugin/package.json @@ -11,7 +11,7 @@ "build:dev": "grafana-toolkit plugin:build --skipTest --skipLint", "test": "jest --verbose", "test:silent": "jest --silent", - "test:integration": "yarn playwright test", + "test:e2e": "yarn playwright test", "dev": "grafana-toolkit plugin:dev", "watch": "grafana-toolkit plugin:dev --watch", "sign": "grafana-toolkit plugin:sign", diff --git a/grafana-plugin/playwright.config.ts b/grafana-plugin/playwright.config.ts index aa5657dc..e38265ce 100644 --- a/grafana-plugin/playwright.config.ts +++ b/grafana-plugin/playwright.config.ts @@ -7,17 +7,17 @@ import { devices } from '@playwright/test'; * Read environment variables from file. * https://github.com/motdotla/dotenv */ -require('dotenv').config({ path: path.resolve(process.cwd(), 'integration-tests/.env') }); +require('dotenv').config({ path: path.resolve(process.cwd(), 'e2e-tests/.env') }); -export const VIEWER_USER_STORAGE_STATE = path.join(__dirname, 'integration-tests/.auth/viewer.json'); -export const EDITOR_USER_STORAGE_STATE = path.join(__dirname, 'integration-tests/.auth/editor.json'); -export const ADMIN_USER_STORAGE_STATE = path.join(__dirname, 'integration-tests/.auth/admin.json'); +export const VIEWER_USER_STORAGE_STATE = path.join(__dirname, 'e2e-tests/.auth/viewer.json'); +export const EDITOR_USER_STORAGE_STATE = path.join(__dirname, 'e2e-tests/.auth/editor.json'); +export const ADMIN_USER_STORAGE_STATE = path.join(__dirname, 'e2e-tests/.auth/admin.json'); /** * See https://playwright.dev/docs/test-configuration. */ const config: PlaywrightTestConfig = { - testDir: './integration-tests', + testDir: './e2e-tests', /* Maximum time all the tests can run for. */ globalTimeout: 20 * 60 * 1000, // 20 minutes diff --git a/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.tsx b/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.tsx index d0d8f004..20c76a80 100644 --- a/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.tsx +++ b/grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.tsx @@ -40,10 +40,7 @@ const MobileAppConnection = observer(({ userPk }: Props) => { // Show link to cloud page for OSS instances with no cloud connection if (store.hasFeature(AppFeature.CloudConnection) && !cloudStore.cloudConnectionStatus.cloud_connection_status) { return ( - + Please connect Cloud OnCall to use the mobile app {showNotificationSettingsTab && ( )} {showMobileAppConnectionTab && ( )} {showSlackConnectionTab && ( @@ -83,6 +88,7 @@ export const Tabs = ({ label="Slack Connection" key={UserSettingsTab.SlackInfo} onChangeTab={getTabClickHandler(UserSettingsTab.SlackInfo)} + data-testid="tab-slack" /> )} {showTelegramConnectionTab && ( @@ -91,6 +97,7 @@ export const Tabs = ({ label="Telegram Connection" key={UserSettingsTab.TelegramInfo} onChangeTab={getTabClickHandler(UserSettingsTab.TelegramInfo)} + data-testid="tab-telegram" /> )} diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/CloudPhoneSettings/CloudPhoneSettings.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/CloudPhoneSettings/CloudPhoneSettings.tsx index e2259795..0b7614d1 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/CloudPhoneSettings/CloudPhoneSettings.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/CloudPhoneSettings/CloudPhoneSettings.tsx @@ -111,27 +111,24 @@ const CloudPhoneSettings = observer((props: CloudPhoneSettingsProps) => { }; return ( - <> - - - OnCall uses Grafana Cloud for SMS and phone call notifications - {syncing ? ( - - ) : ( - - )} - {!syncing ? : } - - - + + + OnCall uses Grafana Cloud for SMS and phone call notifications + {syncing ? ( + + ) : ( + + )} + {!syncing ? : } + + ); }); diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/PhoneVerification/PhoneVerification.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/PhoneVerification/PhoneVerification.tsx index a32420d3..f7ffe8e7 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/PhoneVerification/PhoneVerification.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/PhoneVerification/PhoneVerification.tsx @@ -6,6 +6,7 @@ import { observer } from 'mobx-react'; import PluginLink from 'components/PluginLink/PluginLink'; import Text from 'components/Text/Text'; +import { WithPermissionControlDisplay } from 'containers/WithPermissionControl/WithPermissionControlDisplay'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; import { User } from 'models/user/user.types'; import { rootStore } from 'state'; @@ -187,31 +188,28 @@ const PhoneVerification = observer((props: PhoneVerificationProps) => { } return ( - <> - {isPhoneValid && !user.verified_phone_number && ( - <> - -
- - )} - - {!isPhoneProviderConfigured && store.hasFeature(AppFeature.LiveSettings) && ( - <> - - Can't verify phone. Check ENV variables to - configure your provider. - - } - /> -
- - )} - + + {isPhoneValid && !user.verified_phone_number && ( + + )} + + {!isPhoneProviderConfigured && store.hasFeature(AppFeature.LiveSettings) && ( + <> + + Can't verify phone. Check ENV variables{' '} + to configure your provider. + + ) as any + } + /> + + )} + { required disabled={!isPhoneProviderConfigured || isPhoneDisabled} placeholder="Please enter the phone number with country code, e.g. +12451111111" - // @ts-ignore - prefix={} + prefix={} value={phone} onChange={onChangePhoneCallback} /> @@ -263,25 +260,23 @@ const PhoneVerification = observer((props: PhoneVerificationProps) => { )} + + setState({ showForgetScreen: true })} + user={user} + /> - -
- - setState({ showForgetScreen: true })} - user={user} - /> - +
); }); @@ -363,7 +358,6 @@ function PhoneVerificationButtonsGroup({ ) : ( - {' '} {providerConfiguration.verification_sms && ( -
+ + + + + +
); }; diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx index 278961ed..680dd7e2 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx @@ -39,10 +39,7 @@ const TelegramInfo = observer((_props: TelegramInfoProps) => { }, []); return ( - + {telegramConfigured || !store.hasFeature(AppFeature.LiveSettings) ? ( Manual connection diff --git a/grafana-plugin/src/containers/WithPermissionControl/WithPermissionControlDisplay.tsx b/grafana-plugin/src/containers/WithPermissionControl/WithPermissionControlDisplay.tsx index c8ffed9c..8ceff885 100644 --- a/grafana-plugin/src/containers/WithPermissionControl/WithPermissionControlDisplay.tsx +++ b/grafana-plugin/src/containers/WithPermissionControl/WithPermissionControlDisplay.tsx @@ -8,12 +8,17 @@ import { isUserActionAllowed, UserAction } from 'utils/authorization'; interface WithPermissionControlDisplayProps { userAction: UserAction; children: ReactElement; - message: string; + message?: string; title?: string; } export const WithPermissionControlDisplay: React.FC = (props) => { - const { userAction, children, title, message } = props; + const { + userAction, + children, + title, + message = 'You do not have permission to perform this action. Ask an admin to upgrade your permissions.', + } = props; const hasPermission = isUserActionAllowed(userAction); diff --git a/grafana-plugin/src/pages/users/Users.tsx b/grafana-plugin/src/pages/users/Users.tsx index c04f5804..69b41794 100644 --- a/grafana-plugin/src/pages/users/Users.tsx +++ b/grafana-plugin/src/pages/users/Users.tsx @@ -121,55 +121,14 @@ class Users extends React.Component { }; render() { - const { usersFilters, userPkToEdit, page, errorData, initialUsersLoaded } = this.state; + const { userPkToEdit, errorData } = this.state; const { - store, match: { params: { id }, }, } = this.props; - const { userStore } = store; - const columns = [ - { - width: '25%', - key: 'username', - title: 'User', - render: this.renderTitle, - }, - { - width: '20%', - title: 'Status', - key: 'note', - render: this.renderStatus, - }, - { - width: '20%', - title: 'Default Notifications', - key: 'notifications-chain', - render: this.renderNotificationsChain, - }, - { - width: '20%', - title: 'Important Notifications', - key: 'important-notifications-chain', - render: this.renderImportantNotificationsChain, - }, - { - width: '5%', - key: 'buttons', - render: this.renderButtons, - }, - ]; - - const handleClear = () => - this.setState({ usersFilters: { searchTerm: '' } }, () => { - this.debouncedUpdateUsers(); - }); - - const { count, results } = userStore.getSearchResult(); - - const authorizedToViewUsers = isUserActionAllowed(REQUIRED_PERMISSION_TO_VIEW_USERS); + const isAuthorizedToViewUsers = isUserActionAllowed(REQUIRED_PERMISSION_TO_VIEW_USERS); return ( { itemNotFoundMessage={`User with id=${id} is not found. Please select user from the list.`} > {() => ( - <> -
-
-
-
-
- - Users - - {authorizedToViewUsers && ( - - All Grafana users listed below to set notification preferences. To manage permissions or add - new users, please visit{' '} - - Grafana user management - - - )} -
-
- - - +
+
+
+
+ + Users + + {isAuthorizedToViewUsers && ( + + All Grafana users listed below to set notification preferences. To manage permissions or add new + users, please visit{' '} + + Grafana user management + + + )}
- {authorizedToViewUsers ? ( - <> -
- - -
- - - - ) : ( - - {generateMissingPermissionMessage(REQUIRED_PERMISSION_TO_VIEW_USERS)} to be able to view OnCall - users. Click here to open your - profile - - } - data-testid="view-users-missing-permission-message" - severity="info" - /> - )}
- {userPkToEdit && } + + +
- + + {this.renderContentIfAuthorized(isAuthorizedToViewUsers)} + + {userPkToEdit && } +
)} ); } - handleChangePage = (page: number) => { - this.setState({ page }, this.updateUsers); - }; + renderContentIfAuthorized(authorizedToViewUsers: boolean) { + const { + store: { userStore }, + } = this.props; + const { usersFilters, page, initialUsersLoaded, userPkToEdit } = this.state; + + const { count, results } = userStore.getSearchResult(); + const columns = this.getTableColumns(); + + const handleClear = () => + this.setState({ usersFilters: { searchTerm: '' } }, () => { + this.debouncedUpdateUsers(); + }); + + return ( + <> + {authorizedToViewUsers ? ( + <> +
+ + +
+ + + + ) : ( + + + {generateMissingPermissionMessage(REQUIRED_PERMISSION_TO_VIEW_USERS)} to be able to view OnCall + users. Click here to open your profile + +
+ ) as any + } + data-testid="view-users-missing-permission-message" + severity="info" + /> + )} + + ); + } renderTitle = (user: UserType) => { + const { + store: { userStore }, + } = this.props; + const isCurrent = userStore.currentUserPk === user.pk; + return ( -
-
{user.username}
- {user.email} +
+
{user.username}
+ + {user.email} +
- {user.verified_phone_number} + + {user.verified_phone_number} +
); @@ -311,7 +296,8 @@ class Users extends React.Component {