Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[scout] add ci pipeline #202707

Merged
merged 24 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b06b1e3
[kbn/scout] update script to use tagging
dmlemeshko Dec 3, 2024
2196b50
[ci] add pipeline to run stateful tests
dmlemeshko Dec 3, 2024
3bf06d8
Merge remote-tracking branch 'upstream/main' into scout/add-ci-pipeline
dmlemeshko Dec 3, 2024
15db0aa
refactor and add serverless tests
dmlemeshko Dec 3, 2024
11648e6
exclude security project where viewer has no permission to index
dmlemeshko Dec 3, 2024
21828c6
Merge branch 'main' into scout/add-ci-pipeline
dmlemeshko Dec 3, 2024
d82f4cd
bump timeout to 30 min
dmlemeshko Dec 3, 2024
ffa7a7b
Merge branch 'scout/add-ci-pipeline' of github.com:dmlemeshko/kibana …
dmlemeshko Dec 3, 2024
2774f7b
update script
dmlemeshko Dec 3, 2024
9fe0a76
Merge remote-tracking branch 'upstream/main' into scout/add-ci-pipeline
dmlemeshko Dec 4, 2024
b1feb44
Merge remote-tracking branch 'upstream/main' into scout/add-ci-pipeline
dmlemeshko Dec 5, 2024
3a966c4
update config
dmlemeshko Dec 6, 2024
7277a38
Merge remote-tracking branch 'upstream/main' into scout/add-ci-pipeline
dmlemeshko Dec 9, 2024
ea67b2d
[kbn-scout] fix svl configs to run Kibana build
dmlemeshko Dec 9, 2024
a250a59
update config type
dmlemeshko Dec 9, 2024
040f224
Merge remote-tracking branch 'upstream/main' into scout/add-ci-pipeline
dmlemeshko Dec 9, 2024
7fb9639
update ci script
dmlemeshko Dec 9, 2024
2b49bff
update pipeline script
dmlemeshko Dec 9, 2024
ffae7d3
Merge branch 'main' into scout/add-ci-pipeline
dmlemeshko Dec 9, 2024
a514f73
update pipeline
dmlemeshko Dec 9, 2024
302ad67
improve script
dmlemeshko Dec 10, 2024
1a06832
Merge branch 'scout/add-ci-pipeline' of github.com:dmlemeshko/kibana …
dmlemeshko Dec 10, 2024
868095c
Merge branch 'main' into scout/add-ci-pipeline
dmlemeshko Dec 10, 2024
ca92b74
Merge branch 'main' into scout/add-ci-pipeline
dmlemeshko Dec 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .buildkite/pipelines/pull_request/scout_ui_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
steps:
- command: .buildkite/scripts/steps/functional/scout_ui_tests.sh
label: 'Scout UI Tests'
agents:
machineType: n2-standard-4
preemptible: true
depends_on:
- build
- quick_checks
- checks
- linting
- linting_with_types
- check_types
- check_oas_snapshot
timeout_in_minutes: 30
retry:
automatic:
- exit_status: '-1'
limit: 3
10 changes: 10 additions & 0 deletions .buildkite/scripts/pipelines/pull_request/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,16 @@ const getPipeline = (filename: string, removeSteps = true) => {
);
}

if (
(await doAnyChangesMatch([
/^x-pack\/plugins\/discover_enhanced\/ui_tests/,
/^packages\/kbn-scout/,
])) ||
GITHUB_PR_LABELS.includes('ci:scout-tests')
) {
pipeline.push(getPipeline('.buildkite/pipelines/pull_request/scout_ui_tests.yml'));
}

pipeline.push(getPipeline('.buildkite/pipelines/pull_request/post_build.yml'));

// remove duplicated steps
Expand Down
31 changes: 31 additions & 0 deletions .buildkite/scripts/steps/functional/scout_ui_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env bash

set -euo pipefail

source .buildkite/scripts/steps/functional/common.sh

export JOB=kibana-scout-ui-tests

echo "--- Stateful: 'discover_enhanced' plugin UI Tests"
node scripts/scout_test \
--stateful \
--config x-pack/plugins/discover_enhanced/ui_tests/playwright.config.ts \
--kibana-install-dir "$KIBANA_BUILD_LOCATION"

