diff --git a/e2e-playwright/config/cucumber.js b/e2e-playwright/config/cucumber.js index 7213fd230..72e206d25 100644 --- a/e2e-playwright/config/cucumber.js +++ b/e2e-playwright/config/cucumber.js @@ -1,7 +1,7 @@ /* eslint-disable */ module.exports = { default: { - timeout: 30000, + timeout: 60000, tags: process.env.npm_config_TAGS || "", formatOptions: { snippetInterface: "async-await" diff --git a/e2e-playwright/package.json b/e2e-playwright/package.json index bc84432c7..6d1b9a347 100644 --- a/e2e-playwright/package.json +++ b/e2e-playwright/package.json @@ -7,6 +7,7 @@ "pretest": "npx ts-node src/helper/report/init.ts", "test": "cross-env ENV=prod FORCE_COLOR=0 cucumber-js --config=config/cucumber.js", "test:stage": "cross-env ENV=stage FORCE_COLOR=0 cucumber-js --config=config/cucumber.js", + "test:sp": "cross-env ENV=stage FORCE_COLOR=0 cucumber-js --config=config/cucumber.js --tags @sp", "posttest": "npx ts-node src/helper/report/report.ts", "test:failed": "cucumber-js -p rerun @rerun.txt", "lint": "eslint 'src/**/*.ts' --fix" diff --git a/e2e-playwright/src/features/Brokers.feature b/e2e-playwright/src/features/Brokers.feature new file mode 100644 index 000000000..134558bc0 --- /dev/null +++ b/e2e-playwright/src/features/Brokers.feature @@ -0,0 +1,31 @@ +Feature: Brokers page + + Scenario: Brokers visibility BrokerDetails visibility + Given Brokers is visible + When click on Brokers link + Then Brokers heading visible + Then the end of current URL should be "brokers" + Given Brokers Uptime visible is: "true" + Given Brokers Partitions visible is: "true" + When Brokers cell element "1" clicked + Given BrokerDetails name is: "BrokersBroker" header visible is: "true" + Given BrokerDetails Log directories visible is: "true" + Given BrokerDetails Configs visible is: "true" + Given BrokerDetails Metrics visible is: "true" + + Scenario: Brokers visibility BrokerDetails visibility + Given Brokers is visible + When click on Brokers link + Then Brokers heading visible + Then the end of current URL should be "brokers" + Given Brokers Uptime visible is: "true" + Given Brokers Partitions visible is: "true" + When Brokers cell element "1" clicked + Given BrokerDetails name is: "BrokersBroker" header visible is: "true" + When BrokerDetails Configs clicked + Given BrokerDetails Configs Key visible is: "true" + Given BrokerDetails Configs Value visible is: "true" + Given BrokerDetails Configs Source visible is: "true" + Then BrokerDetails searchfield visible is: "true" + When BrokerDetails searchfield input is: "process.roles" cell value is: "broker,controller" + When BrokerDetails searchfield input is: "broker,controller" cell value is: "process.roles" \ No newline at end of file diff --git a/e2e-playwright/src/features/KsqlDb.feature b/e2e-playwright/src/features/KsqlDb.feature new file mode 100644 index 000000000..44b17c6f8 --- /dev/null +++ b/e2e-playwright/src/features/KsqlDb.feature @@ -0,0 +1,84 @@ +Feature: Ksqldb page visibility + + Scenario: KSQL DB elements visibility + Given KSQL DB is visible + When click on KSQL DB link + Then KSQL DB heading visible + Then the part of current URL should be "ksqldb" + Given KSQL DB Tables header visible is: "true" + Given KSQL DB Streams header visible is: "true" + Given KSQL DB Tables link visible is: "true" + Given KSQL DB Streams link visible is: "true" + When KSQL DB ExecuteKSQLRequest click + Given KSQL DB Clear visible is: "true" + Given KSQL DB Execute visible is: "true" + + Scenario: KSQL DB queries clear result + Given KSQL DB is visible + When click on KSQL DB link + Then KSQL DB heading visible + Then the part of current URL should be "ksqldb" + Given KSQL DB Tables header visible is: "true" + Given KSQL DB Streams header visible is: "true" + Given KSQL DB Tables link visible is: "true" + Given KSQL DB Streams link visible is: "true" + When KSQL DB ExecuteKSQLRequest click + Given KSQL DB textbox visible is: "true" + Given KSQL DB KSQL for stream starts with: "STREAM_ONE", kafka_topic starts with: "NewAutoTopic", value_format: "json" + Then KSQL DB stream created + Then KSQL DB clear result visible is: "true" + + Scenario: KSQL DB queries + Given KSQL DB is visible + When click on KSQL DB link + Then KSQL DB heading visible + Then the part of current URL should be "ksqldb" + Given KSQL DB Tables header visible is: "true" + Given KSQL DB Streams header visible is: "true" + Given KSQL DB Tables link visible is: "true" + Given KSQL DB Streams link visible is: "true" + When KSQL DB ExecuteKSQLRequest click + Given KSQL DB textbox visible is: "true" + Given KSQL DB KSQL for stream starts with: "STREAM_ONE", kafka_topic starts with: "NewAutoTopic", value_format: "json" + Then KSQL DB stream created + When KSQL DB Stream clicked + Then KSQL DB stream starts with: "STREAM_ONE" visible is: "true" + When KSQL DB ExecuteKSQLRequest click + Given KSQL DB textbox visible is: "true" + Then KSQL DB KSQL for table starts with: "TABLE_ONE", stream starts with: "STREAM_ONE" + Then KSQL DB table created + When KSQL DB Table clicked + Then KSQL DB table starts with: "TABLE_ONE" visible is: "true" + + Scenario: KSQL DB cancel queries + Given KSQL DB is visible + When click on KSQL DB link + Then KSQL DB heading visible + Then the part of current URL should be "ksqldb" + Given KSQL DB Tables header visible is: "true" + Given KSQL DB Streams header visible is: "true" + Given KSQL DB Tables link visible is: "true" + Given KSQL DB Streams link visible is: "true" + When KSQL DB ExecuteKSQLRequest click + Given KSQL DB textbox visible is: "true" + Given KSQL DB KSQL for stream starts with: "STREAM_ONE", kafka_topic starts with: "NewAutoTopic", value_format: "json" + Then KSQL DB stream created + Then KSQL DB KSQL cleared + When KSQL DB ExecuteKSQLRequest click + Given KSQL DB textbox visible is: "true" + Then KSQL DB KSQL for table starts with: "TABLE_ONE", stream starts with: "STREAM_ONE" + Then KSQL DB table created + Then KSQL DB KSQL cleared + When KSQL DB ExecuteKSQLRequest click + Given KSQL DB textbox visible is: "true" + Given KSQL DB KSQL data inserted to stream starts with: "STREAM_ONE", from table: + |Id |la |lo | + | c2309eec | 37.7877 | -122.4205 | + | 18f4ea86 | 37.3903 | -122.0643 | + | 4ab5cbad | 37.3952 | -122.0813 | + | 8b6eae59 | 37.3944 | -122.0813 | + | 4a7c7b41 | 37.4049 | -122.0822 | + | 4ddad000 | 37.7857 | -122.4011 | + + Given KSQL DB long query stream starts with: "STREAM_ONE" stared + Given KSQL DB long query stoped \ No newline at end of file diff --git a/e2e-playwright/src/features/SchemaRegistry.feature b/e2e-playwright/src/features/SchemaRegistry.feature new file mode 100644 index 000000000..5aa41f3c8 --- /dev/null +++ b/e2e-playwright/src/features/SchemaRegistry.feature @@ -0,0 +1,84 @@ +Feature: Schema page + + Scenario: SchemaRegistry and SchemaRegistryCreate visibility + Given Schema Registry is visible + When click on Schema Registry link + Then Schema Registry heading visible + Given SchemaRegistry CheateSchema clicked + Given SchemaRegistryCreate is visible + Given SchemaRegistryCreate Subject visible is: "true" + Given SchemaRegistryCreate Schema visible is: "true" + Given SchemaRegistryCreate SchemaType visible is: "true" + When SchemaRegistryCreate Subject input starts with: "SchemaSubject" + When SchemaRegistryCreate Schema input from avro + When SchemaRegistryCreate Submit clicked + Then SchemaRegistrySchemaName starts with: "SchemaSubject", visible is: "true" + + Scenario: SchemaRegistry Avro schema actions + Given Schema Registry is visible + When click on Schema Registry link + Then Schema Registry heading visible + Given SchemaRegistry CheateSchema clicked + Given SchemaRegistryCreate is visible + Given SchemaRegistryCreate Subject visible is: "true" + Given SchemaRegistryCreate Schema visible is: "true" + Given SchemaRegistryCreate SchemaType visible is: "true" + When SchemaRegistryCreate Subject input starts with: "SchemaSubject" + When SchemaRegistryCreate Schema input from avro + When SchemaRegistryCreate Submit clicked + Then SchemaRegistrySchemaName starts with: "SchemaSubject", visible is: "true" + When click on Brokers link + When click on Schema Registry link + Given SchemaRegistry click on schema starts with: "SchemaSubject" + Given SchemaRegistrySchemaName version is: "1" + Given SchemaRegistrySchemaName type is: "AVRO" + Given SchemaRegistrySchemaName Compatibility is: "BACKWARD" + When SchemaRegistrySchemaName remove schema clicked + Then Schema starts with: "SchemaSubject" deleted + Then SchemaRegistrySchemaName starts with: "SchemaSubject", visible is: "false" + + Scenario: SchemaRegistry Json schema actions + Given Schema Registry is visible + When click on Schema Registry link + Then Schema Registry heading visible + Given SchemaRegistry CheateSchema clicked + Given SchemaRegistryCreate is visible + Given SchemaRegistryCreate Subject visible is: "true" + Given SchemaRegistryCreate Schema visible is: "true" + Given SchemaRegistryCreate SchemaType visible is: "true" + When SchemaRegistryCreate Subject input starts with: "SchemaSubject" + When SchemaRegistryCreate Schema input from json + When SchemaRegistryCreate Submit clicked + Then SchemaRegistrySchemaName starts with: "SchemaSubject", visible is: "true" + When click on Brokers link + When click on Schema Registry link + Given SchemaRegistry click on schema starts with: "SchemaSubject" + Given SchemaRegistrySchemaName version is: "1" + Given SchemaRegistrySchemaName type is: "JSON" + Given SchemaRegistrySchemaName Compatibility is: "BACKWARD" + When SchemaRegistrySchemaName remove schema clicked + Then Schema starts with: "SchemaSubject" deleted + Then SchemaRegistrySchemaName starts with: "SchemaSubject", visible is: "false" + + Scenario: SchemaRegistry Json schema actions + Given Schema Registry is visible + When click on Schema Registry link + Then Schema Registry heading visible + Given SchemaRegistry CheateSchema clicked + Given SchemaRegistryCreate is visible + Given SchemaRegistryCreate Subject visible is: "true" + Given SchemaRegistryCreate Schema visible is: "true" + Given SchemaRegistryCreate SchemaType visible is: "true" + When SchemaRegistryCreate Subject input starts with: "SchemaSubject" + When SchemaRegistryCreate Schema input from protobuf + When SchemaRegistryCreate Submit clicked + Then SchemaRegistrySchemaName starts with: "SchemaSubject", visible is: "true" + When click on Brokers link + When click on Schema Registry link + Given SchemaRegistry click on schema starts with: "SchemaSubject" + Given SchemaRegistrySchemaName version is: "1" + Given SchemaRegistrySchemaName type is: "PROTOBUF" + Given SchemaRegistrySchemaName Compatibility is: "BACKWARD" + When SchemaRegistrySchemaName remove schema clicked + Then Schema starts with: "SchemaSubject" deleted + Then SchemaRegistrySchemaName starts with: "SchemaSubject", visible is: "false" \ No newline at end of file diff --git a/e2e-playwright/src/features/TopicsMessages.feature b/e2e-playwright/src/features/TopicsMessages.feature new file mode 100644 index 000000000..0fc0cd637 --- /dev/null +++ b/e2e-playwright/src/features/TopicsMessages.feature @@ -0,0 +1,133 @@ +Feature: Produce Messages page + Scenario: TopicName ui + Given Topics is visible + When click on Topics link + Given Topics AddATopic clicked + Given TopicCreate heading visible is: "true" + When TopicCreate Topic name starts with: "ANewAutoTopic" + When TopicCreate Number of partitons: 1 + When TopicCreate Time to retain data one day + When TopicCreate Create topic clicked + Then Header starts with: "ANewAutoTopic" + Given Topics TopicName partitions is: 1 + Given Topics TopicName Overview visible is: "true" + Given Topics TopicName Messages visible is: "true" + Given Topics TopicName Consumers visible is: "true" + Given Topics TopicName Settings visible is: "true" + Given Topics TopicName Statistics visible is: "true" + + Scenario: Produce Message + Given Topics is visible + When click on Topics link + Given Topics AddATopic clicked + Given TopicCreate heading visible is: "true" + When TopicCreate Topic name starts with: "ANewAutoTopic" + When TopicCreate Number of partitons: 1 + When TopicCreate Time to retain data one day + When TopicCreate Create topic clicked + Then Header starts with: "ANewAutoTopic" + Given Produce message clicked + Then ProduceMessage header visible + Given ProduceMessage Key input is: "keyFromAutotest" + Given ProduceMessage Value input is: "ValueFromAutotest" + Given ProduceMessage Headers input key is: "headerKey", value is: "headerValue" + Given ProduceMessage Produce Message button clicked + When Topics TopicName Messages clicked + Then TopicName messages contains key: "keyFromAutotest" + Then TopicName messages contains value: "ValueFromAutotest" + Then TopicName messages contains headers key is: "headerKey", value is: "headerValue" + + Scenario: Topic Message cleanup policy + Given Topics is visible + When click on Topics link + Given Topics AddATopic clicked + Given TopicCreate heading visible is: "true" + When TopicCreate Topic name starts with: "ANewAutoTopic" + When TopicCreate Number of partitons: 1 + When TopicCreate Time to retain data one day + When TopicCreate Create topic clicked + Then Header starts with: "ANewAutoTopic" + Given TopicName menu button clicked + Then TopicNameMenu clear messages active is: "true" + When TopicNameMenu edit settings clicked + When TopicName cleanup policy set to: "Compact" + When TopicName UpdateTopic button clicked + Then Header starts with: "ANewAutoTopic" + Given TopicName menu button clicked + Then TopicNameMenu clear messages active is: "false" + When TopicNameMenu edit settings clicked + When TopicName cleanup policy set to: "Delete" + When TopicName UpdateTopic button clicked + Then Header starts with: "ANewAutoTopic" + Given TopicName menu button clicked + Then TopicNameMenu clear messages active is: "true" + + Scenario: Produce messages clear messages + Given Topics is visible + When click on Topics link + Given Topics AddATopic clicked + Given TopicCreate heading visible is: "true" + When TopicCreate Topic name starts with: "ANewAutoTopic" + When TopicCreate Number of partitons: 1 + When TopicCreate Time to retain data one day + When TopicCreate Create topic clicked + Then Header starts with: "ANewAutoTopic" + Given Produce message clicked + Then ProduceMessage header visible + Given ProduceMessage Key input is: "keyFromAutotest" + Given ProduceMessage Value input is: "ValueFromAutotest" + Given ProduceMessage Headers input key is: "headerKey", value is: "headerValue" + Given ProduceMessage Produce Message button clicked + When Topics TopicName Messages clicked + Then TopicName messages contains key: "keyFromAutotest" + Then TopicName messages contains value: "ValueFromAutotest" + Then TopicName messages contains headers key is: "headerKey", value is: "headerValue" + Then Topics TopicName Overview click + Then TopicName messages count is "1" + Given Produce message clicked + Given ProduceMessage Key input is: "keyFromAutotest2" + Given ProduceMessage Value input is: "ValueFromAutotest2" + Given ProduceMessage Headers input key is: "headerKey2", value is: "headerValue2" + Given ProduceMessage Produce Message button clicked + Then TopicName messages count is "2" + When TopicName clear messages clicked + Then TopicName messages count is "0" + + Given Produce message clicked + Given ProduceMessage Key input is: "keyFromAutotest3" + Given ProduceMessage Value input is: "ValueFromAutotest3" + Given ProduceMessage Headers input key is: "headerKey3", value is: "headerValue3" + Given ProduceMessage Produce Message button clicked + Then TopicName messages count is "1" + Given Produce message clicked + Given ProduceMessage Key input is: "keyFromAutotest4" + Given ProduceMessage Value input is: "ValueFromAutotest4" + Given ProduceMessage Headers input key is: "headerKey4", value is: "headerValue4" + Given ProduceMessage Produce Message button clicked + Then TopicName messages count is "2" + When TopicName menu button clicked + When TopicName menu clear messages clicked + Then TopicName messages count is "0" + + Given Produce message clicked + Given ProduceMessage Key input is: "keyFromAutotest5" + Given ProduceMessage Value input is: "ValueFromAutotest5" + Given ProduceMessage Headers input key is: "headerKey5", value is: "headerValue5" + Given ProduceMessage Produce Message button clicked + Then TopicName messages count is "1" + Given Produce message clicked + Given ProduceMessage Key input is: "keyFromAutotest6" + Given ProduceMessage Value input is: "ValueFromAutotest6" + Given ProduceMessage Headers input key is: "headerKey6", value is: "headerValue6" + Given ProduceMessage Produce Message button clicked + Then TopicName messages count is "2" + When TopicName menu button clicked + When TopicName menu RecreateTopic clicked + Then TopicName messages count is "0" + + Given Produce message clicked + Given ProduceMessage Key input is: "keyFromAutotest7" + Given ProduceMessage Value input is: "ValueFromAutotest7" + Given ProduceMessage Headers input key is: "headerKey7", value is: "headerValue7" + Given ProduceMessage Produce Message button clicked + Then TopicName messages count is "1" \ No newline at end of file diff --git a/e2e-playwright/src/hooks/hooks.ts b/e2e-playwright/src/hooks/hooks.ts index 33f949db1..b7abff571 100644 --- a/e2e-playwright/src/hooks/hooks.ts +++ b/e2e-playwright/src/hooks/hooks.ts @@ -30,7 +30,7 @@ Before(async function(this: PlaywrightWorld, { pickle }) { await this.init(context, scenarioName); }); -After({ timeout: 30000 }, async function(this: PlaywrightWorld, { pickle, result }) { +After({ timeout: 60000 }, async function(this: PlaywrightWorld, { pickle, result }) { let img: Buffer | undefined; const path = `./test-results/trace/${pickle.id}.zip`; try { diff --git a/e2e-playwright/src/pages/Brokers/BrokerDetailsLocators.ts b/e2e-playwright/src/pages/Brokers/BrokerDetailsLocators.ts new file mode 100644 index 000000000..3f94fb07b --- /dev/null +++ b/e2e-playwright/src/pages/Brokers/BrokerDetailsLocators.ts @@ -0,0 +1,21 @@ +import { Page, Locator } from "@playwright/test"; + +export default class BrokerDetailsLocators { + private readonly page: Page; + + constructor(page: Page) { + this.page = page; + } + + get logDirectors(): Locator { return this.page.getByRole('link', { name: 'Log directories' })}; + get firstDefaultCell(): Locator { return this.page.getByRole('cell', { name: '/tmp/kraft-combined-logs' })}; + get configs(): Locator { return this.page.getByRole('link', { name: 'Configs' })}; + get configsTextbox(): Locator { return this.page.getByRole('textbox', { name: 'Search by Key or Value' })}; + get metrics(): Locator { return this.page.getByRole('link', { name: 'Metrics' })}; + get configsKey(): Locator { return this.page.getByRole('cell', { name: 'Key' })}; + get configsValue(): Locator { return this.page.getByRole('cell', { name: 'Value' })}; + get configsSource(): Locator { return this.page.getByRole('cell', { name: 'Source' })}; + + configCell(value: string): Locator { return this.page.getByRole('cell', { name: value })}; + header(value: string): Locator { return this.page.getByText(value)}; +} \ No newline at end of file diff --git a/e2e-playwright/src/pages/Brokers/BrokersLocators.ts b/e2e-playwright/src/pages/Brokers/BrokersLocators.ts index 7d38c8dbd..24587a095 100644 --- a/e2e-playwright/src/pages/Brokers/BrokersLocators.ts +++ b/e2e-playwright/src/pages/Brokers/BrokersLocators.ts @@ -8,4 +8,8 @@ export default class BrokersLocators { } get heading(): Locator { return this.page.getByRole('heading', { name: 'Brokers' })}; + get uptime(): Locator { return this.page.getByRole('heading', { name: 'Uptime' })}; + get partitions(): Locator { return this.page.getByRole('heading', { name: 'Partitions' })}; + + toBroker(value: string): Locator { return this.page.getByRole('cell', { name: value, exact: true }); } } \ No newline at end of file diff --git a/e2e-playwright/src/pages/KSQLDB/ksqldbLocators.ts b/e2e-playwright/src/pages/KSQLDB/ksqldbLocators.ts index 7fffdc13c..2a50e5ab4 100644 --- a/e2e-playwright/src/pages/KSQLDB/ksqldbLocators.ts +++ b/e2e-playwright/src/pages/KSQLDB/ksqldbLocators.ts @@ -11,5 +11,17 @@ export default class ksqlDbLocators { get executeKSQLREquestButton(): Locator { return this.page.getByRole('button', { name: 'Execute KSQL Request' })}; get tablesLink(): Locator { return this.page.getByRole('link', { name: 'Tables' })}; get streamsLink(): Locator { return this.page.getByRole('link', { name: 'Streams' })}; + get tablesHeader(): Locator { return this.page.getByTitle('Tables').locator('div')}; + get streamsHeader(): Locator { return this.page.getByTitle('Streams').locator('div')}; + get textField(): Locator { return this.page.locator('.ace_content')}; + get clear(): Locator { return this.page.getByRole('button', { name: 'Clear', exact: true })}; + get execute(): Locator { return this.page.getByRole('button', { name: 'Execute', exact: true })}; + get success(): Locator { return this.page.getByRole('cell', { name: 'SUCCESS' })}; + get streamCSreated(): Locator { return this.page.getByRole('cell', { name: 'Stream created' })}; + get clearResults(): Locator { return this.page.getByRole('button', { name: 'Clear results' })}; + get querySucceed(): Locator { return this.page.getByRole('heading', { name: 'Query succeed' })}; + get consumingQueryExecution(): Locator { return this.page.getByText('Consuming query execution')}; + get abort(): Locator { return this.page.getByText('Abort')}; + get cancelled(): Locator { return this.page.getByText('Cancelled')}; } \ No newline at end of file diff --git a/e2e-playwright/src/pages/Locators.ts b/e2e-playwright/src/pages/Locators.ts index 924762740..8e479771a 100644 --- a/e2e-playwright/src/pages/Locators.ts +++ b/e2e-playwright/src/pages/Locators.ts @@ -8,16 +8,24 @@ import SchemaRegistryLocators from "./SchemaRegistry/SchemaRegistryLocators"; import ConnectorsLocators from "./Connectors/ConnectorsLocators"; import ksqlDbLocators from "./KSQLDB/ksqldbLocators"; import DashboardLocators from "./Dashboard/DashboardLocators"; +import TopicsTopickNameLocators from "./Topics/TopicsTopickNameLocators" +import ProduceMessageLocators from "./Topics/ProduceMessageLocators" +import BrokerDetailsLocators from "./Brokers/BrokerDetailsLocators" +import SchemaRegistrySchemaNameLocators from "./SchemaRegistry/SchemaRegistrySchemaNameLocators" export class Locators { private readonly page: Page; private _panel?: PanelLocators; private _brokers?: BrokersLocators; + private _brokerDetails?: BrokerDetailsLocators; private _topics?: TopicsLocators; private _topicsCreate?: TopicCreateLocators; + private _topicTopicName?: TopicsTopickNameLocators; + private _produceMessage?: ProduceMessageLocators; private _consumers?: ConsumersLocators; private _schemaRegistry?: SchemaRegistryLocators; + private _schemaName?: SchemaRegistrySchemaNameLocators; private _connectors?: ConnectorsLocators; private _ksqlDb?: ksqlDbLocators; private _dashboard?: DashboardLocators; @@ -34,6 +42,10 @@ export class Locators { return (this._brokers ??= new BrokersLocators(this.page)); } + get brokerDetails() { + return (this._brokerDetails ??= new BrokerDetailsLocators(this.page)); + } + get topics() { return (this._topics ??= new TopicsLocators(this.page)); } @@ -42,6 +54,14 @@ export class Locators { return (this._topicsCreate ??= new TopicCreateLocators(this.page)); } + get topicTopicName() { + return (this._topicTopicName ??= new TopicsTopickNameLocators(this.page)); + } + + get produceMessage() { + return (this._produceMessage ??= new ProduceMessageLocators(this.page)); + } + get consumers() { return (this._consumers ??= new ConsumersLocators(this.page)); } @@ -50,6 +70,10 @@ export class Locators { return (this._schemaRegistry ??= new SchemaRegistryLocators(this.page)); } + get schemaName() { + return (this._schemaName ??= new SchemaRegistrySchemaNameLocators(this.page)); + } + get connectors() { return (this._connectors ??= new ConnectorsLocators(this.page)); } diff --git a/e2e-playwright/src/pages/SchemaRegistry/SchemaRegistryLocators.ts b/e2e-playwright/src/pages/SchemaRegistry/SchemaRegistryLocators.ts index a1f908e74..4aa7c90a1 100644 --- a/e2e-playwright/src/pages/SchemaRegistry/SchemaRegistryLocators.ts +++ b/e2e-playwright/src/pages/SchemaRegistry/SchemaRegistryLocators.ts @@ -10,4 +10,17 @@ export default class SchemaRegistryLocators { get heading(): Locator { return this.page.getByRole('heading', { name: 'Schema Registry' })}; get searchBox(): Locator { return this.page.getByRole('textbox', { name: 'Search by Schema Name' })}; get createSchemaButton(): Locator { return this.page.getByRole('button', { name: 'Create Schema' })}; + + get createHeading(): Locator { return this.page.getByText('Schema RegistryCreate')}; + get subjectTextBox(): Locator { return this.page.getByRole('textbox', { name: 'Schema Name' })}; + get schemaTextBox(): Locator { return this.page.locator('textarea[name="schema"]')}; + get schemaTypeDropDown(): Locator { return this.page.locator('form path')}; + get submit(): Locator { return this.page.getByRole('button', { name: 'Submit' })}; + get schemaType(): Locator { return this.page.locator('form').getByRole('img')}; + + schemaTypeDropDownElement(value:string): Locator { return this.page.getByRole('option', { name: value })}; + + toSchema(value:string): Locator { return this.page.getByRole('link', { name: value })}; + + schemaTypeElement(value: string): Locator { return this.page.getByRole('option', { name: value })}; } \ No newline at end of file diff --git a/e2e-playwright/src/pages/SchemaRegistry/SchemaRegistrySchemaNameLocators.ts b/e2e-playwright/src/pages/SchemaRegistry/SchemaRegistrySchemaNameLocators.ts new file mode 100644 index 000000000..ab3397b10 --- /dev/null +++ b/e2e-playwright/src/pages/SchemaRegistry/SchemaRegistrySchemaNameLocators.ts @@ -0,0 +1,26 @@ +import { Page, Locator } from "@playwright/test"; + +export default class SchemaRegistrySchemaNameLocators { + private readonly page: Page; + + constructor(page: Page) { + this.page = page; + } + + get editSchema(): Locator { return this.page.getByRole('button', { name: 'Edit Schema' })}; + get newSchemaTextbox(): Locator { return this.page.locator('#newSchema div').filter({ hasText: '{ "type": "record", "name": "' }).nth(1)}; + get submit(): Locator { return this.page.getByRole('button', { name: 'Submit' })}; + get incompatibeError(): Locator { return this.page.getByText('Schema being registered is')}; + get menu(): Locator { return this.page.getByRole('button', { name: 'Dropdown Toggle' })}; + get removeSchema(): Locator { return this.page.getByText('Remove schema')}; + get confirm(): Locator { return this.page.getByRole('button', { name: 'Confirm' })}; + + heading(value: string): Locator { return this.page.getByText(`Schema Registry${value}`)}; + + version(value: string): Locator { return this.page.getByText(`Latest version${value}`)}; + type(value: string): Locator { return this.page.getByRole('paragraph').filter({ hasText: value })}; + compatibility(value: string): Locator { return this.page.getByText(`Compatibility${value}`)}; + + editDropdown(value: string): Locator { return this.page.getByRole('listbox').filter({ hasText: value }).getByRole('img')}; + editDropdownElement(value: string): Locator { return this.page.getByRole('list').getByRole('option', { name: value, exact: true })}; +} \ No newline at end of file diff --git a/e2e-playwright/src/pages/Topics/ProduceMessageLocators.ts b/e2e-playwright/src/pages/Topics/ProduceMessageLocators.ts new file mode 100644 index 000000000..b46577cb0 --- /dev/null +++ b/e2e-playwright/src/pages/Topics/ProduceMessageLocators.ts @@ -0,0 +1,22 @@ +import { Page, Locator } from "@playwright/test"; + +export default class ProduceMessageLocators { + private readonly page: Page; + + constructor(page: Page) { + this.page = page; + } + + get heading(): Locator { return this.page.getByText('Produce Message').nth(1); } + get keySerdeDropdown(): Locator { return this.page.locator('#selectKeySerdeOptions').getByRole('img'); } + get valueSerdeDropdown(): Locator { return this.page.locator('#selectValueSerdeOptions path'); } + get keyTextbox(): Locator { return this.page.locator('#key').getByRole('textbox'); } + get valueTextbox(): Locator { return this.page.locator('#content').getByRole('textbox'); } + get headersTextbox(): Locator { return this.page.locator('#headers').getByRole('textbox'); } + get produceMessage():Locator { return this.page.locator('form').getByRole('button', { name: 'Produce Message' }); } + + partitionDropdown(vakue:string = 'Partition #'): Locator { return this.page.getByRole('listbox', { name: vakue }); } + partitionDropdownElement(value: string): Locator { return this.page.getByRole('list').getByRole('option', { name: value }); } + keySerdeDropdownElement(value: string): Locator { return this.page.getByRole('list').getByRole('option', { name: value }); } + valueSerdeDropdownElement(value: string): Locator { return this.page.getByRole('list').getByRole('option', { name: value }); } +} \ No newline at end of file diff --git a/e2e-playwright/src/pages/Topics/TopicsTopickNameLocators.ts b/e2e-playwright/src/pages/Topics/TopicsTopickNameLocators.ts new file mode 100644 index 000000000..670125bc8 --- /dev/null +++ b/e2e-playwright/src/pages/Topics/TopicsTopickNameLocators.ts @@ -0,0 +1,44 @@ +import { Page, Locator } from "@playwright/test"; + +export default class TopicsTopickNameLocators { + private readonly page: Page; + + constructor(page: Page) { + this.page = page; + } + + get overview():Locator { return this.page.getByRole('link', { name: 'Overview' }); } + get messages():Locator { return this.page.getByRole('link', { name: 'Messages' }); } + get consumers():Locator { return this.page.getByRole('main').getByRole('navigation').getByRole('link', { name: 'Consumers' }); } + get settings():Locator { return this.page.getByRole('link', { name: 'Settings' }); } + get statistics():Locator { return this.page.getByRole('link', { name: 'Statistics' }); } + get produceMessage():Locator { return this.page.getByRole('button', { name: 'Produce Message' }).first(); } + get keyButton():Locator { return this.page.getByRole('button', { name: 'Key' }); } + get valueButton():Locator { return this.page.getByRole('button', { name: 'Value' }); } + get headersButton():Locator { return this.page.getByRole('button', { name: 'Headers' }); } + get dotsMenu():Locator {return this.page.locator('#root div').filter({ hasText: 'Edit settingsPay attention!' }).nth(3)} + + get menuItemEditSettings():Locator { return this.page.getByRole('menuitem', { name: 'Edit settings Pay attention!' }) } + get menuItemClearMessages():Locator { return this.page.getByText('Clear messagesClearing') } + get menuItemRecreateTopic():Locator { return this.page.getByText('Recreate Topic') } + get confirm():Locator { return this.page.getByRole('button', { name: 'Confirm' }) } + + get cleanupPolicyDropdown():Locator { return this.page.getByRole('listbox', { name: 'Cleanup policy' }) } + get updateTopicButton():Locator { return this.page.getByRole('button', { name: 'Update topic' }) } + get messagesDropdown():Locator { return this.page.getByRole('cell', { name: 'Dropdown Toggle' }).getByLabel('Dropdown Toggle') } + get clearMessages():Locator { return this.page.getByText('Clear Messages', { exact: true }) } + get deleteSuccess():Locator { return this.page.getByRole('heading', { name: 'Success' }) } + + heading(topicName: string): Locator { return this.page.getByText(`Topics${topicName}`); } + partitions(value: string):Locator { return this.page.getByRole('group').getByText(value).first(); } + messageKey(value: string):Locator { return this.page.getByText(value, { exact: true }); } + messageValue(value: string):Locator { return this.page.getByText(value).first(); } + + messageKeyTextbox(value: string):Locator { return this.page.locator('#schema div').filter({ hasText: value }).nth(1); } + messageValueTextbox(value: string):Locator { return this.page.locator('div').filter({ hasText:value }).nth(1); } + messageHeadersTextbox(value: string):Locator { return this.page.locator('div').filter({ hasText: value }).nth(1); } + + cleanupPolicyDropdownItem(value: string):Locator { return this.page.getByRole('option', { name: value, exact: true }); } + + messagesCount(value: string):Locator { return this.page.getByText(`Message Count ${value}`); } +} \ No newline at end of file diff --git a/e2e-playwright/src/services/KsqlScripts.ts b/e2e-playwright/src/services/KsqlScripts.ts new file mode 100644 index 000000000..e8f8827c9 --- /dev/null +++ b/e2e-playwright/src/services/KsqlScripts.ts @@ -0,0 +1,13 @@ + +export function createStreamQuery( + streamName: string, + topicName: string, + valueFormat: string = "json", + partitions: number = 1 +): string { + return `CREATE STREAM ${streamName} (profileId VARCHAR, latitude DOUBLE, longitude DOUBLE ) WITH (kafka_topic='${topicName}', value_format='${valueFormat}', partitions=${partitions});`; +} + +export function createTableQuery(tableName: string, streamName: string): string { + return `CREATE TABLE ${tableName} AS SELECT profileId, LATEST_BY_OFFSET(latitude) AS la, LATEST_BY_OFFSET(longitude) AS lo FROM ${streamName} GROUP BY profileId EMIT CHANGES;`; +} \ No newline at end of file diff --git a/e2e-playwright/src/services/commonFunctions.ts b/e2e-playwright/src/services/commonFunctions.ts index f99fec670..ddddeabcc 100644 --- a/e2e-playwright/src/services/commonFunctions.ts +++ b/e2e-playwright/src/services/commonFunctions.ts @@ -1,5 +1,22 @@ import { v4 as uuidv4 } from 'uuid'; +import { Page } from "@playwright/test"; export const generateName = (prefix: string): string => { - return `${prefix}-${uuidv4().slice(0, 8)}`; + return `${prefix}${uuidv4().slice(0, 8)}`; }; + +export async function Delete(page: Page, times: number = 5): Promise { + for (let i = 0; i < times; i++) { + await page.keyboard.press('Delete'); + } +} + +export async function clearWithSelectAll(page: Page): Promise { + const selectAll = process.platform === 'darwin' ? 'Meta+A' : 'Control+A'; + await page.keyboard.press(selectAll); + await page.keyboard.press('Backspace'); +} + +// export const generateName = (prefix: string): string => { +// return `${prefix}-${uuidv4().slice(0, 8)}`; +// }; diff --git a/e2e-playwright/src/services/schemas.ts b/e2e-playwright/src/services/schemas.ts new file mode 100644 index 000000000..83c61ab0a --- /dev/null +++ b/e2e-playwright/src/services/schemas.ts @@ -0,0 +1,51 @@ +export function getAvroSchema(): string { + return JSON.stringify({ + type: 'record', + name: 'Student', + namespace: 'DataFlair', + fields: [ + { name: 'Name', type: 'string' }, + { name: 'Age', type: 'int' } + ] + }, null, 2); +} + +export function getAvroUpdatedSchema(): string { + return JSON.stringify({ + type: "record", + name: "Message", + namespace: "io.kafbat.ui", + fields: [ + { + name: "text", + type: "string", + default: null + }, + { + name: "value", + type: "string", + default: null + } + ] + }, null, 2); +} + +export function getJsonSchema(): string { + return JSON.stringify({ + "connector.class": "io.confluent.connect.jdbc.JdbcSinkConnector", + "connection.url": "jdbc:postgresql://postgres-db:5432/test", + "connection.user": "dev_user", + "connection.password": "12345", + "topics": "topic_for_connector" + }, null, 2); +} + +export function getProtobufSchema(): string { + return ` +enum SchemaType { + AVRO = 0; + JSON = 1; + PROTOBUF = 2; +} +`.trim(); +} \ No newline at end of file diff --git a/e2e-playwright/src/services/uiHelper.ts b/e2e-playwright/src/services/uiHelper.ts index 340140dd4..c87a0999b 100644 --- a/e2e-playwright/src/services/uiHelper.ts +++ b/e2e-playwright/src/services/uiHelper.ts @@ -1,5 +1,6 @@ import { Locator } from '@playwright/test'; import expect from "../helper/util/expect"; +import { Page } from "@playwright/test"; export const expectVisibility = async(locator: Locator, visibleString: string): Promise => { if (visibleString === "true") { @@ -9,6 +10,15 @@ import expect from "../helper/util/expect"; } }; +export const expectEnabled = async(locator: Locator, enabledString: string): Promise => { + const shouldBeEnabled = enabledString === "true"; + if (shouldBeEnabled) { + await expect(locator).toBeEnabled(); + } else { + await expect(locator).toBeDisabled(); + } +}; + export const ensureCheckboxState = async(checkbox: Locator, expectedState: string) => { const desiredState = expectedState === 'true'; const isChecked = await checkbox.isChecked(); @@ -21,3 +31,33 @@ export const ensureCheckboxState = async(checkbox: Locator, expectedState: strin } } }; + +export const expectVisuallyActive = async( + locator: Locator, + expected: string +): Promise => { + const shouldBeClickable = expected === 'true'; + + const style = await locator.evaluate((el) => { + const computed = window.getComputedStyle(el); + return { + pointerEvents: computed.pointerEvents, + visibility: computed.visibility, + display: computed.display, + cursor: computed.cursor, + }; + }); + + const isClickable = + style.pointerEvents !== 'none' && + style.visibility !== 'hidden' && + style.display !== 'none' && + style.cursor !== 'not-allowed'; + + expect(isClickable).toBe(shouldBeClickable); +}; + +export async function refreshPageAfterDelay(page: Page, delayMs: number = 35000): Promise { + await page.waitForTimeout(delayMs); + await page.reload(); +} \ No newline at end of file diff --git a/e2e-playwright/src/steps/Brokers.steps.ts b/e2e-playwright/src/steps/Brokers.steps.ts new file mode 100644 index 000000000..e3cd90b0e --- /dev/null +++ b/e2e-playwright/src/steps/Brokers.steps.ts @@ -0,0 +1,60 @@ +/* eslint-disable no-unused-vars */ +import { Given, When, Then, setDefaultTimeout } from "@cucumber/cucumber"; +import { PlaywrightWorld } from "../support/PlaywrightWorld"; +import { expectVisibility } from "../services/uiHelper"; + +setDefaultTimeout(60 * 1000 * 2); + +Given('Brokers Uptime visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.brokers.uptime, visible); +}); + +Given('Brokers Partitions visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.brokers.partitions, visible); +}); + +When('Brokers cell element {string} clicked', async function(this: PlaywrightWorld, index: string) { + await this.locators.brokers.toBroker(index).click(); +}); + +Given('BrokerDetails name is: {string} header visible is: {string}', async function(this: PlaywrightWorld, expectedName: string, visible: string) { + await expectVisibility(this.locators.brokerDetails.header(expectedName), visible); +}); + +Given('BrokerDetails Log directories visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.brokerDetails.logDirectors, visible); +}); + +Given('BrokerDetails Configs visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.brokerDetails.configs, visible); +}); + +Given('BrokerDetails Metrics visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.brokerDetails.metrics, visible); +}); + +Given('BrokerDetails Configs Key visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.brokerDetails.configsKey, visible); +}); + +Given('BrokerDetails Configs Value visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.brokerDetails.configsValue, visible); +}); + +Given('BrokerDetails Configs Source visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.brokerDetails.configsSource, visible); +}); + +When('BrokerDetails Configs clicked', async function(this: PlaywrightWorld) { + await this.locators.brokerDetails.configs.click(); +}); +Then('BrokerDetails searchfield visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.brokerDetails.configsTextbox, visible); +}); + +When('BrokerDetails searchfield input is: {string} cell value is: {string}', async function(this: PlaywrightWorld, input: string, expected: string) { + const searchField = this.locators.brokerDetails.configsTextbox; + await searchField.fill(input); + await expectVisibility( this.locators.brokerDetails.configCell(expected), "true"); + await searchField.fill(''); +}); diff --git a/e2e-playwright/src/steps/Ksqldb.steps.ts b/e2e-playwright/src/steps/Ksqldb.steps.ts new file mode 100644 index 000000000..7b659ebd7 --- /dev/null +++ b/e2e-playwright/src/steps/Ksqldb.steps.ts @@ -0,0 +1,166 @@ +/* eslint-disable no-unused-vars */ +import { Given, When, Then, setDefaultTimeout, DataTable } from "@cucumber/cucumber"; +import { expect } from "@playwright/test"; +import { expectVisibility, refreshPageAfterDelay } from "../services/uiHelper"; +import { PlaywrightWorld } from "../support/PlaywrightWorld"; +import { createStreamQuery, createTableQuery } from "../services/KsqlScripts"; +import { generateName, Delete } from "../services/commonFunctions"; + +setDefaultTimeout(60 * 1000 * 4); + +Given('KSQL DB Tables header visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.ksqlDb.tablesHeader, visible); +}); + +Given('KSQL DB Streams header visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.ksqlDb.streamsHeader, visible); +}); + +Given('KSQL DB Tables link visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.ksqlDb.tablesLink, visible); +}); + +Given('KSQL DB Streams link visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.ksqlDb.streamsLink, visible); +}); + +When('KSQL DB ExecuteKSQLRequest click', async function(this: PlaywrightWorld) { + await this.locators.ksqlDb.executeKSQLREquestButton.click(); +}); + +Given('KSQL DB Clear visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.ksqlDb.clear, visible); +}); + +Given('KSQL DB Execute visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.ksqlDb.execute, visible); +}); + +Given('KSQL DB textbox visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.ksqlDb.textField, visible); +}); + +Given('KSQL DB KSQL for stream starts with: {string}, kafka_topic starts with: {string}, value_format: {string}', + async function(this: PlaywrightWorld, stream: string, topic: string, format: string) { + const topicName = generateName(topic); + const streamName = generateName(stream).toUpperCase(); + const query = createStreamQuery(streamName, topicName, format); + + await this.locators.ksqlDb.clear.click(); + const textbox = this.locators.ksqlDb.textField; + await textbox.click(); + await textbox.type(query); + await Delete(this.page); + await this.locators.ksqlDb.execute.click(); + + this.setValue(`topicName-${topic}`, topicName); + this.setValue(`streamName-${stream}`, streamName); + } +); + +Then('KSQL DB stream created', async function(this: PlaywrightWorld) { + await expectVisibility(this.locators.ksqlDb.success, "true"); + await expectVisibility(this.locators.ksqlDb.streamCSreated, "true"); +}); + +Then('KSQL DB clear result visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.ksqlDb.clearResults, visible); +}); + +Then( + 'KSQL DB KSQL for table starts with: {string}, stream starts with: {string}', + async function(this: PlaywrightWorld, table: string, stream: string) { + + const tableName = generateName(table); + const streamName = this.getValue(`streamName-${stream}`); + const query = createTableQuery(tableName, streamName); + + await this.locators.ksqlDb.clear.click(); + const textbox = this.locators.ksqlDb.textField; + await textbox.click(); + await textbox.type(query); + await Delete(this.page); + await this.locators.ksqlDb.execute.click(); + + this.setValue(`tableName-${table}`, tableName); + } +); + +Then('KSQL DB table created', async function(this: PlaywrightWorld) { + await expectVisibility(this.locators.ksqlDb.success, "true"); +}); + +When('KSQL DB Stream clicked', async function(this: PlaywrightWorld) { + await this.locators.ksqlDb.streamsLink.click(); +}); + +When('KSQL DB Table clicked', async function(this: PlaywrightWorld) { + await this.locators.ksqlDb.tablesLink.click(); +}); + +Then('KSQL DB stream starts with: {string} visible is: {string}', async function(this: PlaywrightWorld, name: string, visible: string) { + const streamName = this.getValue(`streamName-${name}`); + await refreshPageAfterDelay(this.page); + await expectVisibility(this.page.getByRole('cell', { name: streamName }), visible); +}); + +Then('KSQL DB table starts with: {string} visible is: {string}', async function(this: PlaywrightWorld, name: string, visible: string) { + const tableName = this.getValue(`tableName-${name}`); + await refreshPageAfterDelay(this.page); + await expectVisibility(this.page.getByRole('cell', { name: tableName }).first(), visible); +}); + +Then('KSQL DB KSQL cleared', async function(this: PlaywrightWorld) { + await this.locators.ksqlDb.clear.click(); + await this.locators.ksqlDb.clearResults.click(); +}); + +Given( + 'KSQL DB KSQL data inserted to stream starts with: {string}, from table:', + async function(this: PlaywrightWorld, streamPrefix: string, table: DataTable) { + const streamName = this.getValue(`streamName-${streamPrefix}`); + const stream = streamName?.trim(); + const rows = table.hashes(); + + for (const row of rows) { + const id = row.Id; + const lat = row.la; + const lon = row.lo; + + const query = `INSERT INTO ${stream} (profileId, latitude, longitude) VALUES ('${id}', ${lat}, ${lon});`; + + await this.locators.ksqlDb.clear.click(); + const textbox = this.locators.ksqlDb.textField; + await textbox.click(); + await textbox.type(query); + await Delete(this.page); + await this.locators.ksqlDb.execute.click(); + + await expectVisibility(this.locators.ksqlDb.querySucceed, 'true'); + await this.locators.ksqlDb.clear.click(); + } +}); + +Given( + 'KSQL DB long query stream starts with: {string} stared', + async function(this: PlaywrightWorld, streamPrefix: string) { + await this.locators.ksqlDb.clear.click(); + const streamName = this.getValue(`streamName-${streamPrefix}`); + const query = `SELECT * FROM ${streamName} EMIT CHANGES;`.trim(); + + await this.locators.ksqlDb.clear.click(); + const textbox = this.locators.ksqlDb.textField; + await textbox.click(); + await textbox.type(query); + await Delete(this.page); + await this.locators.ksqlDb.execute.click(); + + await expectVisibility(this.locators.ksqlDb.consumingQueryExecution, 'true'); +}); + +Given('KSQL DB long query stoped', async function(this: PlaywrightWorld) { + await expectVisibility(this.locators.ksqlDb.consumingQueryExecution, 'true'); + const abortButton = this.locators.ksqlDb.abort; + await abortButton.scrollIntoViewIfNeeded(); + await this.locators.ksqlDb.abort.click({ force: true }); +}); \ No newline at end of file diff --git a/e2e-playwright/src/steps/ProduceMessages.ts b/e2e-playwright/src/steps/ProduceMessages.ts new file mode 100644 index 000000000..7563c307e --- /dev/null +++ b/e2e-playwright/src/steps/ProduceMessages.ts @@ -0,0 +1,136 @@ +/* eslint-disable no-unused-vars */ +import { Given, When, Then, setDefaultTimeout } from "@cucumber/cucumber"; +import { expect } from "@playwright/test"; +import { expectVisibility, expectVisuallyActive, refreshPageAfterDelay } from "../services/uiHelper"; +import { PlaywrightWorld } from "../support/PlaywrightWorld"; + +setDefaultTimeout(60 * 1000 * 4); + +Given('Topics TopicName partitions is: {int}', async function(this: PlaywrightWorld, count: number) { + await expectVisibility(this.locators.topicTopicName.partitions(count.toString()), 'true'); +}); + +Given('Topics TopicName Overview visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.topicTopicName.overview, visible); +}); + +Given('Topics TopicName Messages visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.topicTopicName.messages, visible); +}); + +Given('Topics TopicName Consumers visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.topicTopicName.consumers, visible); +}); + +Given('Topics TopicName Settings visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.topicTopicName.settings, visible); +}); + +Given('Topics TopicName Statistics visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.topicTopicName.statistics, visible); +}); + +Given('Produce message clicked', async function(this: PlaywrightWorld) { + await this.locators.topicTopicName.produceMessage.click(); +}); + +Then('ProduceMessage header visible', async function(this: PlaywrightWorld) { + await expect(this.locators.produceMessage.heading).toBeVisible(); +}); + +Given('ProduceMessage Key input is: {string}', async function(this: PlaywrightWorld, key: string) { + const textbox = this.locators.produceMessage.keyTextbox; + await textbox.fill(key); + + const actualValue = await textbox.inputValue(); + expect(actualValue).toContain(key); +}); + +Given('ProduceMessage Value input is: {string}', async function(this: PlaywrightWorld, value: string) { + const textbox = this.locators.produceMessage.valueTextbox; + await textbox.fill(value); + + const actualValue = await textbox.inputValue(); + expect(actualValue).toContain(value); +}); + +Given('ProduceMessage Headers input key is: {string}, value is: {string}', async function(this: PlaywrightWorld, key: string, value: string) { + const header = `{"${key}":"${value}"}`; + const textbox = this.locators.produceMessage.headersTextbox; + await textbox.clear(); + await textbox.fill(header); + + const actualValue = await textbox.inputValue(); + expect(actualValue).toContain(header); + } +); + +Given('ProduceMessage Produce Message button clicked', async function(this: PlaywrightWorld) { + await this.locators.produceMessage.produceMessage.click(); +}); + +When('Topics TopicName Messages clicked', async function(this: PlaywrightWorld) { + await this.locators.topicTopicName.messages.click(); +}); + +Then('TopicName messages contains key: {string}', async function(this: PlaywrightWorld, expectedKey: string) { + await this.locators.topicTopicName.messageValue(expectedKey).click() + await this.locators.topicTopicName.keyButton.click(); + await expectVisibility(this.locators.topicTopicName.messageKeyTextbox(expectedKey), "true"); +}); + +Then('TopicName messages contains value: {string}', async function(this: PlaywrightWorld, expectedValue: string) { + await this.locators.topicTopicName.valueButton.click() + await expectVisibility(this.locators.topicTopicName.messageValue(expectedValue), "true"); +}); + +Then('TopicName messages contains headers key is: {string}, value is: {string}', async function(this: PlaywrightWorld, headerKey: string, headerValue: string) { + const expectedHeader = `"${headerKey}":"${headerValue}"`; + await this.locators.topicTopicName.headersButton.click() + await expectVisibility(this.locators.topicTopicName.messageHeadersTextbox(expectedHeader), "true"); +}); + +Given('TopicName menu button clicked', async function(this: PlaywrightWorld) { + await this.locators.topicTopicName.dotsMenu.click(); +}); + +Then('TopicNameMenu clear messages active is: {string}', async function(this: PlaywrightWorld, state: string) { + await expectVisuallyActive(this.locators.topicTopicName.menuItemClearMessages, state); +}); + +When('TopicNameMenu edit settings clicked', async function(this: PlaywrightWorld) { + await this.locators.topicTopicName.menuItemEditSettings.click(); +}); + +When('TopicName cleanup policy set to: {string}', async function(this: PlaywrightWorld, policy: string) { + await this.locators.topicTopicName.cleanupPolicyDropdown.click(); + await this.locators.topicTopicName.cleanupPolicyDropdownItem(policy).click(); +}); + +When('TopicName UpdateTopic button clicked', async function(this: PlaywrightWorld) { + await this.locators.topicTopicName.updateTopicButton.click(); +}); + +Then('Topics TopicName Overview click', async function(this: PlaywrightWorld) { + await this.locators.topicTopicName.overview.click(); +}); + +Then('TopicName messages count is {string}', async function(this: PlaywrightWorld, expectedCount: string) { + await expectVisibility(this.locators.topicTopicName.messagesCount(expectedCount), "true"); +}); + +When('TopicName clear messages clicked', async function(this: PlaywrightWorld) { + await this.locators.topicTopicName.messagesDropdown.click(); + await this.locators.topicTopicName.clearMessages.click(); +}); + +When('TopicName menu clear messages clicked', async function(this: PlaywrightWorld) { + await this.locators.topicTopicName.menuItemClearMessages.click(); + await this.locators.topicTopicName.confirm.click(); + } +); + +When('TopicName menu RecreateTopic clicked', async function(this: PlaywrightWorld) { + await this.locators.topicTopicName.menuItemRecreateTopic.click(); + await this.locators.topicTopicName.confirm.click(); +}); \ No newline at end of file diff --git a/e2e-playwright/src/steps/SchemaRegistry.steps.ts b/e2e-playwright/src/steps/SchemaRegistry.steps.ts new file mode 100644 index 000000000..1ef0b921d --- /dev/null +++ b/e2e-playwright/src/steps/SchemaRegistry.steps.ts @@ -0,0 +1,123 @@ +/* eslint-disable no-unused-vars */ +import { Given, When, Then, setDefaultTimeout } from "@cucumber/cucumber"; +import { PlaywrightWorld } from "../support/PlaywrightWorld"; +import { expectVisibility } from "../services/uiHelper"; +import { generateName } from "../services/commonFunctions"; +import { getAvroSchema, getAvroUpdatedSchema, getJsonSchema, getProtobufSchema } from "../services/schemas" +import { clearWithSelectAll } from '../services/commonFunctions' + +setDefaultTimeout(60 * 1000 * 2); + +Given('SchemaRegistry CheateSchema clicked', async function(this: PlaywrightWorld) { + await this.locators.schemaRegistry.createSchemaButton.click(); +}); + +Given('SchemaRegistryCreate is visible', async function(this: PlaywrightWorld) { + await expectVisibility(this.locators.schemaRegistry.createHeading, "true"); +}); + +Given('SchemaRegistryCreate Subject visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.schemaRegistry.subjectTextBox, visible); +}); + +Given('SchemaRegistryCreate Schema visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.schemaRegistry.schemaTextBox, visible); +}); + +Given('SchemaRegistryCreate SchemaType visible is: {string}', async function(this: PlaywrightWorld, visible: string) { + await expectVisibility(this.locators.schemaRegistry.schemaTypeDropDown, visible); +}); + +When('SchemaRegistryCreate Subject input starts with: {string}', async function(this: PlaywrightWorld, prefix: string) { + const subjectName = generateName(prefix); + await this.locators.schemaRegistry.subjectTextBox.fill(subjectName); + this.setValue(`subjectName-${prefix}`, subjectName); +}); + +When('SchemaRegistryCreate Schema input from avro', async function(this: PlaywrightWorld) { + const schema = getAvroSchema(); + await this.locators.schemaRegistry.schemaTextBox.fill(schema); +}); + +When('SchemaRegistryCreate Schema input from json', async function(this: PlaywrightWorld) { + const schema = getJsonSchema(); + await this.locators.schemaRegistry.schemaType.click(); + await this.locators.schemaRegistry.schemaTypeElement("JSON").click(); + await this.locators.schemaRegistry.schemaTextBox.fill(schema); +}); + +When('SchemaRegistryCreate Schema input from protobuf', async function(this: PlaywrightWorld) { + const schema = getProtobufSchema(); + await this.locators.schemaRegistry.schemaType.click(); + await this.locators.schemaRegistry.schemaTypeElement("PROTOBUF").click(); + await this.locators.schemaRegistry.schemaTextBox.fill(schema); +}); + +When('SchemaRegistryCreate Submit clicked', async function(this: PlaywrightWorld) { + await this.locators.schemaRegistry.submit.click(); +}); + +Then('SchemaRegistrySchemaName starts with: {string}, visible is: {string}', + async function(this: PlaywrightWorld, prefix: string, visible: string) { + const streamName = this.getValue(`subjectName-${prefix}`); + const locator = this.locators.schemaName.heading(streamName); + await expectVisibility(locator, visible); + } +); + +Given('SchemaRegistry click on schema starts with: {string}', async function(this: PlaywrightWorld, prefix: string) { + const schemaName = this.getValue(`subjectName-${prefix}`); + const locator = await this.locators.schemaRegistry.toSchema(schemaName).click(); +}); + +Given('SchemaRegistrySchemaName version is: {string}', async function(this: PlaywrightWorld, version: string) { + await expectVisibility( this.locators.schemaName.version(version), "true"); +}); + +Given('SchemaRegistrySchemaName type is: {string}', async function(this: PlaywrightWorld, expectedType: string) { + await expectVisibility( this.locators.schemaName.type(expectedType), "true"); +}); + +Given('SchemaRegistrySchemaName Compatibility is: {string}', async function(this: PlaywrightWorld, expectedCompatibility: string) { + await expectVisibility( this.locators.schemaName.compatibility(expectedCompatibility), "true"); +}); + +Given('SchemaRegistrySchemaName EditSchema clicked', async function(this: PlaywrightWorld) { + await this.locators.schemaName.editSchema.click(); +}); + +Given('SchemaRegistrySchemaNameEdit New schema is update avro not valid', async function(this: PlaywrightWorld) { + const schema = getAvroUpdatedSchema(); + const textBox = this.locators.schemaName.newSchemaTextbox; + await textBox.click(); + await clearWithSelectAll(this.page); + await textBox.focus(); + await textBox.type(schema) +}); + +Given( + 'SchemaRegistrySchemaNameEdit old Compatibility is: {string}, new Compatibility is: {string}', + async function(this: PlaywrightWorld, oldLevel: string, newLevel: string) { + await this.locators.schemaName.editDropdown(oldLevel).click(); + await this.locators.schemaName.editDropdownElement(newLevel).click(); + } +); + +When('SchemaRegistrySchemaNameEdit submit clicked', async function(this: PlaywrightWorld) { + await this.locators.schemaName.submit.click(); +}); + +Then('Error incompatible visible', async function(this: PlaywrightWorld) { + await expectVisibility(this.locators.schemaName.incompatibeError, "true"); +}); + +When('SchemaRegistrySchemaName remove schema clicked', async function(this: PlaywrightWorld) { + await this.locators.schemaName.menu.click(); + await this.locators.schemaName.removeSchema.click() + await this.locators.schemaName.confirm.click(); +}); + +Then('Schema starts with: {string} deleted', async function(this: PlaywrightWorld, prefix: string) { + const schemaName = this.getValue(`subjectName-${prefix}`); + +}); \ No newline at end of file