diff --git a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx index 54a70ac904..d2b6b5d1b1 100644 --- a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx +++ b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx @@ -119,24 +119,28 @@ export function ExecuteResult({ /> )} -
-
- - {currentResult?.truncated - ? i18n('title.truncated') - : i18n('title.result')} - - {`(${currentResult?.result?.length})`} + {currentResult && ( +
+
+ + {currentResult?.truncated + ? i18n('title.truncated') + : i18n('title.result')} + + {currentResult.result && ( + {`(${currentResult.result.length})`} + )} +
+
- -
+ )}
); }; diff --git a/tests/suites/tenant/constants.ts b/tests/suites/tenant/constants.ts index be9ca373c5..5dfcc89eca 100644 --- a/tests/suites/tenant/constants.ts +++ b/tests/suites/tenant/constants.ts @@ -1,7 +1,40 @@ // Long running query for tests // May cause Memory exceed on real database -const simpleQuery = 'SELECT 1;'; +export const simpleQuery = 'SELECT 1;'; +export const longTableSelect = 'SELECT * FROM `.sys/pg_class`'; // 400 is pretty enough export const longRunningQuery = new Array(400).fill(simpleQuery).join(''); + +export const createTableQuery = ` +CREATE TABLE \`/local/ydb_row_table\` ( + category_id Uint64 NOT NULL, + id Uint64, + expire_at Datetime, + updated_on Datetime, + name Text, + \`binary-payload\` Bytes, + attributes JsonDocument, + -- uncomment to add a secondary index + -- INDEX idx_row_table_id GLOBAL SYNC ON ( id ) COVER ( name, attributes ), -- Secondary indexes docs https://ydb.tech/en/docs/yql/reference/syntax/create_table#secondary_index + PRIMARY KEY (category_id, id) +) +WITH ( + AUTO_PARTITIONING_BY_SIZE = ENABLED, + AUTO_PARTITIONING_PARTITION_SIZE_MB = 2048, + AUTO_PARTITIONING_BY_LOAD = ENABLED, + AUTO_PARTITIONING_MIN_PARTITIONS_COUNT = 4, + AUTO_PARTITIONING_MAX_PARTITIONS_COUNT = 1024 + -- uncomment to create a table with predefined partitions + -- , UNIFORM_PARTITIONS = 4 -- The number of partitions for uniform initial table partitioning. + -- The primary key's first column must have type Uint64 or Uint32. + -- A created table is immediately divided into the specified number of partitions + -- uncomment to launch read only replicas in every AZ + -- , READ_REPLICAS_SETTINGS = 'PER_AZ:1' -- Enable read replicas for stale read, launch one replica in every availability zone + -- uncomment to enable ttl + -- , TTL = Interval("PT1H") ON expire_at -- Enable background deletion of expired rows https://ydb.tech/en/docs/concepts/ttl + -- uncomment to create a table with a bloom filter + -- , KEY_BLOOM_FILTER = ENABLED -- With a Bloom filter, you can more efficiently determine + -- if some keys are missing in a table when making multiple single queries by the primary key. +)`; diff --git a/tests/suites/tenant/queryEditor/QueryEditor.ts b/tests/suites/tenant/queryEditor/QueryEditor.ts index 2083335bbf..b15c1a7064 100644 --- a/tests/suites/tenant/queryEditor/QueryEditor.ts +++ b/tests/suites/tenant/queryEditor/QueryEditor.ts @@ -104,6 +104,12 @@ export class SettingsDialog { await this.page.waitForTimeout(1000); } + async changeLimitRows(limitRows: number) { + const limitRowsInput = this.dialog.locator('.ydb-query-settings-dialog__limit-rows input'); + await limitRowsInput.fill(limitRows.toString()); + await this.page.waitForTimeout(1000); + } + async clickButton(buttonName: ButtonNames) { const button = this.dialog.getByRole('button', {name: buttonName}); await button.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT}); @@ -140,10 +146,12 @@ class PaneWrapper { export class ResultTable { private table: Locator; private preview: Locator; + private resultHead: Locator; constructor(selector: Locator) { this.table = selector.locator('.ydb-query-execute-result__result'); this.preview = selector.locator('.kv-preview__result'); + this.resultHead = selector.locator('.ydb-query-execute-result__result-head'); } async isVisible() { @@ -175,6 +183,16 @@ export class ResultTable { const cell = this.table.locator(`tr:nth-child(${row}) td:nth-child(${col})`); return cell.innerText(); } + + async isResultHeaderHidden() { + await this.resultHead.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT}); + return true; + } + + async getResultHeadText() { + await this.resultHead.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT}); + return this.resultHead.innerText(); + } } export class QueryEditor { diff --git a/tests/suites/tenant/queryEditor/queryEditor.test.ts b/tests/suites/tenant/queryEditor/queryEditor.test.ts index 5c3f404d81..1db10a9a11 100644 --- a/tests/suites/tenant/queryEditor/queryEditor.test.ts +++ b/tests/suites/tenant/queryEditor/queryEditor.test.ts @@ -2,7 +2,7 @@ import {expect, test} from '@playwright/test'; import {tenantName} from '../../../utils/constants'; import {NavigationTabs, TenantPage, VISIBILITY_TIMEOUT} from '../TenantPage'; -import {longRunningQuery} from '../constants'; +import {createTableQuery, longRunningQuery, longTableSelect} from '../constants'; import { ButtonNames, @@ -377,4 +377,29 @@ test.describe('Test Query Editor', async () => { await tenantPage.selectNavigationTab(NavigationTabs.Query); await expect(queryEditor.resultTable.isVisible()).resolves.toBe(true); }); + + test('Result head value is 1 for 1 row result', async ({page}) => { + const queryEditor = new QueryEditor(page); + await queryEditor.setQuery(testQuery); + await queryEditor.clickRunButton(); + await expect(queryEditor.resultTable.getResultHeadText()).resolves.toBe('Result(1)'); + }); + + test('No result head value for no result', async ({page}) => { + const queryEditor = new QueryEditor(page); + await queryEditor.setQuery(createTableQuery); + await queryEditor.clickRunButton(); + await page.waitForTimeout(1000); + await expect(queryEditor.resultTable.isResultHeaderHidden()).resolves.toBe(true); + }); + + test('Truncated head value is 1 for 1 row truncated result', async ({page}) => { + const queryEditor = new QueryEditor(page); + await queryEditor.setQuery(longTableSelect); + await queryEditor.clickGearButton(); + await queryEditor.settingsDialog.changeLimitRows(1); + await queryEditor.settingsDialog.clickButton(ButtonNames.Save); + await queryEditor.clickRunButton(); + await expect(queryEditor.resultTable.getResultHeadText()).resolves.toBe('Truncated(1)'); + }); });