echo "--- Serverless Elasticsearch: 'discover_enhanced' plugin UI Tests"
node scripts/scout_test \
--serverless=es \
--config x-pack/plugins/discover_enhanced/ui_tests/playwright.config.ts \
--kibana-install-dir "$KIBANA_BUILD_LOCATION"

echo "--- Serverless Observability: 'discover_enhanced' plugin UI Tests"
node scripts/scout_test \
--serverless=oblt \
--config x-pack/plugins/discover_enhanced/ui_tests/playwright.config.ts \
--kibana-install-dir "$KIBANA_BUILD_LOCATION"

echo "--- Serverless Security: 'discover_enhanced' plugin UI Tests"
node scripts/scout_test \
--serverless=security \
--config x-pack/plugins/discover_enhanced/ui_tests/playwright.config.ts \
--kibana-install-dir "$KIBANA_BUILD_LOCATION"
2 changes: 1 addition & 1 deletion packages/kbn-scout/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

export { startServersCli, runTestsCli } from './src/cli';
export { expect, test, createPlaywrightConfig, createLazyPageObject } from './src/playwright';
export { expect, test, tags, createPlaywrightConfig, createLazyPageObject } from './src/playwright';
export type {
ScoutPage,
ScoutPlaywrightOptions,
Expand Down
4 changes: 3 additions & 1 deletion packages/kbn-scout/src/playwright/fixtures/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import { mergeTests } from '@playwright/test';
import { browserAuthFixture } from './browser_auth';
import { scoutPageFixture } from './page';
import { pageObjectsFixture } from './page_objects';
import { validateTagsFixture } from './validate_tags';

export const scoutTestFixtures = mergeTests(
browserAuthFixture,
scoutPageFixture,
pageObjectsFixture
pageObjectsFixture,
validateTagsFixture
);
35 changes: 35 additions & 0 deletions packages/kbn-scout/src/playwright/fixtures/test/validate_tags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { test as base } from '@playwright/test';
import { tags } from '../../tags';

const supportedTags = tags.DEPLOYMENT_AGNOSTIC;

export const validateTagsFixture = base.extend<{ validateTags: void }>({
validateTags: [
async ({}, use, testInfo) => {
if (testInfo.tags.length === 0) {
throw new Error(`At least one tag is required: ${supportedTags.join(', ')}`);
}

const invalidTags = testInfo.tags.filter((tag: string) => !supportedTags.includes(tag));
if (invalidTags.length > 0) {
throw new Error(
`Unsupported tag(s) found in test suite "${testInfo.title}": ${invalidTags.join(
', '
)}. ` + `Supported tags are: ${supportedTags.join(', ')}.`
);
}

await use();
},
{ auto: true },
],
});
Comment on lines +15 to +35
Copy link
Member Author

@dmlemeshko dmlemeshko Dec 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal: notify test author about requirement of tags definition and validating its correctness. I also introduces aliases through tags variable to minimize spelling error.

Note: it only work when you run tests in IDE; when you run tests via npx playwright test --grep <tag> playwright is doing filtering in advance and no tags or incorrect tags will cause test to be filtered out.

That said, for CI we will need to come up with some script to validate tags being defined. But I still believe there is a benefit of raising error during test development process

Copy link
Member Author

@dmlemeshko dmlemeshko Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example: adding non-supported tag @xyz like

test.describe(
  'Discover app - saved searches',
  { tag: ['@ess', '@svlSearch', '@svlOblt', '@xyz'] },
  () => {

will lead to runtime error

image

3 changes: 3 additions & 0 deletions packages/kbn-scout/src/playwright/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ export type {
ScoutPage,
EsArchiverFixture,
} from './fixtures';

// use to tag tests
export { tags } from './tags';
9 changes: 8 additions & 1 deletion packages/kbn-scout/src/playwright/runner/run_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ import { loadServersConfig } from '../../config';
import { silence } from '../../common';
import { RunTestsOptions } from './flags';
import { getExtraKbnOpts } from '../../servers/run_kibana_server';
import { getPlaywrightGrepTag } from '../utils';

export async function runTests(log: ToolingLog, options: RunTestsOptions) {
const runStartTime = Date.now();
const reportTime = getTimeReporter(log, 'scripts/scout_test');

const config = await loadServersConfig(options.mode, log);
const playwrightGrepTag = getPlaywrightGrepTag(config);
const playwrightConfigPath = options.configPath;

await withProcRunner(log, async (procs) => {
Expand Down Expand Up @@ -59,7 +61,12 @@ export async function runTests(log: ToolingLog, options: RunTestsOptions) {
// Running 'npx playwright test --config=${playwrightConfigPath}'
await procs.run(`playwright`, {
cmd: resolve(REPO_ROOT, './node_modules/.bin/playwright'),
args: ['test', `--config=${playwrightConfigPath}`, ...(options.headed ? ['--headed'] : [])],
args: [
'test',
`--config=${playwrightConfigPath}`,
`--grep=${playwrightGrepTag}`,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing one of @ess , svlSearch, ... tags to playwright runner

...(options.headed ? ['--headed'] : []),
],
cwd: resolve(REPO_ROOT),
env: {
...process.env,
Expand Down
27 changes: 27 additions & 0 deletions packages/kbn-scout/src/playwright/tags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

const SERVERLESS_ONLY = ['@svlSecurity', '@svlOblt', '@svlSearch'];
const ESS_ONLY = ['@ess'];
const DEPLOYMENT_AGNOSTIC = SERVERLESS_ONLY.concat(ESS_ONLY);

export const tags = {
ESS_ONLY,
SERVERLESS_ONLY,
DEPLOYMENT_AGNOSTIC,
};

export const tagsByMode = {
stateful: '@ess',
serverless: {
es: '@svlSearch',
oblt: '@svlOblt',
security: '@svlSecurity',
},
};
9 changes: 9 additions & 0 deletions packages/kbn-scout/src/playwright/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
*/

import moment from 'moment';
import { Config } from '../../config';
import { tagsByMode } from '../tags';

export const serviceLoadedMsg = (name: string) => `scout service loaded: ${name}`;

Expand All @@ -18,3 +20,10 @@ export const isValidUTCDate = (date: string): boolean => {
export function formatTime(date: string, fmt: string = 'MMM D, YYYY @ HH:mm:ss.SSS') {
return moment.utc(date, fmt).format();
}

export const getPlaywrightGrepTag = (config: Config): string => {
const serversConfig = config.getTestServersConfig();
return serversConfig.serverless
? tagsByMode.serverless[serversConfig.projectType!]
: tagsByMode.stateful;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
* 2.0.
*/

import { expect } from '@kbn/scout';
import { expect, tags } from '@kbn/scout';
import { test, testData } from '../fixtures';

test.describe('Discover app - errors', { tag: ['@ess'] }, () => {
test.describe('Discover app - errors', { tag: tags.ESS_ONLY }, () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alias to simplify the process and avoid spelling/incorrect tagging issues

test.beforeAll(async ({ esArchiver, kbnClient, uiSettings }) => {
await kbnClient.savedObjects.clean({ types: ['search', 'index-pattern'] });
await esArchiver.loadIfNeeded(testData.ES_ARCHIVES.LOGSTASH);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { ScoutWorkerFixtures, expect } from '@kbn/scout';
import { ScoutWorkerFixtures, expect, tags } from '@kbn/scout';
import { test, testData } from '../fixtures';

const createSavedSearch = async (
Expand Down Expand Up @@ -37,66 +37,62 @@ const createSavedSearch = async (
],
});

test.describe(
'Discover app - saved search embeddable',
{ tag: ['@ess', '@svlSecurity', '@svlOblt', '@svlSearch'] },
() => {
const SAVED_SEARCH_TITLE = 'TempSearch';
const SAVED_SEARCH_ID = '90943e30-9a47-11e8-b64d-95841ca0b247';
test.beforeAll(async ({ esArchiver, kbnClient, uiSettings }) => {
await esArchiver.loadIfNeeded(testData.ES_ARCHIVES.LOGSTASH);
await kbnClient.importExport.load(testData.KBN_ARCHIVES.DASHBOARD_DRILLDOWNS);
await uiSettings.set({
defaultIndex: testData.DATA_VIEW_ID.LOGSTASH, // TODO: investigate why it is required for `node scripts/playwright_test.js` run
'timepicker:timeDefaults': `{ "from": "${testData.LOGSTASH_DEFAULT_START_TIME}", "to": "${testData.LOGSTASH_DEFAULT_END_TIME}"}`,
});
test.describe('Discover app - saved search embeddable', { tag: tags.DEPLOYMENT_AGNOSTIC }, () => {
const SAVED_SEARCH_TITLE = 'TempSearch';
const SAVED_SEARCH_ID = '90943e30-9a47-11e8-b64d-95841ca0b247';
test.beforeAll(async ({ esArchiver, kbnClient, uiSettings }) => {
await esArchiver.loadIfNeeded(testData.ES_ARCHIVES.LOGSTASH);
await kbnClient.importExport.load(testData.KBN_ARCHIVES.DASHBOARD_DRILLDOWNS);
await uiSettings.set({
defaultIndex: testData.DATA_VIEW_ID.LOGSTASH, // TODO: investigate why it is required for `node scripts/playwright_test.js` run
'timepicker:timeDefaults': `{ "from": "${testData.LOGSTASH_DEFAULT_START_TIME}", "to": "${testData.LOGSTASH_DEFAULT_END_TIME}"}`,
});
});

test.afterAll(async ({ kbnClient, uiSettings }) => {
await uiSettings.unset('defaultIndex', 'timepicker:timeDefaults');
await kbnClient.savedObjects.cleanStandardList();
});
test.afterAll(async ({ kbnClient, uiSettings }) => {
await uiSettings.unset('defaultIndex', 'timepicker:timeDefaults');
await kbnClient.savedObjects.cleanStandardList();
});

test.beforeEach(async ({ browserAuth, pageObjects }) => {
await browserAuth.loginAsPrivilegedUser();
await pageObjects.dashboard.goto();
});
test.beforeEach(async ({ browserAuth, pageObjects }) => {
await browserAuth.loginAsPrivilegedUser();
await pageObjects.dashboard.goto();
});

test('should allow removing the dashboard panel after the underlying saved search has been deleted', async ({
test('should allow removing the dashboard panel after the underlying saved search has been deleted', async ({
kbnClient,
page,
pageObjects,
}) => {
await pageObjects.dashboard.openNewDashboard();
await createSavedSearch(
kbnClient,
page,
pageObjects,
}) => {
await pageObjects.dashboard.openNewDashboard();
await createSavedSearch(
kbnClient,
SAVED_SEARCH_ID,
SAVED_SEARCH_TITLE,
testData.DATA_VIEW_ID.LOGSTASH
);
await pageObjects.dashboard.addPanelFromLibrary(SAVED_SEARCH_TITLE);
await page.testSubj.locator('savedSearchTotalDocuments').waitFor({
state: 'visible',
});
SAVED_SEARCH_ID,
SAVED_SEARCH_TITLE,
testData.DATA_VIEW_ID.LOGSTASH
);
await pageObjects.dashboard.addPanelFromLibrary(SAVED_SEARCH_TITLE);
await page.testSubj.locator('savedSearchTotalDocuments').waitFor({
state: 'visible',
});

await pageObjects.dashboard.saveDashboard('Dashboard with deleted saved search');
await kbnClient.savedObjects.delete({
type: 'search',
id: SAVED_SEARCH_ID,
});
await pageObjects.dashboard.saveDashboard('Dashboard with deleted saved search');
await kbnClient.savedObjects.delete({
type: 'search',
id: SAVED_SEARCH_ID,
});

await page.reload();
await page.waitForLoadingIndicatorHidden();
await expect(
page.testSubj.locator('embeddableError'),
'Embeddable error should be displayed'
).toBeVisible();
await page.reload();
await page.waitForLoadingIndicatorHidden();
await expect(
page.testSubj.locator('embeddableError'),
'Embeddable error should be displayed'
).toBeVisible();

await pageObjects.dashboard.removePanel('embeddableError');
await expect(
page.testSubj.locator('embeddableError'),
'Embeddable error should not be displayed'
).toBeHidden();
});
}
);
await pageObjects.dashboard.removePanel('embeddableError');
await expect(
page.testSubj.locator('embeddableError'),
'Embeddable error should not be displayed'
).toBeHidden();
});
});
Loading