@ -2,8 +2,10 @@ import { render, screen, waitFor, within } from '@testing-library/react';
import userEvent , { PointerEventsCheckLevel } from '@testing-library/user-event' ;
import React from 'react' ;
import { OrgRole } from '@grafana/data' ;
import { OrgRole , PluginExtensionComponent , PluginExtensionTypes } from '@grafana/data' ;
import { selectors } from '@grafana/e2e-selectors' ;
import { setPluginExtensionGetter , GetPluginExtensions } from '@grafana/runtime' ;
import * as useQueryParams from 'app/core/hooks/useQueryParams' ;
import { TestProvider } from '../../../test/helpers/TestProvider' ;
import { backendSrv } from '../../core/services/backend_srv' ;
@ -13,6 +15,13 @@ import { getMockTeam } from '../teams/__mocks__/teamMocks';
import { Props , UserProfileEditPage } from './UserProfileEditPage' ;
import { initialUserState } from './state/reducers' ;
const mockUseQueryParams = useQueryParams as { useQueryParams : typeof useQueryParams . useQueryParams } ;
jest . mock ( 'app/core/hooks/useQueryParams' , ( ) = > ( {
__esModule : true ,
useQueryParams : ( ) = > [ { } ] ,
} ) ) ;
const defaultProps : Props = {
. . . initialUserState ,
user : {
@ -91,10 +100,69 @@ function getSelectors() {
within ( sessionsTable ( ) ) . getByRole ( 'row' , {
name : /now January 1, 2021 localhost chrome on mac os x 11/i ,
} ) ,
/ * *
* using queryByTestId instead of getByTestId because the tabs are not always rendered
* and getByTestId throws an TestingLibraryElementError error if the element is not found
* whereas queryByTestId returns null if the element is not found . There are some test cases
* where we ' d explicitly like to assert that the tabs are not rendered .
* /
extensionPointTabs : ( ) = > screen . queryByTestId ( selectors . components . UserProfile . extensionPointTabs ) ,
/ * *
* here lets use getByTestId because a specific tab should always be rendered within the tabs container
* /
extensionPointTab : ( tabId : string ) = >
within ( screen . getByTestId ( selectors . components . UserProfile . extensionPointTabs ) ) . getByTestId (
selectors . components . UserProfile . extensionPointTab ( tabId )
) ,
} ;
}
async function getTestContext ( overrides : Partial < Props > = { } ) {
enum ExtensionPointComponentId {
One = '1' ,
Two = '2' ,
Three = '3' ,
}
enum ExtensionPointComponentTabs {
One = '1' ,
Two = '2' ,
}
const _createTabName = ( tab : ExtensionPointComponentTabs ) = > ` Tab ${ tab } ` ;
const _createTabContent = ( tabId : ExtensionPointComponentId ) = > ` this is settings for component ${ tabId } ` ;
const generalTabName = 'General' ;
const tabOneName = _createTabName ( ExtensionPointComponentTabs . One ) ;
const tabTwoName = _createTabName ( ExtensionPointComponentTabs . Two ) ;
const _createPluginExtensionPointComponent = (
id : ExtensionPointComponentId ,
tab : ExtensionPointComponentTabs
) : PluginExtensionComponent = > ( {
id ,
type : PluginExtensionTypes . component ,
title : _createTabName ( tab ) ,
description : '' , // description isn't used here..
component : ( ) = > < p > { _createTabContent ( id ) } < / p > ,
pluginId : 'grafana-plugin' ,
} ) ;
const PluginExtensionPointComponent1 = _createPluginExtensionPointComponent (
ExtensionPointComponentId . One ,
ExtensionPointComponentTabs . One
) ;
const PluginExtensionPointComponent2 = _createPluginExtensionPointComponent (
ExtensionPointComponentId . Two ,
ExtensionPointComponentTabs . One
) ;
const PluginExtensionPointComponent3 = _createPluginExtensionPointComponent (
ExtensionPointComponentId . Three ,
ExtensionPointComponentTabs . Two
) ;
async function getTestContext ( overrides : Partial < Props & { extensions : PluginExtensionComponent [ ] } > = { } ) {
const extensions = overrides . extensions || [ ] ;
jest . clearAllMocks ( ) ;
const putSpy = jest . spyOn ( backendSrv , 'put' ) ;
const getSpy = jest
@ -102,6 +170,10 @@ async function getTestContext(overrides: Partial<Props> = {}) {
. mockResolvedValue ( { timezone : 'UTC' , homeDashboardUID : 'home-dashboard' , theme : 'dark' } ) ;
const searchSpy = jest . spyOn ( backendSrv , 'search' ) . mockResolvedValue ( [ ] ) ;
const getter : GetPluginExtensions < PluginExtensionComponent > = jest . fn ( ) . mockReturnValue ( { extensions } ) ;
setPluginExtensionGetter ( getter ) ;
const props = { . . . defaultProps , . . . overrides } ;
const { rerender } = render (
< TestProvider >
@ -254,5 +326,73 @@ describe('UserProfileEditPage', () => {
expect ( props . revokeUserSession ) . toHaveBeenCalledWith ( 0 ) ;
} ) ;
} ) ;
describe ( 'and a plugin registers a component against the user profile settings extension point' , ( ) = > {
const extensions = [
PluginExtensionPointComponent1 ,
PluginExtensionPointComponent2 ,
PluginExtensionPointComponent3 ,
] ;
it ( 'should not show tabs when no components are registered' , async ( ) = > {
await getTestContext ( ) ;
const { extensionPointTabs } = getSelectors ( ) ;
expect ( extensionPointTabs ( ) ) . not . toBeInTheDocument ( ) ;
} ) ;
it ( 'should group registered components into tabs' , async ( ) = > {
await getTestContext ( { extensions } ) ;
const { extensionPointTabs , extensionPointTab } = getSelectors ( ) ;
const _assertTab = ( tabId : string , isDefault = false ) = > {
const tab = extensionPointTab ( tabId ) ;
expect ( tab ) . toBeInTheDocument ( ) ;
expect ( tab ) . toHaveAttribute ( 'aria-selected' , isDefault . toString ( ) ) ;
} ;
expect ( extensionPointTabs ( ) ) . toBeInTheDocument ( ) ;
_assertTab ( generalTabName . toLowerCase ( ) , true ) ;
_assertTab ( tabOneName . toLowerCase ( ) ) ;
_assertTab ( tabTwoName . toLowerCase ( ) ) ;
} ) ;
it ( 'should change the active tab when a tab is clicked and update the "tab" query param' , async ( ) = > {
const mockUpdateQueryParams = jest . fn ( ) ;
mockUseQueryParams . useQueryParams = ( ) = > [ { } , mockUpdateQueryParams ] ;
await getTestContext ( { extensions } ) ;
const { extensionPointTab } = getSelectors ( ) ;
/ * *
* Tab one has two extension components registered against it , they ' ll both be registered in the same tab
* Tab two only has one extension component registered against it .
* /
const tabOneContent1 = _createTabContent ( ExtensionPointComponentId . One ) ;
const tabOneContent2 = _createTabContent ( ExtensionPointComponentId . Two ) ;
const tabTwoContent = _createTabContent ( ExtensionPointComponentId . Three ) ;
// "General" should be the default content
expect ( screen . queryByText ( tabOneContent1 ) ) . toBeNull ( ) ;
expect ( screen . queryByText ( tabOneContent2 ) ) . toBeNull ( ) ;
expect ( screen . queryByText ( tabTwoContent ) ) . toBeNull ( ) ;
await userEvent . click ( extensionPointTab ( tabOneName . toLowerCase ( ) ) ) ;
expect ( mockUpdateQueryParams ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( mockUpdateQueryParams ) . toHaveBeenCalledWith ( { tab : tabOneName.toLowerCase ( ) } ) ;
expect ( screen . queryByText ( tabOneContent1 ) ) . not . toBeNull ( ) ;
expect ( screen . queryByText ( tabOneContent2 ) ) . not . toBeNull ( ) ;
expect ( screen . queryByText ( tabTwoContent ) ) . toBeNull ( ) ;
mockUpdateQueryParams . mockClear ( ) ;
await userEvent . click ( extensionPointTab ( tabTwoName . toLowerCase ( ) ) ) ;
expect ( mockUpdateQueryParams ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( mockUpdateQueryParams ) . toHaveBeenCalledWith ( { tab : tabTwoName.toLowerCase ( ) } ) ;
expect ( screen . queryByText ( tabOneContent1 ) ) . toBeNull ( ) ;
expect ( screen . queryByText ( tabOneContent2 ) ) . toBeNull ( ) ;
expect ( screen . queryByText ( tabTwoContent ) ) . not . toBeNull ( ) ;
} ) ;
} ) ;
} ) ;
} ) ;