diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7b81e1f23af..ff5a5950112 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -816,6 +816,7 @@ embed.go @grafana/grafana-as-code /.github/workflows/verify-kinds.yml @grafana/platform-monitoring /.github/workflows/dashboards-issue-add-label.yml @grafana/dashboards-squad /.github/workflows/run-schema-v2-e2e.yml @grafana/dashboards-squad +/.github/workflows/run-dashboard-search-e2e.yml @grafana/grafana-search-and-storage /.github/workflows/ephemeral-instances-pr-comment.yml @grafana/grafana-backend-services-squad /.github/workflows/create-security-patch-from-security-mirror.yml @grafana/grafana-developer-enablement-squad /.github/workflows/core-plugins-build-and-release.yml @grafana/plugins-platform-frontend @grafana/plugins-platform-backend diff --git a/.github/workflows/run-dashboard-search-e2e.yml b/.github/workflows/run-dashboard-search-e2e.yml new file mode 100644 index 00000000000..aa41c96e0b7 --- /dev/null +++ b/.github/workflows/run-dashboard-search-e2e.yml @@ -0,0 +1,121 @@ +name: Run dashboard search e2e + +on: + push: + branches: + - main + pull_request: + branches: + - main + +env: + ARCH: linux-amd64 + +jobs: + setup: + runs-on: ubuntu-latest + if: github.event.pull_request.draft == false + outputs: + ini_files: ${{ steps.get_files.outputs.ini_files }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Pin Go version to mod file + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + cache: true + - run: go version + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'yarn' + - name: Cache Node Modules + id: cache-node-modules + uses: actions/cache@v3 + with: + path: | + node_modules + /home/runner/.cache/Cypress + key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} + - name: Install dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + run: yarn install --immutable + - name: Install Cypress dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + uses: cypress-io/github-action@v6 + with: + runTests: false + - name: Cache Grafana Build and Dependencies + id: cache-grafana + uses: actions/cache@v3 + with: + path: | + bin/ + scripts/grafana-server/ + tools/ + public/ + conf/ + e2e/test-plugins/ + devenv/ + key: ${{ runner.os }}-grafana-${{ hashFiles('go.mod', 'package-lock.json', 'Makefile', 'pkg/storage/**/*.go', 'public/app/features/search/**/*.ts', 'public/app/features/search/**/*.tsx') }} + # only rebuild grafana if search files have changed ( or dependencies ) + - name: Build Grafana (Runs Only If Not Cached) + if: steps.cache-grafana.outputs.cache-hit != 'true' + run: make build + + - name: Get list of .ini files + id: get_files + run: | + INI_FILES=$(ls ${{ github.workspace }}/e2e/dashboards-search-suite/*.ini | jq -R -s -c 'split("\n")[:-1]') + echo "ini_files=$INI_FILES" >> $GITHUB_OUTPUT + shell: bash + + run_tests: + needs: setup + runs-on: ubuntu-latest + continue-on-error: true + if: github.event.pull_request.draft == false + strategy: + matrix: + ini_file: ${{ fromJson(needs.setup.outputs.ini_files) }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Restore Cached Node Modules + uses: actions/cache@v3 + with: + path: | + node_modules + /home/runner/.cache/Cypress + key: foo + + - name: Restore Cached Grafana Build and Dependencies + uses: actions/cache@v3 + with: + path: | + bin/ + scripts/grafana-server/ + tools/ + public/ + conf/ + e2e/test-plugins/ + devenv/ + key: ${{ runner.os }}-grafana-${{ hashFiles('go.mod', 'package-lock.json', 'Makefile', 'pkg/storage/**/*.go', 'public/app/features/search/**/*.ts', 'public/app/features/search/**/*.tsx') }} + - name: Prettify the name + run: echo "Running tests for $(basename "${{ matrix.ini_file }}")" + - name: Set the step name + id: set_file_name + run: | + FILE_NAME=$(basename "${{ matrix.ini_file }}" .ini) + echo "FILE_NAME=$FILE_NAME" >> $GITHUB_ENV + - name: Run tests for ${{ env.FILE_NAME }} + run: | + cp -rf ${{ matrix.ini_file }} ${{ github.workspace }}/scripts/grafana-server/custom.ini + yarn e2e:dashboards-search || echo "Test failed but marking as success since unified search is behind a feature flag and should not block PRs" + - name: Always succeed # This is a workaround to make the job pass even if the previous step fails + if: failure() + run: exit 0 diff --git a/e2e/dashboards-search-suite/dashboard-search.spec.ts b/e2e/dashboards-search-suite/dashboard-search.spec.ts new file mode 100644 index 00000000000..e93f116dfff --- /dev/null +++ b/e2e/dashboards-search-suite/dashboard-search.spec.ts @@ -0,0 +1,61 @@ +import { e2e } from '../utils'; + +const rowGroup = '[role="rowgroup"]'; +const row = '[role="row"]'; +const searchInput = '[data-testid="input-wrapper"]'; + +describe('Dashboard search', () => { + beforeEach(() => { + e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); + }); + + it('Search - Dashboards list', () => { + e2e.pages.Dashboards.visit(); + + toggleSearchView(); + + assertResultsCount(24); + }); + + it('Search - Filter by search input', () => { + e2e.pages.Dashboards.visit(); + + toggleSearchView(); + + assertResultsCount(24); + + // prefix match + cy.get(searchInput).type('Datasource tests - MySQL'); + assertResultsCount(2); + + cy.get(searchInput).type('{selectall}{backspace}'); // clear input + + // exact match + cy.get(searchInput).type('Datasource tests - MySQL (unittest)'); + assertResultsCount(1); + + cy.get(searchInput).type('{selectall}{backspace}'); // clear input + + // suffix match + cy.get(searchInput).type('- MySQL'); + assertResultsCount(1); + }); +}); + +const assertResultsCount = (length: number) => { + e2e.pages.SearchDashboards.table().should('exist'); + + const table = e2e.pages.SearchDashboards.table(); + const group = table.find(rowGroup); + group.should('have.length', 1); + const rows = group.find(row); + rows.should('have.length', length); +}; + +const toggleSearchView = () => { + e2e.pages.Dashboards.toggleView().each((e, i) => { + if (i === 1) { + cy.wrap(e).click(); + } + }); +}; diff --git a/e2e/dashboards-search-suite/mode0.ini b/e2e/dashboards-search-suite/mode0.ini new file mode 100644 index 00000000000..752638f24f2 --- /dev/null +++ b/e2e/dashboards-search-suite/mode0.ini @@ -0,0 +1,19 @@ +[server] + +[feature_toggles] +kubernetesFolders = true +unifiedStorageSearch = true +unifiedStorageSearchUI = true +grafanaAPIServerWithExperimentalAPIs = true +kubernetesDashboardsAPI = true +kubernetesCliDashboards = true +unifiedStorageSearchSprinkles = true +kubernetesFoldersServiceV2 = true +unifiedStorageSearchPermissionFiltering = true +kubernetesClientDashboardsFolders = true + +[unified_storage.folders.folder.grafana.app] +dualWriterMode = 0 + +[unified_storage.dashboards.dashboard.grafana.app] +dualWriterMode = 0 \ No newline at end of file diff --git a/e2e/dashboards-search-suite/mode1.ini b/e2e/dashboards-search-suite/mode1.ini new file mode 100644 index 00000000000..8f8f80e8e30 --- /dev/null +++ b/e2e/dashboards-search-suite/mode1.ini @@ -0,0 +1,19 @@ +[server] + +[feature_toggles] +kubernetesFolders = true +unifiedStorageSearch = true +unifiedStorageSearchUI = true +grafanaAPIServerWithExperimentalAPIs = true +kubernetesDashboardsAPI = true +kubernetesCliDashboards = true +unifiedStorageSearchSprinkles = true +kubernetesFoldersServiceV2 = true +unifiedStorageSearchPermissionFiltering = true +kubernetesClientDashboardsFolders = true + +[unified_storage.folders.folder.grafana.app] +dualWriterMode = 1 + +[unified_storage.dashboards.dashboard.grafana.app] +dualWriterMode = 1 \ No newline at end of file diff --git a/e2e/dashboards-search-suite/mode2-legacy-search-api.ini b/e2e/dashboards-search-suite/mode2-legacy-search-api.ini new file mode 100644 index 00000000000..38590b40a67 --- /dev/null +++ b/e2e/dashboards-search-suite/mode2-legacy-search-api.ini @@ -0,0 +1,19 @@ +[server] + +[feature_toggles] +kubernetesFolders = true +unifiedStorageSearch = true +unifiedStorageSearchUI = false +grafanaAPIServerWithExperimentalAPIs = true +kubernetesDashboardsAPI = true +kubernetesCliDashboards = true +unifiedStorageSearchSprinkles = true +kubernetesFoldersServiceV2 = true +unifiedStorageSearchPermissionFiltering = true +kubernetesClientDashboardsFolders = true + +[unified_storage.folders.folder.grafana.app] +dualWriterMode = 2 + +[unified_storage.dashboards.dashboard.grafana.app] +dualWriterMode = 2 \ No newline at end of file diff --git a/e2e/dashboards-search-suite/mode2.ini b/e2e/dashboards-search-suite/mode2.ini new file mode 100644 index 00000000000..c1d5890d6f4 --- /dev/null +++ b/e2e/dashboards-search-suite/mode2.ini @@ -0,0 +1,19 @@ +[server] + +[feature_toggles] +kubernetesFolders = true +unifiedStorageSearch = true +unifiedStorageSearchUI = true +grafanaAPIServerWithExperimentalAPIs = true +kubernetesDashboardsAPI = true +kubernetesCliDashboards = true +unifiedStorageSearchSprinkles = true +kubernetesFoldersServiceV2 = true +unifiedStorageSearchPermissionFiltering = true +kubernetesClientDashboardsFolders = true + +[unified_storage.folders.folder.grafana.app] +dualWriterMode = 2 + +[unified_storage.dashboards.dashboard.grafana.app] +dualWriterMode = 2 \ No newline at end of file diff --git a/e2e/dashboards-search-suite/mode3.ini b/e2e/dashboards-search-suite/mode3.ini new file mode 100644 index 00000000000..e027f7e429f --- /dev/null +++ b/e2e/dashboards-search-suite/mode3.ini @@ -0,0 +1,19 @@ +[server] + +[feature_toggles] +kubernetesFolders = true +unifiedStorageSearch = true +unifiedStorageSearchUI = true +grafanaAPIServerWithExperimentalAPIs = true +kubernetesDashboardsAPI = true +kubernetesCliDashboards = true +unifiedStorageSearchSprinkles = true +kubernetesFoldersServiceV2 = true +unifiedStorageSearchPermissionFiltering = true +kubernetesClientDashboardsFolders = true + +[unified_storage.folders.folder.grafana.app] +dualWriterMode = 3 + +[unified_storage.dashboards.dashboard.grafana.app] +dualWriterMode = 3 \ No newline at end of file diff --git a/e2e/dashboards-search-suite/mode4.ini b/e2e/dashboards-search-suite/mode4.ini new file mode 100644 index 00000000000..4c00fd10c47 --- /dev/null +++ b/e2e/dashboards-search-suite/mode4.ini @@ -0,0 +1,19 @@ +[server] + +[feature_toggles] +kubernetesFolders = true +unifiedStorageSearch = true +unifiedStorageSearchUI = true +grafanaAPIServerWithExperimentalAPIs = true +kubernetesDashboardsAPI = true +kubernetesCliDashboards = true +unifiedStorageSearchSprinkles = true +kubernetesFoldersServiceV2 = true +unifiedStorageSearchPermissionFiltering = true +kubernetesClientDashboardsFolders = true + +[unified_storage.folders.folder.grafana.app] +dualWriterMode = 4 + +[unified_storage.dashboards.dashboard.grafana.app] +dualWriterMode = 4 \ No newline at end of file diff --git a/e2e/dashboards-search-suite/mode5.ini b/e2e/dashboards-search-suite/mode5.ini new file mode 100644 index 00000000000..277d4cdf1c1 --- /dev/null +++ b/e2e/dashboards-search-suite/mode5.ini @@ -0,0 +1,19 @@ +[server] + +[feature_toggles] +kubernetesFolders = true +unifiedStorageSearch = true +unifiedStorageSearchUI = true +grafanaAPIServerWithExperimentalAPIs = true +kubernetesDashboardsAPI = true +kubernetesCliDashboards = true +unifiedStorageSearchSprinkles = true +kubernetesFoldersServiceV2 = true +unifiedStorageSearchPermissionFiltering = true +kubernetesClientDashboardsFolders = true + +[unified_storage.folders.folder.grafana.app] +dualWriterMode = 5 + +[unified_storage.dashboards.dashboard.grafana.app] +dualWriterMode = 5 \ No newline at end of file diff --git a/e2e/run-suite b/e2e/run-suite index ef649a6c6c4..28ed2f6bd8d 100755 --- a/e2e/run-suite +++ b/e2e/run-suite @@ -29,6 +29,7 @@ testFilesForSingleSuite="*.spec.ts" rootForEnterpriseSuite="./e2e/extensions-suite" rootForOldArch="./e2e/old-arch" rootForKubernetesDashboards="./e2e/dashboards-suite" +rootForSearchDashboards="./e2e/dashboards-search-suite" declare -A cypressConfig=( [screenshotsFolder]=./e2e/"${args[0]}"/screenshots @@ -130,6 +131,24 @@ case "$1" in ;; esac ;; + "dashboards-search") + env[kubernetesDashboards]=true + cypressConfig[specPattern]=$rootForSearchDashboards/$testFilesForSingleSuite + cypressConfig[video]=false + case "$2" in + "debug") + echo -e "Debug mode" + env[SLOWMO]=1 + PARAMS="--no-exit" + enterpriseSuite=$(basename "${args[2]}") + ;; + "dev") + echo "Dev mode" + CMD="cypress open" + enterpriseSuite=$(basename "${args[2]}") + ;; + esac + ;; "enterprise-smtp") env[SMTP_PLUGIN_ENABLED]=true cypressConfig[specPattern]=./e2e/extensions/enterprise/smtp-suite/$testFilesForSingleSuite diff --git a/package.json b/package.json index 21e0b67bc64..e38de177c7f 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "e2e": "./e2e/start-and-run-suite", "e2e:old-arch": "./e2e/start-and-run-suite old-arch", "e2e:schema-v2": "./e2e/start-and-run-suite dashboards-schema-v2", + "e2e:dashboards-search": "./e2e/start-and-run-suite dashboards-search", "e2e:debug": "./e2e/start-and-run-suite debug", "e2e:dev": "./e2e/start-and-run-suite dev", "e2e:benchmark:live": "./e2e/start-and-run-suite benchmark live", diff --git a/packages/grafana-e2e-selectors/src/selectors/pages.ts b/packages/grafana-e2e-selectors/src/selectors/pages.ts index 2e63f0f21c4..bd45587a7bc 100644 --- a/packages/grafana-e2e-selectors/src/selectors/pages.ts +++ b/packages/grafana-e2e-selectors/src/selectors/pages.ts @@ -569,6 +569,9 @@ export const versionedPages = { dashboards: { '10.2.0': (title: string) => `Dashboard search item ${title}`, }, + toggleView: { + [MIN_GRAFANA_VERSION]: 'data-testid radio-button', + }, }, SaveDashboardAsModal: { newName: { @@ -945,6 +948,11 @@ export const versionedPages = { }, }, }, + SearchDashboards: { + table: { + '10.2.0': 'Search results table', + }, + }, Search: { url: { '9.3.0': '/?search=openn',