diff --git a/x-pack/plugins/serverless_search/public/application/components/api_key/api_key.tsx b/x-pack/plugins/serverless_search/public/application/components/api_key/api_key.tsx index 99bd547cbaca733..e79926fd4c97078 100644 --- a/x-pack/plugins/serverless_search/public/application/components/api_key/api_key.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/api_key/api_key.tsx @@ -53,7 +53,7 @@ export const ApiKeyPanel = ({ setClientApiKey }: { setClientApiKey: (value: stri /> )} {apiKey ? ( - + - {JSON.stringify(apiKey, undefined, 2)} + + {JSON.stringify(apiKey, undefined, 2)} + ) : ( @@ -104,6 +106,7 @@ export const ApiKeyPanel = ({ setClientApiKey }: { setClientApiKey: (value: stri size="s" fill onClick={() => setIsFlyoutOpen(true)} + data-test-subj="new-api-key-button" > {i18n.translate('xpack.serverlessSearch.apiKey.newButtonLabel', { @@ -120,6 +123,7 @@ export const ApiKeyPanel = ({ setClientApiKey }: { setClientApiKey: (value: stri size="s" href={http.basePath.prepend(MANAGEMENT_API_KEYS)} target="_blank" + data-test-subj="manage-api-keys-button" > {i18n.translate('xpack.serverlessSearch.apiKey.manageLabel', { defaultMessage: 'Manage', @@ -142,7 +146,10 @@ export const ApiKeyPanel = ({ setClientApiKey }: { setClientApiKey: (value: stri defaultMessage="You have {number} active keys." values={{ number: ( - 0 ? 'success' : 'warning'}> + 0 ? 'success' : 'warning'} + data-test-subj="api-keys-count-badge" + > {data.apiKeys.length} ), diff --git a/x-pack/plugins/serverless_search/public/application/components/api_key/basic_setup_form.tsx b/x-pack/plugins/serverless_search/public/application/components/api_key/basic_setup_form.tsx index 68262b11950c975..a130e08654bcb0b 100644 --- a/x-pack/plugins/serverless_search/public/application/components/api_key/basic_setup_form.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/api_key/basic_setup_form.tsx @@ -60,6 +60,7 @@ export const BasicSetupForm: React.FC = ({ isLoading={isLoading} value={name} onChange={(e) => onChangeName(e.currentTarget.value)} + data-test-subj="create-api-key-name" /> = ({ defaultMessage: 'User', })} > - onChangeName(e.currentTarget.value)} - /> + {}} /> = ({ label: i18n.translate('xpack.serverlessSearch.apiKey.expiresField.neverLabel', { defaultMessage: 'Never', }), + 'data-test-subj': 'create-api-key-expires-never-radio', }, { id: 'days', label: i18n.translate('xpack.serverlessSearch.apiKey.expiresField.daysLabel', { defaultMessage: 'in days', }), + 'data-test-subj': 'create-api-key-expires-days-radio', }, ]} idSelected={expires === null ? 'never' : 'days'} onChange={(id) => onChangeExpires(id === 'never' ? null : DEFAULT_EXPIRES_VALUE)} + data-test-subj="create-api-key-expires-radio" /> {expires !== null && ( @@ -149,6 +148,7 @@ export const BasicSetupForm: React.FC = ({ defaultValue={expires} min={1} onChange={(e) => onChangeExpires(e.currentTarget.value)} + data-test-subj="create-api-key-expires-days-number-field" /> )} diff --git a/x-pack/plugins/serverless_search/public/application/components/api_key/create_api_key_flyout.tsx b/x-pack/plugins/serverless_search/public/application/components/api_key/create_api_key_flyout.tsx index 1110f4e214561f9..04d8e366df85871 100644 --- a/x-pack/plugins/serverless_search/public/application/components/api_key/create_api_key_flyout.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/api_key/create_api_key_flyout.tsx @@ -171,6 +171,7 @@ export const CreateApiKeyFlyout: React.FC = ({ title={i18n.translate('xpack.serverlessSearch.apiKey.flyout.errorTitle', { defaultMessage: 'Error creating API key', })} + data-test-subj="create-api-key-error-callout" > {createError} @@ -256,6 +257,7 @@ export const CreateApiKeyFlyout: React.FC = ({ label={privilegesEnabled ? ENABLED_LABEL : DISABLED_LABEL} checked={privilegesEnabled} onChange={togglePrivileges} + data-test-subj="create-api-role-descriptors-switch" /> } forceState={privilegesOpen} @@ -310,6 +312,7 @@ export const CreateApiKeyFlyout: React.FC = ({ label={metadataEnabled ? ENABLED_LABEL : DISABLED_LABEL} checked={metadataEnabled} onChange={toggleMetadata} + data-test-subj="create-api-metadata-switch" /> } forceState={metadataOpen} @@ -331,14 +334,24 @@ export const CreateApiKeyFlyout: React.FC = ({ - + {CANCEL_LABEL} - + {i18n.translate('xpack.serverlessSearch.apiKey.flyOutCreateLabel', { defaultMessage: 'Create API Key', })} diff --git a/x-pack/plugins/serverless_search/public/application/components/api_key/metadata_form.tsx b/x-pack/plugins/serverless_search/public/application/components/api_key/metadata_form.tsx index 8d05da3d47a8bfc..af7c78e5408afad 100644 --- a/x-pack/plugins/serverless_search/public/application/components/api_key/metadata_form.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/api_key/metadata_form.tsx @@ -23,7 +23,7 @@ export const MetadataForm: React.FC = ({ error, }) => { return ( - <> +
{i18n.translate('xpack.serverlessSearch.apiKey.metadataLinkLabel', { defaultMessage: 'Learn how to structure role metadata', @@ -44,6 +44,6 @@ export const MetadataForm: React.FC = ({ onChange={(e) => onChangeMetadata(e)} value={metadata} /> - +
); }; diff --git a/x-pack/plugins/serverless_search/public/application/components/api_key/security_privileges_form.tsx b/x-pack/plugins/serverless_search/public/application/components/api_key/security_privileges_form.tsx index 9ebee2ad3775e92..a92224b2b45d6bf 100644 --- a/x-pack/plugins/serverless_search/public/application/components/api_key/security_privileges_form.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/api_key/security_privileges_form.tsx @@ -23,7 +23,7 @@ export const SecurityPrivilegesForm: React.FC = ({ error, }) => { return ( - <> +
{i18n.translate('xpack.serverlessSearch.apiKey.roleDescriptorsLinkLabel', { defaultMessage: 'Learn how to structure role descriptors', @@ -44,6 +44,6 @@ export const SecurityPrivilegesForm: React.FC = ({ onChange={(e) => onChangeRoleDescriptors(e)} value={roleDescriptors} /> - +
); }; diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/language_grid.tsx b/x-pack/plugins/serverless_search/public/application/components/languages/language_grid.tsx index 58f8d0742971ffc..85062e5552df7f0 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/language_grid.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/languages/language_grid.tsx @@ -38,7 +38,7 @@ export const LanguageGrid: React.FC = ({ const columns = isXLarge ? 3 : isLarge ? 2 : 1; return ( - + {languages.map((language) => ( = ({ } onClick={() => setSelectedLanguage(language)} color={language.id === selectedLanguage ? 'primary' : 'plain'} + data-test-subj={`${language.id}-client-panel`} > diff --git a/x-pack/plugins/serverless_search/public/application/components/overview.tsx b/x-pack/plugins/serverless_search/public/application/components/overview.tsx index beb5cea5d19c1d9..cd5faa1ba63e482 100644 --- a/x-pack/plugins/serverless_search/public/application/components/overview.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/overview.tsx @@ -84,7 +84,11 @@ export const ElasticsearchOverview = () => {
- + { - + { sharePlugin={share} /> - + { })} /> - + { })} /> - + { })} /> - + { })} /> - + { sharePlugin={share} /> - + { })} /> - + { + return await (await row.findByTestSubject('indexTableIndexNameLink')).getVisibleText(); + }) + ); + expect(indexNames.some((i) => i === indexName)).to.be(true); + }, }; } diff --git a/x-pack/test_serverless/functional/page_objects/svl_search_landing_page.ts b/x-pack/test_serverless/functional/page_objects/svl_search_landing_page.ts index 3cf69b896983dd9..672ec3aeada49db 100644 --- a/x-pack/test_serverless/functional/page_objects/svl_search_landing_page.ts +++ b/x-pack/test_serverless/functional/page_objects/svl_search_landing_page.ts @@ -5,6 +5,7 @@ * 2.0. */ +import expect from '@kbn/expect'; import { FtrProviderContext } from '../ftr_provider_context'; export function SvlSearchLandingPageProvider({ getService }: FtrProviderContext) { @@ -14,5 +15,65 @@ export function SvlSearchLandingPageProvider({ getService }: FtrProviderContext) async assertSvlSearchSideNavExists() { await testSubjects.existOrFail('svlSearchSideNav'); }, + languageClients: { + async expectLanguagePanelExists(id: string) { + await testSubjects.existOrFail(`${id}-client-panel`); + }, + async expectLanguageSelected(id: string) { + await testSubjects.existOrFail(`${id}-client-panel`); + const langPanel = await testSubjects.find(`${id}-client-panel`); + expect(await langPanel.elementHasClass('euiPanel--primary')).to.be(true); + }, + async selectLanguage(id: string) { + await testSubjects.click(`${id}-client-panel`); + }, + }, + apiKeys: { + async openCreateFlyout() { + await testSubjects.click('new-api-key-button'); + await testSubjects.existOrFail('create-api-key-submit'); + }, + async setApiKeyName(value: string) { + await testSubjects.existOrFail('create-api-key-name'); + await testSubjects.setValue('create-api-key-name', value); + }, + async selectNeverExpires() { + await ( + await ( + await testSubjects.find('create-api-key-expires-never-radio') + ).findByTagName('label') + ).click(); + await testSubjects.missingOrFail('create-api-key-expires-days-number-field'); + }, + async createApiKeySubmitAndSuccess() { + await testSubjects.click('create-api-key-submit'); + await testSubjects.existOrFail('api-key-create-success-panel'); + }, + async createApiKeySubmitAndError() { + await testSubjects.click('create-api-key-submit'); + await testSubjects.existOrFail('create-api-key-error-callout'); + }, + async createApiKeyCancel() { + await testSubjects.click('create-api-key-cancel'); + }, + async createApiKeyToggleMetadataSwitch() { + await testSubjects.click('create-api-metadata-switch'); + }, + async expectMetadataEditorToExist() { + await testSubjects.existOrFail('create-api-metadata-code-editor-container'); + }, + async createApiKeyToggleRoleDescriptorsSwitch() { + await testSubjects.click('create-api-role-descriptors-switch'); + }, + async expectRoleDescriptorsEditorToExist() { + await testSubjects.existOrFail('create-api-role-descriptors-code-editor-container'); + }, + async setRoleDescriptorsValue(value: string) { + await testSubjects.existOrFail('create-api-role-descriptors-code-editor-container'); + await testSubjects.setValue('kibanaCodeEditor', value, { + clearWithKeyboard: true, + }); + }, + }, }; } diff --git a/x-pack/test_serverless/functional/services/svl_search_navigation.ts b/x-pack/test_serverless/functional/services/svl_search_navigation.ts index dd0c1515a429cd9..db2992287c15883 100644 --- a/x-pack/test_serverless/functional/services/svl_search_navigation.ts +++ b/x-pack/test_serverless/functional/services/svl_search_navigation.ts @@ -13,14 +13,13 @@ export function SvlSearchNavigationServiceProvider({ }: FtrProviderContext) { const retry = getService('retry'); const PageObjects = getPageObjects(['common']); + const testSubjects = getService('testSubjects'); return { async navigateToLandingPage() { await retry.tryForTime(60 * 1000, async () => { await PageObjects.common.navigateToApp('landingPage'); - // The getting started page is currently empty, so there's nothing we could - // assert on. Once something exists here, we should add back a check. - // await testSubjects.existOrFail('svlSearchOverviewPage', { timeout: 2000 }); + await testSubjects.existOrFail('svlSearchOverviewPage', { timeout: 2000 }); }); }, }; diff --git a/x-pack/test_serverless/functional/test_suites/common/management/index_management/indices.ts b/x-pack/test_serverless/functional/test_suites/common/management/index_management/indices.ts index e0ef321017b9e3a..efcddefda84e127 100644 --- a/x-pack/test_serverless/functional/test_suites/common/management/index_management/indices.ts +++ b/x-pack/test_serverless/functional/test_suites/common/management/index_management/indices.ts @@ -28,5 +28,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const url = await browser.getCurrentUrl(); expect(url).to.contain(`/indices`); }); + it('can create an index', async () => { + const testIndexName = `index-ftr-test-${Math.random()}`; + await pageObjects.indexManagement.clickCreateIndexButton(); + await pageObjects.indexManagement.setCreateIndexName(testIndexName); + await pageObjects.indexManagement.clickCreateIndexSaveButton(); + await pageObjects.indexManagement.expectIndexToExist(testIndexName); + }); }); }; diff --git a/x-pack/test_serverless/functional/test_suites/search/landing_page.ts b/x-pack/test_serverless/functional/test_suites/search/landing_page.ts index 9a410a71e0844f4..ee7fa320cc88535 100644 --- a/x-pack/test_serverless/functional/test_suites/search/landing_page.ts +++ b/x-pack/test_serverless/functional/test_suites/search/landing_page.ts @@ -24,5 +24,62 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await svlSearchNavigation.navigateToLandingPage(); await pageObjects.svlSearchLandingPage.assertSvlSearchSideNavExists(); }); + + it('has expected languages', async () => { + const languages = ['curl', 'java', 'dotnet', 'python', 'javascript', 'php', 'go', 'ruby']; + + for (const id of languages) { + await pageObjects.svlSearchLandingPage.languageClients.expectLanguagePanelExists(id); + } + // Java is selected by default + await pageObjects.svlSearchLandingPage.languageClients.expectLanguageSelected('java'); + + // We can select a non-default language + await pageObjects.svlSearchLandingPage.languageClients.selectLanguage('curl'); + await pageObjects.svlSearchLandingPage.languageClients.expectLanguageSelected('curl'); + }); + + describe('API Key creation', async () => { + beforeEach(async () => { + // We need to reload the page between api key creations + await svlSearchNavigation.navigateToLandingPage(); + }); + + it('can create an API key that expires', async () => { + await pageObjects.svlSearchLandingPage.apiKeys.openCreateFlyout(); + await pageObjects.svlSearchLandingPage.apiKeys.setApiKeyName('test-api-key'); + await pageObjects.svlSearchLandingPage.apiKeys.createApiKeySubmitAndSuccess(); + }); + it('can create an API key that never expires', async () => { + await pageObjects.svlSearchLandingPage.apiKeys.openCreateFlyout(); + await pageObjects.svlSearchLandingPage.apiKeys.setApiKeyName('test-unlimited-api-key'); + await pageObjects.svlSearchLandingPage.apiKeys.selectNeverExpires(); + await pageObjects.svlSearchLandingPage.apiKeys.createApiKeySubmitAndSuccess(); + }); + it('can create an API key with metadata', async () => { + await pageObjects.svlSearchLandingPage.apiKeys.openCreateFlyout(); + await pageObjects.svlSearchLandingPage.apiKeys.setApiKeyName('test-metadata-api-key'); + await pageObjects.svlSearchLandingPage.apiKeys.createApiKeyToggleMetadataSwitch(); + await pageObjects.svlSearchLandingPage.apiKeys.expectMetadataEditorToExist(); + await pageObjects.svlSearchLandingPage.apiKeys.createApiKeySubmitAndSuccess(); + }); + it('can create an API key with role descriptors', async () => { + await pageObjects.svlSearchLandingPage.apiKeys.openCreateFlyout(); + await pageObjects.svlSearchLandingPage.apiKeys.setApiKeyName('test-roles-api-key'); + await pageObjects.svlSearchLandingPage.apiKeys.createApiKeyToggleRoleDescriptorsSwitch(); + await pageObjects.svlSearchLandingPage.apiKeys.expectRoleDescriptorsEditorToExist(); + await pageObjects.svlSearchLandingPage.apiKeys.createApiKeySubmitAndSuccess(); + }); + it('shows server error with invalid API key data', async () => { + await pageObjects.svlSearchLandingPage.apiKeys.openCreateFlyout(); + await pageObjects.svlSearchLandingPage.apiKeys.setApiKeyName('test-roles-api-key'); + await pageObjects.svlSearchLandingPage.apiKeys.createApiKeyToggleRoleDescriptorsSwitch(); + await pageObjects.svlSearchLandingPage.apiKeys.setRoleDescriptorsValue( + '{"invalid": "role"}' + ); + await pageObjects.svlSearchLandingPage.apiKeys.createApiKeySubmitAndError(); + await pageObjects.svlSearchLandingPage.apiKeys.createApiKeyCancel(); + }); + }); }); }