Skip to content

Commit c71f203

Browse files
authored
Merge pull request #2057 from RedisInsight/e2e/feature/RI-4352_bulk-upload-from-tutorials
E2e/feature/ri 4352 bulk upload from tutorials
2 parents 2730459 + 30769e3 commit c71f203

37 files changed

+560
-143
lines changed

tests/e2e/helpers/common.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import * as path from 'path';
2+
import * as archiver from 'archiver';
3+
import * as fs from 'fs';
14
import { ClientFunction, RequestMock, t } from 'testcafe';
25
import { Chance } from 'chance';
36
import { apiUrl, commonUrl } from './conf';
@@ -188,4 +191,30 @@ export class Common {
188191
static async getPageUrl(): Promise<string> {
189192
return (await ClientFunction(() => window.location.href))();
190193
}
194+
195+
/**
196+
* Create Zip archive from folder
197+
* @param folderPath Path to folder to archive
198+
* @param zipName Zip archive name
199+
*/
200+
static async createZipFromFolder(folderPath: string, zipName: string): Promise<void> {
201+
const sourceDir = path.join(__dirname, folderPath);
202+
const zipFilePath = path.join(__dirname, zipName);
203+
const output = fs.createWriteStream(zipFilePath);
204+
const archive = archiver('zip', { zlib: { level: 9 } });
205+
206+
// Add the contents of the directory to the zip archive
207+
archive.directory(sourceDir, false);
208+
// Finalize the archive and write it to disk
209+
await archive.finalize();
210+
archive.pipe(output);
211+
}
212+
213+
/**
214+
* Delete file from folder
215+
* @param folderPath Path to file
216+
*/
217+
static async deleteFileFromFolder(filePath: string): Promise<void> {
218+
fs.unlinkSync(path.join(__dirname, filePath));
219+
}
191220
}

tests/e2e/helpers/database.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export async function addNewStandaloneDatabase(databaseParameters: AddNewDatabas
2828
// Wait for database to be exist
2929
.expect(myRedisDatabasePage.dbNameList.withExactText(databaseParameters.databaseName ?? '').exists).ok('The database not displayed', { timeout: 10000 })
3030
// Close message
31-
.click(myRedisDatabasePage.toastCloseButton);
31+
.click(myRedisDatabasePage.Toast.toastCloseButton);
3232
}
3333

