mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[9.0] [Scout] add painless lab (#219124)
# Backport This will backport the following commits from `main` to `9.0`: - [[Scout] add painless lab](https://github.com/elastic/kibana/pull/216446) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Charis Kalpakis","email":"39087493+fake-haris@users.noreply.github.com"},"sourceCommit":{"committedDate":"2025-04-24T15:19:23Z","message":"[Scout] add painless lab","sha":"2713a79ba90500abf47d0cc73d7749410cc55b23","branchLabelMapping":{"^v9.1.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","backport:version","test:scout","v9.1.0","v8.19.0","v9.0.1"],"title":"[Scout] add painless lab","number":216446,"url":"https://github.com/elastic/kibana/pull/216446","mergeCommit":{"message":"[Scout] add painless lab","sha":"2713a79ba90500abf47d0cc73d7749410cc55b23"}},"sourceBranch":"main","suggestedTargetBranches":["8.19","9.0"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/216446","number":216446,"mergeCommit":{"message":"[Scout] add painless lab","sha":"2713a79ba90500abf47d0cc73d7749410cc55b23"}},{"branch":"8.19","label":"v8.19.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"9.0","label":"v9.0.1","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> --------- Co-authored-by: Charis Kalpakis <39087493+fake-haris@users.noreply.github.com> Co-authored-by: fake-haris <charis.kalpakis@elastic.co>
This commit is contained in:
parent
eb6f9d7ff2
commit
bb1dbf6f98
13 changed files with 219 additions and 8 deletions
|
@ -5,5 +5,6 @@ ui_tests:
|
|||
- discover_enhanced
|
||||
- maps
|
||||
- observability_onboarding
|
||||
- painless_lab
|
||||
- security_solution
|
||||
disabled:
|
||||
|
|
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -1601,6 +1601,7 @@ packages/kbn-monaco/src/esql @elastic/kibana-esql
|
|||
/x-pack/platform/plugins/shared/maps/ui_tests @elastic/appex-qa # temporarily
|
||||
/x-pack/platform/plugins/private/discover_enhanced/ui_tests/ @elastic/appex-qa # temporarily
|
||||
/x-pack/platform/plugins/private/discover_enhanced/ui_tests/tests/discover_cdp_perf.spec.ts @elastic/kibana-data-discovery # test tracks bundle size limits
|
||||
/x-pack/platform/plugins/private/painless_lab/ui_tests # temporarily
|
||||
/x-pack/test/functional/fixtures/package_registry_config.yml @elastic/appex-qa # No usages found
|
||||
/x-pack/test/functional/fixtures/kbn_archiver/packaging.json @elastic/appex-qa # No usages found
|
||||
/x-pack/test/functional/es_archives/filebeat @elastic/appex-qa
|
||||
|
|
|
@ -50,7 +50,7 @@ export const OutputPane: FunctionComponent<Props> = ({ isLoading, response }) =>
|
|||
<div className="painlessLabRightPane">
|
||||
<EuiTabbedContent
|
||||
className="painlessLabRightPane__tabs"
|
||||
data-test-subj="painlessTabs"
|
||||
data-test-subj={isLoading ? `painlessTabs-loading` : `painlessTabs-loaded`}
|
||||
size="s"
|
||||
tabs={[
|
||||
{
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"common/**/*",
|
||||
"public/**/*",
|
||||
"server/**/*",
|
||||
"ui_tests/**/*",
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/core",
|
||||
|
@ -21,6 +22,7 @@
|
|||
"@kbn/config-schema",
|
||||
"@kbn/code-editor",
|
||||
"@kbn/react-kibana-context-render",
|
||||
"@kbn/scout",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
## How to run tests
|
||||
First start the servers with
|
||||
|
||||
```bash
|
||||
// ESS
|
||||
node scripts/scout.js start-server --stateful
|
||||
```
|
||||
|
||||
Then you can run the tests multiple times in another terminal with:
|
||||
|
||||
```bash
|
||||
// ESS
|
||||
npx playwright test --config x-pack/platform/plugins/private/painless_lab/ui_tests/playwright.config.ts --project local --grep @ess
|
||||
```
|
||||
|
||||
Test results are available in `x-pack/platform/plugins/private/painless_lab/ui_tests/output`
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { test as base } from '@kbn/scout';
|
||||
import type { ScoutPage, ScoutTestFixtures, ScoutWorkerFixtures } from '@kbn/scout';
|
||||
|
||||
import { extendPageObjects, PainlessLabPageObjects } from './page_objects';
|
||||
|
||||
export interface PainlessLabTestFixtures extends ScoutTestFixtures {
|
||||
pageObjects: PainlessLabPageObjects;
|
||||
}
|
||||
|
||||
export const test = base.extend<PainlessLabTestFixtures, ScoutWorkerFixtures>({
|
||||
pageObjects: async (
|
||||
{
|
||||
pageObjects,
|
||||
page,
|
||||
}: {
|
||||
pageObjects: PainlessLabPageObjects;
|
||||
page: ScoutPage;
|
||||
},
|
||||
use: (pageObjects: PainlessLabPageObjects) => Promise<void>
|
||||
) => {
|
||||
const extendedPageObjects = extendPageObjects(pageObjects, page);
|
||||
await use(extendedPageObjects);
|
||||
},
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { PageObjects, ScoutPage, createLazyPageObject } from '@kbn/scout';
|
||||
import { PainlessLab } from './painless_lab_page';
|
||||
|
||||
export interface PainlessLabPageObjects extends PageObjects {
|
||||
painlessLab: PainlessLab;
|
||||
}
|
||||
|
||||
export function extendPageObjects(
|
||||
pageObjects: PageObjects,
|
||||
page: ScoutPage
|
||||
): PainlessLabPageObjects {
|
||||
return {
|
||||
...pageObjects,
|
||||
painlessLab: createLazyPageObject(PainlessLab, page),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Locator } from '@kbn/scout';
|
||||
import { ScoutPage } from '@kbn/scout/src/playwright';
|
||||
|
||||
export class PainlessLab {
|
||||
public editorOutputPane: Locator;
|
||||
public requestFlyoutHeader: Locator;
|
||||
public viewRequestButton: Locator;
|
||||
public flyoutResponseTab: Locator;
|
||||
|
||||
constructor(private readonly page: ScoutPage) {
|
||||
this.editorOutputPane = this.page.testSubj.locator('painlessTabs-loaded');
|
||||
this.requestFlyoutHeader = this.page.testSubj.locator('painlessLabRequestFlyoutHeader');
|
||||
this.viewRequestButton = this.page.testSubj.locator('btnViewRequest');
|
||||
this.flyoutResponseTab = this.page.locator('#response');
|
||||
}
|
||||
|
||||
async goto() {
|
||||
return this.page.gotoApp('dev_tools', { hash: 'painless_lab' });
|
||||
}
|
||||
|
||||
async waitForEditorToLoad() {
|
||||
// wait for page to be rendered
|
||||
await this.page.testSubj.locator('kibanaCodeEditor').waitFor({ state: 'visible' });
|
||||
await this.editorOutputPane.waitFor({ state: 'visible' });
|
||||
}
|
||||
|
||||
async setCodeEditorValue(value: string, nthIndex?: number): Promise<void> {
|
||||
await this.page.evaluate(
|
||||
({ editorIndex, codeEditorValue }: { editorIndex?: number; codeEditorValue: string }) => {
|
||||
const editor = (window.MonacoEnvironment as any)!.monaco!.editor;
|
||||
const textModels = editor.getModels();
|
||||
|
||||
if (editorIndex !== undefined) {
|
||||
textModels[editorIndex].setValue(codeEditorValue);
|
||||
} else {
|
||||
textModels.forEach((model: { setValue: (arg0: string) => any }) =>
|
||||
model.setValue(codeEditorValue)
|
||||
);
|
||||
}
|
||||
},
|
||||
{ editorIndex: nthIndex, codeEditorValue: value }
|
||||
);
|
||||
}
|
||||
|
||||
async getFlyoutRequestBody() {
|
||||
return this.page.testSubj.locator('painlessLabFlyoutRequest').innerText();
|
||||
}
|
||||
|
||||
async getFlyoutResponseBody() {
|
||||
const flyoutResponse = this.page.testSubj.locator('painlessLabFlyoutResponse');
|
||||
await flyoutResponse.waitFor({ state: 'visible' });
|
||||
return flyoutResponse.innerText();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createPlaywrightConfig } from '@kbn/scout';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default createPlaywrightConfig({
|
||||
testDir: './tests',
|
||||
});
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { expect, tags } from '@kbn/scout';
|
||||
import { test } from '../fixtures';
|
||||
|
||||
const space = ' ';
|
||||
const TEST_SCRIPT_RESULT = '45';
|
||||
const UPDATED_TEST_SCRIPT_RESPONSE = '"45"';
|
||||
const TEST_SCRIPT = `
|
||||
int total = 0;
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
total += i;
|
||||
}
|
||||
|
||||
return total;
|
||||
`.trim();
|
||||
const TEST_SCRIPT_REQUEST = `POST _scripts/painless/_execute
|
||||
{
|
||||
"script": {
|
||||
"source": """int total = 0;
|
||||
${space}
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
total += i;
|
||||
}
|
||||
${space}
|
||||
return total;""",
|
||||
"params": {
|
||||
"string_parameter": "string value",
|
||||
"number_parameter": 1.5,
|
||||
"boolean_parameter": true
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
test.describe('Painless Lab', { tag: tags.ESS_ONLY }, () => {
|
||||
test.beforeEach(async ({ browserAuth, pageObjects }) => {
|
||||
await browserAuth.loginAsAdmin();
|
||||
await pageObjects.painlessLab.goto();
|
||||
await pageObjects.painlessLab.waitForEditorToLoad();
|
||||
});
|
||||
|
||||
test('validate painless lab editor and request', async ({ pageObjects }) => {
|
||||
await pageObjects.painlessLab.setCodeEditorValue(TEST_SCRIPT);
|
||||
await pageObjects.painlessLab.editorOutputPane.waitFor({ state: 'visible' });
|
||||
await expect(pageObjects.painlessLab.editorOutputPane).toContainText(TEST_SCRIPT_RESULT);
|
||||
|
||||
await pageObjects.painlessLab.viewRequestButton.click();
|
||||
await expect(pageObjects.painlessLab.requestFlyoutHeader).toBeVisible();
|
||||
|
||||
expect(await pageObjects.painlessLab.getFlyoutRequestBody()).toBe(TEST_SCRIPT_REQUEST);
|
||||
|
||||
await pageObjects.painlessLab.flyoutResponseTab.click();
|
||||
expect(await pageObjects.painlessLab.getFlyoutResponseBody()).toBe(
|
||||
UPDATED_TEST_SCRIPT_RESPONSE
|
||||
);
|
||||
});
|
||||
});
|
|
@ -26,7 +26,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
it('click on the output button', async () => {
|
||||
const painlessTabsOutput = await find.byCssSelector(
|
||||
'[data-test-subj="painlessTabs"] #output'
|
||||
'[data-test-subj="painlessTabs-loaded"] #output'
|
||||
);
|
||||
await painlessTabsOutput.click();
|
||||
await a11y.testAppSnapshot();
|
||||
|
@ -34,7 +34,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
it('click on the parameters button', async () => {
|
||||
const painlessTabsParameters = await find.byCssSelector(
|
||||
'[data-test-subj="painlessTabs"] #parameters'
|
||||
'[data-test-subj="painlessTabs-loaded"] #parameters'
|
||||
);
|
||||
await painlessTabsParameters.click();
|
||||
await a11y.testAppSnapshot();
|
||||
|
@ -42,7 +42,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
it('click on the context button', async () => {
|
||||
const painlessTabsContext = await find.byCssSelector(
|
||||
'[data-test-subj="painlessTabs"] #context'
|
||||
'[data-test-subj="painlessTabs-loaded"] #context'
|
||||
);
|
||||
await painlessTabsContext.click();
|
||||
await a11y.testAppSnapshot();
|
||||
|
|
|
@ -35,7 +35,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
it('should show the editor and preview panels', async () => {
|
||||
const editor = await testSubjects.find('kibanaCodeEditor');
|
||||
const preview = await testSubjects.find('painlessTabs');
|
||||
const preview = await testSubjects.find('painlessTabs-loaded');
|
||||
|
||||
expect(await editor.isDisplayed()).to.be(true);
|
||||
expect(await preview.isDisplayed()).to.be(true);
|
||||
|
@ -45,7 +45,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await monacoEditor.setCodeEditorValue(TEST_SCRIPT);
|
||||
|
||||
await retry.try(async () => {
|
||||
const result = await testSubjects.find('painlessTabs');
|
||||
const result = await testSubjects.find('painlessTabs-loaded');
|
||||
expect(await result.getVisibleText()).to.contain(TEST_SCRIPT_RESULT.toString());
|
||||
});
|
||||
});
|
||||
|
|
|
@ -36,7 +36,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
it('should show the editor and preview panels', async () => {
|
||||
const editor = await testSubjects.find('kibanaCodeEditor');
|
||||
const preview = await testSubjects.find('painlessTabs');
|
||||
const preview = await testSubjects.find('painlessTabs-loaded');
|
||||
|
||||
expect(await editor.isDisplayed()).to.be(true);
|
||||
expect(await preview.isDisplayed()).to.be(true);
|
||||
|
@ -46,7 +46,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await monacoEditor.setCodeEditorValue(TEST_SCRIPT);
|
||||
|
||||
await retry.try(async () => {
|
||||
const result = await testSubjects.find('painlessTabs');
|
||||
const result = await testSubjects.find('painlessTabs-loaded');
|
||||
expect(await result.getVisibleText()).to.contain(TEST_SCRIPT_RESULT.toString());
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue