Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions tests/e2e/pageObjects/workbench-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export class WorkbenchPage {
customTutorials = Selector('[data-testid=accordion-button-custom-tutorials]');
tutorialOpenUploadButton = Selector('[data-testid=open-upload-tutorial-btn]');
tutorialLinkField = Selector('[data-testid=tutorial-link-field]');
tutorialLatestDeleteIcon = Selector('[data-testid^=delete-tutorial-icon-]').nth(0);
tutorialDeleteButton = Selector('[data-testid^=delete-tutorial-]').withText('Delete');
tutorialLatestDeleteIcon = Selector('[data-testid^=delete-tutorial-icon-]').nth(0);
tutorialDeleteButton = Selector('[data-testid^=delete-tutorial-]').withText('Delete');
tutorialNameField = Selector('[data-testid=tutorial-name-field]');
tutorialSubmitButton = Selector('[data-testid=submit-upload-tutorial-btn]');
tutorialImport = Selector('[data-testid=import-tutorial]');
Expand Down Expand Up @@ -233,32 +233,59 @@ export class WorkbenchPage {
const actualCommandResult = await this.queryCardContainer.nth(childNum).find(this.cssQueryTextResult).textContent;
await t.expect(actualCommandResult).contains(result, 'Actual command result is not equal to executed');
}

/**
* Get selector with tutorial name
* @param tutorialName name of the uploaded tutorial
*/
async getAccordionButtonWithName(tutorialName: string): Promise<Selector> {
return Selector(`[data-testid=accordion-button-${tutorialName}]`);
}

/**
* Get internal tutorial link with .md name
* @param internalLink name of the .md file
*/
async getInternalLinkWithManifest(internalLink: string): Promise<Selector> {
return Selector(`[data-testid="internal-link-${internalLink}.md"]`);
}

/**
* Get internal tutorial link without .md name
* @param internalLink name of the label
*/
async getInternalLinkWithoutManifest(internalLink: string): Promise<Selector> {
return Selector(`[data-testid="internal-link-${internalLink}"]`);
}

/**
* Find tutorial selector by name
* @param name A tutorial name
*/
async getTutorialByName(name: string): Promise<Selector> {
return Selector('div').withText(name);
}

/**
* Find image in tutorial by alt text
* @param alt Image alt text
*/
async getTutorialImageByAlt(alt: string): Promise<Selector> {
return Selector('img').withAttribute('alt', alt);
}

/**
* Wait until image rendered
* @param selector Image selector
*/
async waitUntilImageRendered(selector: Selector): Promise<void> {
const searchTimeout = 5 * 1000; // 5 sec maximum wait
const startTime = Date.now();
let imageHeight = await selector.getStyleProperty('height');

do {
imageHeight = await selector.getStyleProperty('height');
}
while ((imageHeight == '0px') && Date.now() - startTime < searchTimeout);
}
}
Binary file modified tests/e2e/test-data/upload-tutorials/testTutorials.zip
Binary file not shown.
30 changes: 25 additions & 5 deletions tests/e2e/tests/regression/workbench/import-tutorials.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,37 @@ let internalLinkName1 = 'probably-1';
let internalLinkName2 = 'vector-2';

fixture `Upload custom tutorials`
.meta({type: 'regression', rte: rte.standalone})
.meta({ type: 'regression', rte: rte.standalone })
.page(commonUrl)
.beforeEach(async t => {
await acceptLicenseTermsAndAddDatabaseApi(ossStandaloneConfig, ossStandaloneConfig.databaseName);
await t.click(myRedisDatabasePage.workbenchButton);
})
.afterEach(async() => {
.afterEach(async () => {
await deleteStandaloneDatabaseApi(ossStandaloneConfig);
});
// https://redislabs.atlassian.net/browse/RI-4186, https://redislabs.atlassian.net/browse/RI-4198, https://redislabs.atlassian.net/browse/RI-4302
/* https://redislabs.atlassian.net/browse/RI-4186, https://redislabs.atlassian.net/browse/RI-4198,
https://redislabs.atlassian.net/browse/RI-4302, https://redislabs.atlassian.net/browse/RI-4318
*/
test('Verify that user can upload tutorial with local zip file without manifest.json', async t => {
// Verify that user can upload custom tutorials on docker version
const imageExternalPath = 'RedisInsight screen external';
const imageRelativePath = 'RedisInsight screen relative';
folder1 = 'folder-1';
folder2 = 'folder-2';
internalLinkName1 = 'probably-1';
internalLinkName2 = 'vector-2';

// Verify that user can see the “MY TUTORIALS” section in the Enablement area.
await t.expect(workbenchPage.customTutorials.visible).ok('custom tutorials sections is not visible');
await t.click(workbenchPage.tutorialOpenUploadButton);
await t.expect(workbenchPage.tutorialSubmitButton.hasAttribute('disabled')).ok('submit button is not disabled');

// Verify that User can request to add a new custom Tutorial by uploading a .zip archive from a local folder
await t.setFilesToUpload(workbenchPage.tutorialImport, [filePath]);
await t.click(workbenchPage.tutorialSubmitButton);
await t.expect(workbenchPage.tutorialAccordionButton.withText(tutorialName).visible).ok(`${tutorialName} tutorial is not uploaded`);

// Verify that when user upload a .zip archive without a .json manifest, all markdown files are inserted at the same hierarchy level
await t.click(workbenchPage.tutorialAccordionButton.withText(tutorialName));
await t.expect((await workbenchPage.getAccordionButtonWithName(folder1)).visible).ok(`${folder1} is not visible`);
Expand All @@ -52,16 +59,29 @@ test('Verify that user can upload tutorial with local zip file without manifest.
.ok(`${internalLinkName1} is not visible`);
await t.click(await workbenchPage.getAccordionButtonWithName(folder2));
await t.expect((await workbenchPage.getInternalLinkWithManifest(internalLinkName2)).visible)
.ok(`${internalLinkName1} is not visible`);
.ok(`${internalLinkName2} is not visible`);
await t.expect(workbenchPage.scrolledEnablementArea.exists).notOk('enablement area is visible before clicked');
await t.click((await workbenchPage.getInternalLinkWithManifest(internalLinkName1)));
await t.expect(workbenchPage.scrolledEnablementArea.visible).ok('enablement area is not visible after clicked');

// Verify that user can see image in custom tutorials by providing absolute external path in md file
const imageExternal = await workbenchPage.getTutorialImageByAlt(imageExternalPath);
await workbenchPage.waitUntilImageRendered(imageExternal);
const imageExternalHeight = await imageExternal.getStyleProperty('height');
await t.expect(parseInt(imageExternalHeight.replace(/[^\d]/g, ''))).gte(150);

// Verify that user can see image in custom tutorials by providing relative path in md file
const imageRelative = await workbenchPage.getTutorialImageByAlt(imageRelativePath);
await workbenchPage.waitUntilImageRendered(imageRelative);
const imageRelativeHeight = await imageRelative.getStyleProperty('height');
await t.expect(parseInt(imageRelativeHeight.replace(/[^\d]/g, ''))).gte(150);

// Verify that when User delete the tutorial, then User can see this tutorial and relevant markdown files are deleted from: the Enablement area in Workbench
await t.click(workbenchPage.closeEnablementPage);
await t.click(workbenchPage.tutorialLatestDeleteIcon);
await t.expect(workbenchPage.tutorialDeleteButton.visible).ok('Delete popup is not visible');
await t.click(workbenchPage.tutorialDeleteButton);
await t.expect(workbenchPage.tutorialDeleteButton.exists).notOk('Delete popup is still visible');
// Verify that when User delete the tutorial, then User can see this tutorial and relevant markdown files are deleted from: the Enablement area in Workbench
await t.expect((workbenchPage.tutorialAccordionButton.withText(tutorialName).exists))
.notOk(`${tutorialName} tutorial is not uploaded`);
});
Expand Down