3434
/**
@@ -77,7 +77,7 @@ export async function addOSSClusterDatabase(databaseParameters: OSSClusterParame
7777
await t
7878
.click(myRedisDatabasePage.AddRedisDatabase.addRedisDatabaseButton)
7979
// Check for info message that DB was added
80-
.expect(myRedisDatabasePage.databaseInfoMessage.exists).ok('Info message not exists', { timeout: 10000 })
80+
.expect(myRedisDatabasePage.Toast.toastHeader.exists).ok('Info message not exists', { timeout: 10000 })
8181
// Wait for database to be exist
8282
.expect(myRedisDatabasePage.dbNameList.withExactText(databaseParameters.ossClusterDatabaseName).exists).ok('The database not displayed', { timeout: 10000 });
8383
}

tests/e2e/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@
3030
"@types/node": "18.11.9"
3131
},
3232
"devDependencies": {
33+
"@types/archiver": "^5.3.2",
3334
"@types/chance": "1.1.3",
3435
"@types/edit-json-file": "1.7.0",
3536
"@types/supertest": "^2.0.8",
3637
"@typescript-eslint/eslint-plugin": "4.28.2",
3738
"@typescript-eslint/parser": "4.28.2",
39+
"archiver": "^5.3.1",
3840
"chance": "1.1.8",
3941
"cross-env": "^7.0.3",
4042
"dotenv-cli": "^5.0.0",

tests/e2e/pageObjects/base-page.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { t } from 'testcafe';
22
import { NavigationPanel } from './components/navigation-panel';
3+
import { Toast } from './components/toast';
34

45
export class BasePage {
56
NavigationPanel = new NavigationPanel();
7+
Toast = new Toast();
68

79
/**
810
* Reload page

tests/e2e/pageObjects/browser-page.ts

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ export class BrowserPage extends InstancePage {
6161
addJsonObjectButton = Selector('[data-testid=add-object-btn]');
6262
addJsonFieldButton = Selector('[data-testid=add-field-btn]');
6363
expandJsonObject = Selector('[data-testid=expand-object]');
64-
toastCloseButton = Selector('[data-test-subj=toastCloseButton]');
6564
scoreButton = Selector('[data-testid=score-button]');
6665
sortingButton = Selector('[data-testid=header-sorting-button]');
6766
editJsonObjectButton = Selector('[data-testid=edit-object-btn]');
@@ -103,7 +102,6 @@ export class BrowserPage extends InstancePage {
103102
editZsetButton = Selector('[data-testid^=zset-edit-button-]');
104103
editListButton = Selector('[data-testid^=edit-list-button-]');
105104
cancelStreamGroupBtn = Selector('[data-testid=cancel-stream-groups-btn]');
106-
submitTooltipBtn = Selector('[data-testid=submit-tooltip-btn]');
107105
patternModeBtn = Selector('[data-testid=search-mode-pattern-btn]');
108106
redisearchModeBtn = Selector('[data-testid=search-mode-redisearch-btn]');
109107
showFilterHistoryBtn = Selector('[data-testid=show-suggestions-btn]');
@@ -198,7 +196,6 @@ export class BrowserPage extends InstancePage {
198196
//TEXT ELEMENTS
199197
keySizeDetails = Selector('[data-testid=key-size-text]');
200198
keyLengthDetails = Selector('[data-testid=key-length-text]');
201-
notificationMessage = Selector('[data-test-subj=euiToastHeader]');
202199
keyNameInTheList = Selector(this.cssSelectorKey);
203200
databaseNames = Selector('[data-testid^=db_name_]');
204201
hashFieldsList = Selector('[data-testid^=hash-field-] span');
@@ -265,7 +262,6 @@ export class BrowserPage extends InstancePage {
265262
streamConsumerName = Selector('[data-testid^=stream-consumer-]');
266263
consumerGroup = Selector('[data-testid^=stream-group-]');
267264
entryIdInfoIcon = Selector('[data-testid=entry-id-info-icon]');
268-
errorMessage = Selector('[data-test-subj=toast-error]');
269265
entryIdError = Selector('[data-testid=id-error]');
270266
pendingCount = Selector('[data-testid=pending-count]');
271267
lastRefreshMessage = Selector('[data-testid=refresh-message]');
@@ -442,7 +438,7 @@ export class BrowserPage extends InstancePage {
442438
await t.typeText(this.streamValue, value, { replace: true, paste: true });
443439
await t.expect(this.addKeyButton.withAttribute('disabled').exists).notOk('Add Key button not clickable');
444440
await t.click(this.addKeyButton);
445-
await t.click(this.toastCloseButton);
441+
await t.click(this.Toast.toastCloseButton);
446442
}
447443

448444
/**
@@ -545,15 +541,10 @@ export class BrowserPage extends InstancePage {
545541
return keyNameInTheList.exists;
546542
}
547543

548-
//Getting the text of the Notification message
549-
async getMessageText(): Promise<string> {
550-
return this.notificationMessage.textContent;
551-
}
552-
553544
//Delete key from details
554545
async deleteKey(): Promise<void> {
555-
if (await this.toastCloseButton.exists) {
556-
await t.click(this.toastCloseButton);
546+
if (await this.Toast.toastCloseButton.exists) {
547+
await t.click(this.Toast.toastCloseButton);
557548
}
558549
await t.click(this.keyNameInTheList);
559550
await t.click(this.deleteKeyButton);
@@ -640,8 +631,8 @@ export class BrowserPage extends InstancePage {
640631
* @param keyValue The hash value
641632
*/
642633
async addFieldToHash(keyFieldValue: string, keyValue: string): Promise<void> {
643-
if (await this.toastCloseButton.exists) {
644-
await t.click(this.toastCloseButton);
634+
if (await this.Toast.toastCloseButton.exists) {
635+
await t.click(this.Toast.toastCloseButton);
645636
}
646637
await t.click(this.addKeyValueItemsButton);
647638
await t.typeText(this.hashFieldInput, keyFieldValue, { replace: true, paste: true });
@@ -734,8 +725,8 @@ export class BrowserPage extends InstancePage {
734725
* @param keyMember The value of the set member
735726
*/
736727
async addMemberToSet(keyMember: string): Promise<void> {
737-
if (await this.toastCloseButton.exists) {
738-
await t.click(this.toastCloseButton);
728+
if (await this.Toast.toastCloseButton.exists) {
729+
await t.click(this.Toast.toastCloseButton);
739730
}
740731
await t
741732
.click(this.addKeyValueItemsButton)
@@ -749,8 +740,8 @@ export class BrowserPage extends InstancePage {
749740
* @param score The value of the score
750741
*/
751742
async addMemberToZSet(keyMember: string, score: string): Promise<void> {
752-
if (await this.toastCloseButton.exists) {
753-
await t.click(this.toastCloseButton);
743+
if (await this.Toast.toastCloseButton.exists) {
744+
await t.click(this.Toast.toastCloseButton);
754745
}
755746
await t
756747
.click(this.addKeyValueItemsButton)
@@ -784,8 +775,8 @@ export class BrowserPage extends InstancePage {
784775
* @param element The value of the list element
785776
*/
786777
async addElementToList(element: string): Promise<void> {
787-
if (await this.toastCloseButton.exists) {
788-
await t.click(this.toastCloseButton);
778+
if (await this.Toast.toastCloseButton.exists) {
779+
await t.click(this.Toast.toastCloseButton);
789780
}
790781
await t
791782
.click(this.addKeyValueItemsButton)

tests/e2e/pageObjects/components/myRedisDatabase/add-redis-database.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ export class AddRedisDatabase {
4040
secretKeyInput = Selector('[data-testid=secret-key]');
4141
welcomePageTitle = Selector('[data-testid=welcome-page-title]');
4242
databaseIndexInput = Selector('[data-testid=db]');
43-
errorMessage = Selector('[data-test-subj=toast-error]');
4443
databaseIndexMessage = Selector('[data-testid=db-index-message]');
4544
primaryGroupNameInput = Selector('[data-testid=primary-group]');
4645
masterGroupPassword = Selector('[data-testid=sentinel-master-password]');
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Selector } from 'testcafe';
2+
3+
export class Toast {
4+
toastHeader = Selector('[data-test-subj=euiToastHeader]');
5+
toastBody = Selector('[class*=euiToastBody]');
6+
toastSuccess = Selector('[class*=euiToast--success]');
7+
toastError = Selector('[class*=euiToast--danger]');
8+
toastCloseButton = Selector('[data-test-subj=toastCloseButton]');
9+
toastSubmitBtn = Selector('[data-testid=submit-tooltip-btn]');
10+
toastCancelBtn = Selector('[data-testid=toast-cancel-btn]');
11+
}

tests/e2e/pageObjects/my-redis-databases-page.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export class MyRedisDatabasePage extends BasePage {
1818
//BUTTONS
1919
deleteDatabaseButton = Selector('[data-testid^=delete-instance-]');
2020
confirmDeleteButton = Selector('[data-testid^=delete-instance-]').withExactText('Remove');
21-
toastCloseButton = Selector('[data-test-subj=toastCloseButton]');
21+
2222
deleteButtonInPopover = Selector('#deletePopover button');
2323
confirmDeleteAllDbButton = Selector('[data-testid=delete-selected-dbs]');
2424
editDatabaseButton = Selector('[data-testid^=edit-instance]');
@@ -60,7 +60,6 @@ export class MyRedisDatabasePage extends BasePage {
6060
moduleQuantifier = Selector('[data-testid=_module]');
6161
dbNameList = Selector('[data-testid^=instance-name]', { timeout: 3000 });
6262
tableRowContent = Selector('[data-test-subj=database-alias-column]');
63-
databaseInfoMessage = Selector('[data-test-subj=euiToastHeader]');
6463
hostPort = Selector('[data-testid=host-port]');
6564
noResultsFoundMessage = Selector('div').withExactText('No results found');
6665
noResultsFoundText = Selector('div').withExactText('No databases matched your search. Try reducing the criteria.');
@@ -79,8 +78,8 @@ export class MyRedisDatabasePage extends BasePage {
7978
* @param dbName The name of the database to be opened
8079
*/
8180
async clickOnDBByName(dbName: string): Promise<void> {
82-
if (await this.toastCloseButton.exists) {
83-
await t.click(this.toastCloseButton);
81+
if (await this.Toast.toastCloseButton.exists) {
82+
await t.click(this.Toast.toastCloseButton);
8483
}
8584
const db = this.dbNameList.withExactText(dbName.trim());
8685
await t.expect(db.exists).ok(`"${dbName}" database doesn't exist`, {timeout: 10000});
@@ -103,8 +102,8 @@ export class MyRedisDatabasePage extends BasePage {
103102
.click(this.deleteDatabaseButton)
104103
.click(this.confirmDeleteButton);
105104
}
106-
if (await this.toastCloseButton.exists) {
107-
await t.click(this.toastCloseButton);
105+
if (await this.Toast.toastCloseButton.exists) {
106+
await t.click(this.Toast.toastCloseButton);
108107
}
109108
}
110109

tests/e2e/pageObjects/workbench-page.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export class WorkbenchPage extends InstancePage {
2525
cssCustomPluginTableResult = '[data-testid^=query-table-result-client]';
2626
cssCommandExecutionDateTime = '[data-testid=command-execution-date-time]';
2727
cssRowInVirtualizedTable = '[data-testid^=row-]';
28+
cssTutorialDeleteIcon = '[data-testid^=delete-tutorial-icon-]';
2829
//-------------------------------------------------------------------------------------------
2930
//DECLARATION OF SELECTORS
3031
//*Declare all elements/components of the relevant page.
@@ -37,7 +38,7 @@ export class WorkbenchPage extends InstancePage {
3738
tutorialOpenUploadButton = Selector('[data-testid=open-upload-tutorial-btn]');
3839
tutorialLinkField = Selector('[data-testid=tutorial-link-field]');
3940
tutorialLatestDeleteIcon = Selector('[data-testid^=delete-tutorial-icon-]').nth(0);
40-
tutorialDeleteButton = Selector('[data-testid^=delete-tutorial-]').withText('Delete');
41+
tutorialDeleteButton = Selector('button[data-testid^=delete-tutorial-]');
4142
tutorialNameField = Selector('[data-testid=tutorial-name-field]');
4243
tutorialSubmitButton = Selector('[data-testid=submit-upload-tutorial-btn]');
4344
tutorialImport = Selector('[data-testid=import-tutorial]');
@@ -80,6 +81,8 @@ export class WorkbenchPage extends InstancePage {
8081
copyCommand = Selector('[data-testid=copy-command]');
8182
redisStackTimeSeriesLoadMorePoints = Selector('[data-testid=preselect-Load more data points]');
8283
documentHashCreateButton = Selector('[data-testid=preselect-auto-Create]');
84+
uploadDataBulkBtn = Selector('[data-testid=upload-data-bulk-btn]');
85+
uploadDataBulkApplyBtn = Selector('[data-testid=upload-data-bulk-apply-btn]');
8386
//ICONS
8487
noCommandHistoryIcon = Selector('[data-testid=wb_no-results__icon]');
8588
parametersAnchor = Selector('[data-testid=parameters-anchor]');
@@ -248,39 +251,52 @@ export class WorkbenchPage extends InstancePage {
248251
* Get selector with tutorial name
249252
* @param tutorialName name of the uploaded tutorial
250253
*/
251-
async getAccordionButtonWithName(tutorialName: string): Promise<Selector> {
254+
getAccordionButtonWithName(tutorialName: string): Selector {
252255
return Selector(`[data-testid=accordion-button-${tutorialName}]`);
253256
}
254257

255258
/**
256259
* Get internal tutorial link with .md name
257260
* @param internalLink name of the .md file
258261
*/
259-
async getInternalLinkWithManifest(internalLink: string): Promise<Selector> {
262+
getInternalLinkWithManifest(internalLink: string): Selector {
260263
return Selector(`[data-testid="internal-link-${internalLink}.md"]`);
261264
}
262265

263266
/**
264267
* Get internal tutorial link without .md name
265268
* @param internalLink name of the label
266269
*/
267-
async getInternalLinkWithoutManifest(internalLink: string): Promise<Selector> {
270+
getInternalLinkWithoutManifest(internalLink: string): Selector {
268271
return Selector(`[data-testid="internal-link-${internalLink}"]`);
269272
}
270273

271274
/**
272275
* Find tutorial selector by name
273276
* @param name A tutorial name
274277
*/
275-
async getTutorialByName(name: string): Promise<Selector> {
278+
getTutorialByName(name: string): Selector {
276279
return Selector('div').withText(name);
277280
}
278281

282+
/**
283+
* Delete tutorial by name
284+
* @param name A tutorial name
285+
*/
286+
async deleteTutorialByName(name: string): Promise<void> {
287+
const deleteTutorialBtn = this.tutorialAccordionButton.withText(name).find(this.cssTutorialDeleteIcon);
288+
if (await this.closeEnablementPage.exists) {
289+
await t.click(this.closeEnablementPage);
290+
}
291+
await t.click(deleteTutorialBtn);
292+
await t.click(this.tutorialDeleteButton);
293+
}
294+
279295
/**
280296
* Find image in tutorial by alt text
281297
* @param alt Image alt text
282298
*/
283-
async getTutorialImageByAlt(alt: string): Promise<Selector> {
299+
getTutorialImageByAlt(alt: string): Selector {
284300
return Selector('img').withAttribute('alt', alt);
285301
}
286302

-2.22 KB
Binary file not shown.

0 commit comments

Comments
 (0)