-
- {!!output && (
-
-
-
-
-
- {parse(output)}
-
-
- )}
- {!!columns?.length && (
-
-
-
- )}
- { !loading && createDbContent?.cloud && (
-
-
-
- )}
- {(!!summaryText || !!summaryImgPath || !!summaryImgDark || !!summaryImgLight) && (
-
-
- {(!!summaryImgPath || !!summaryImgDark || !!summaryImgLight) && (
-

- )}
- {!!summaryText &&
{parse(summaryText)}
}
-
-
- )}
-
+
MAX_ELEMENT_WIDTH })}>
+
+
+ {width > MAX_ELEMENT_WIDTH
+ ?
+ : }
+
+
+ {renderTitle(width, module)}
+
+ {CONTENT[moduleName]?.text.map((item: string) => (
+ width > MIN_ELEMENT_WIDTH ? <>{item}
> : item
+ ))}
+
+
+ {CONTENT[moduleName]?.improvements.map((item: string) => (
+
+ ))}
+
+ {!!CONTENT[moduleName]?.additionalText && (
+
+ {CONTENT[moduleName]?.additionalText.map((item: string) => (
+ width > MIN_ELEMENT_WIDTH ? <>{item}
> : item
+ ))}
+
+ )}
+ {renderText(module)}
+
+
+
+
+ Learn More
+
+
+
+ Get Started For Free
+
+
+
)
}
diff --git a/redisinsight/ui/src/pages/workbench/components/module-not-loaded/styles.module.scss b/redisinsight/ui/src/pages/workbench/components/module-not-loaded/styles.module.scss
index 82e4e247d3..8268533ff4 100644
--- a/redisinsight/ui/src/pages/workbench/components/module-not-loaded/styles.module.scss
+++ b/redisinsight/ui/src/pages/workbench/components/module-not-loaded/styles.module.scss
@@ -1,97 +1,144 @@
.container {
- font-family: 'Graphik', sans-serif !important;
+ padding: 41px 30px;
+
+ .title {
+ font-family: 'Graphik', sans-serif;
+ font-size: 32px;
+ font-weight: 600;
+ word-break: break-word;
+ margin-bottom: 20px;
+ }
- br {
- display: block !important;
- content: "";
- margin: 2em;
- font-size: 34%;
+ .linksWrapper .text,
+ .text {
+ font-family: 'Graphik', sans-serif;
+ font-size: 14px;
+ line-height: 17px;
+ word-break: break-word;
+ color: var(--wbTextColor) !important;
+ text-decoration: none !important;
}
- ul {
- list-style: disc !important;
- padding-left: 20px !important;
+ .bigText {
+ font-family: 'Graphik', sans-serif;
+ font-size: 20px;
+ line-height: 24px;
+ word-break: break-word;
+ color: var(--wbTextColor);
+ margin-bottom: 20px;
}
- :global(.query-card-output-response-fail) {
- padding: 6px 0;
- color: var(--euiColorDanger) !important
+ .flex {
+ display: flex;
+ flex-direction: row-reverse;
+ align-items: center;
+ justify-content: space-between;
+
+ .icon {
+ width: 173px;
+ margin-left: 15px;
+ }
+
+ .bigIcon {
+ width: 317px;
+ margin-bottom: 42px;
+ }
}
-}
-.table {
- margin: 0 -20px !important;
+ .list.bloomList {
+ display: flex;
+ flex-wrap: wrap;
- :global(.euiTableRowCell) {
- background-color: var(--euiPageBackgroundColor) !important;
+ .listItem {
+ margin-right: 18px;
+ }
}
- br {
- font-size: 18%;
+ .listItem {
+ display: flex;
+ margin-bottom: 20px;
+
+ .iconWrapper {
+ display: flex;
+ flex: 0 0 16px;
+ align-items: center;
+ justify-content: center;
+ width: 16px;
+ height: 16px;
+ background: var(--wbActiveIconColor);
+ border-radius: 50%;
+ margin-right: 10px;
+ }
+
+ .listIcon {
+ width: 10px;
+ height: 10px;
+
+ path {
+ fill: var(--euiPageBackgroundColor);
+ }
+ }
}
-
- a {
- line-height: 24px;
- text-align: left;
- text-decoration: underline;
- color: var(--buttonSecondaryTextColor);
-
- &:hover {
- text-decoration: none;
+
+ .linksWrapper {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ margin-top: 20px;
+
+ .link {
+ margin-right: 20px;
+ color: var(-wbTextColor);
}
}
-}
+
+ .marginBottom {
+ margin-bottom: 20px;
+ }
-.summary {
- display: flex;
+ &.fullScreen {
+ padding: 89px 77px;
- > div {
- padding-bottom: 10px;
- }
-}
+ .flex {
+ flex-direction: column;
+ }
-.alertIconWrapper {
- height: 19px;
- display: inline-flex;
- align-items: center;
-}
+ .contentWrapper {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
-.summaryImg {
- width: 468px !important;
- height: 174px !important;
- margin-right: 22px;
-}
+ .title,
+ .text,
+ .bigText {
+ text-align: center;
+ }
-@media only screen and (max-width: 700px) {
- .summaryImg {
- width: 312px !important;
- height: 116px !important;
- }
-}
+ .list {
+ display: flex;
+ justify-content: center;
+ }
-@media only screen and (max-width: 1200px) {
- .summary {
- display: block;
- }
-}
+ .listItem {
+ margin-right: 15px;
-@media only screen and (max-width: 767px) {
- .container :global(.euiFlexGroup--responsive .query-card-output-response-fail) {
- margin: 10px 0 !important;
- }
+ &:last-child {
+ margin-right: 0;
+ }
+ }
- .table {
- :global(.euiTableRowCell) {
- background-color: initial !important;
- max-width: 100% !important;
- width: 100%;
- border-top: 1px solid var(--euiColorLightShade) !important;
+ .linksWrapper {
+ justify-content: center;
+ flex-direction: column-reverse;
}
- :global(.euiTableRow) {
- padding: 0 !important;
- border-right: 0 !important;
- background-color: var(--euiPageBackgroundColor) !important;
+ .link {
+ margin-right: 0;
+
+ &:first-child {
+ margin-top: 13px;
+ }
}
}
}
diff --git a/redisinsight/ui/src/pages/workbench/constants.ts b/redisinsight/ui/src/pages/workbench/constants.ts
index 673c0bce3d..0df68f333b 100644
--- a/redisinsight/ui/src/pages/workbench/constants.ts
+++ b/redisinsight/ui/src/pages/workbench/constants.ts
@@ -1,8 +1,5 @@
-import RSNotAvailableLightImg from 'uiSrc/assets/img/workbench/RediSearchNotAvailableLight.jpg'
-import RSNotAvailableDarkImg from 'uiSrc/assets/img/workbench/RediSearchNotAvailableDark.jpg'
import TextViewIconDark from 'uiSrc/assets/img/workbench/text_view_dark.svg'
import TextViewIconLight from 'uiSrc/assets/img/workbench/text_view_light.svg'
-import { IModuleNotLoadedContent } from './components/module-not-loaded'
export const WORKBENCH_HISTORY_WRAPPER_NAME = 'WORKBENCH'
export const WORKBENCH_HISTORY_MAX_LENGTH = 30
@@ -57,30 +54,14 @@ const PROFILE_VIEW_TYPE_OPTIONS = [
export const getProfileViewTypeOptions = () =>
[...PROFILE_VIEW_TYPE_OPTIONS]
-
export enum ModuleCommandPrefix {
RediSearch = 'FT.',
-}
-
-export const RSNotLoadedContent: IModuleNotLoadedContent = {
- output: 'RediSearch module is not loaded for this database',
- createCloudBtnText: 'Create your free Redis database with RediSearch on Redis Cloud',
- createCloudBtnHref: 'https://redis.com/try-free/?utm_source=redis&utm_medium=app&utm_campaign=redisinsight_redisearch_offer_jan',
- summaryText: 'RedisInsight supports RediSearch and allows you to:
'
- + '
- Build and execute queries
- Browse, analyse and export results
'
- + 'As a benefit you get faster turnarounds when building your application using Redis and RediSearch.',
- summaryImgDark: RSNotAvailableDarkImg,
- summaryImgLight: RSNotAvailableLightImg,
- // summaryImgPath: 'uiSrc/assets/img/workbench/RediSearchNotAvailable.jpg',
- columns: [{
- title: 'What is RediSearch?',
- text: 'RediSearch is a real-time search engine that enables you to query your Redis data. It implements a secondary index on top of Redis.
This enables advanced features, such as multi-field queries, aggregation, auto-completion and full text search capabilities.
These capabilities include exact phrase matching, fuzzy and prefix matching, numeric filtering, geo-spatial filtering, something that is neither possible or efficient with traditional Redis indexing approaches.',
- }, {
- title: 'Learn more about RediSearch module:',
- text: '
RediSearch Quick Start tutorial'
- + '
RediSearch Documentation'
- + '
RediSearch Commands'
- + '
RediSearch in Brief'
- + '
RediSearch Benchmarks (blog post)'
- }]
+ JSON = 'JSON.',
+ TimeSeries = 'TS.',
+ Graph = 'GRAPH.',
+ BF = 'BF.',
+ CF = 'CF.',
+ CMS = 'CMS.',
+ TOPK = 'TOPK.',
+ TDIGEST = 'TDIGEST.',
}
diff --git a/redisinsight/ui/src/slices/interfaces/instances.ts b/redisinsight/ui/src/slices/interfaces/instances.ts
index e9ff40ed8d..6c052b6548 100644
--- a/redisinsight/ui/src/slices/interfaces/instances.ts
+++ b/redisinsight/ui/src/slices/interfaces/instances.ts
@@ -175,6 +175,14 @@ export const REDISEARCH_MODULES: string[] = [
RedisDefaultModules.FTL,
]
+export const COMMAND_MODULES = {
+ [RedisDefaultModules.Search]: REDISEARCH_MODULES,
+ [RedisDefaultModules.ReJSON]: [RedisDefaultModules.ReJSON],
+ [RedisDefaultModules.TimeSeries]: [RedisDefaultModules.TimeSeries],
+ [RedisDefaultModules.Graph]: [RedisDefaultModules.Graph],
+ [RedisDefaultModules.Bloom]: [RedisDefaultModules.Bloom],
+}
+
const RediSearchModulesText = [...REDISEARCH_MODULES].reduce((prev, next) => ({ ...prev, [next]: 'RediSearch' }), {})
// Enums don't allow to use dynamic key
diff --git a/redisinsight/ui/src/styles/themes/dark_theme/_dark_theme.lazy.scss b/redisinsight/ui/src/styles/themes/dark_theme/_dark_theme.lazy.scss
index 2819808f43..1f1ffb713c 100644
--- a/redisinsight/ui/src/styles/themes/dark_theme/_dark_theme.lazy.scss
+++ b/redisinsight/ui/src/styles/themes/dark_theme/_dark_theme.lazy.scss
@@ -179,6 +179,7 @@
--wbRunResultsBg: #{$wbRunResultsBg};
--wbHoverIconColor: #{$wbHoverIconColor};
--wbActiveIconColor: #{$wbActiveIconColor};
+ --wbTextColor: #{$wbTextColor};
// Pub/Sub
--pubSubClientsBadge: #{$pubSubClientsBadge};
diff --git a/redisinsight/ui/src/styles/themes/dark_theme/_theme_color.scss b/redisinsight/ui/src/styles/themes/dark_theme/_theme_color.scss
index e42473ee00..f596371d27 100644
--- a/redisinsight/ui/src/styles/themes/dark_theme/_theme_color.scss
+++ b/redisinsight/ui/src/styles/themes/dark_theme/_theme_color.scss
@@ -139,6 +139,7 @@ $rsSubmitBtn: #1ae26e;
$wbRunResultsBg: #000;
$wbHoverIconColor: #ffffff;
$wbActiveIconColor: #8ba2ff;
+$wbTextColor: #dfe5ef;
// PubSub
$pubSubClientsBadge: #008000;
diff --git a/redisinsight/ui/src/styles/themes/light_theme/_light_theme.lazy.scss b/redisinsight/ui/src/styles/themes/light_theme/_light_theme.lazy.scss
index ccc938f991..5fdef0de41 100644
--- a/redisinsight/ui/src/styles/themes/light_theme/_light_theme.lazy.scss
+++ b/redisinsight/ui/src/styles/themes/light_theme/_light_theme.lazy.scss
@@ -181,6 +181,7 @@
--wbRunResultsBg: #{$wbRunResultsBg};
--wbHoverIconColor: #{$wbHoverIconColor};
--wbActiveIconColor: #{$wbActiveIconColor};
+ --wbTextColor: #{$wbTextColor};
// Pub/Sub
--pubSubClientsBadge: #{$pubSubClientsBadge};
diff --git a/redisinsight/ui/src/styles/themes/light_theme/_theme_color.scss b/redisinsight/ui/src/styles/themes/light_theme/_theme_color.scss
index 20c09ec571..670e364b21 100644
--- a/redisinsight/ui/src/styles/themes/light_theme/_theme_color.scss
+++ b/redisinsight/ui/src/styles/themes/light_theme/_theme_color.scss
@@ -136,6 +136,7 @@ $rsInputWrapperColor: #fff;
$wbRunResultsBg: #fff;
$wbHoverIconColor: #415681;
$wbActiveIconColor: #3163D8;
+$wbTextColor: #415681;
// Pub/Sub
$pubSubClientsBadge: #b5cea8;
diff --git a/redisinsight/ui/src/utils/cliHelper.tsx b/redisinsight/ui/src/utils/cliHelper.tsx
index 2f24de7954..b65f8ea2d5 100644
--- a/redisinsight/ui/src/utils/cliHelper.tsx
+++ b/redisinsight/ui/src/utils/cliHelper.tsx
@@ -1,4 +1,4 @@
-import React, { Fragment } from 'react'
+import React from 'react'
import { Dispatch, PayloadAction } from '@reduxjs/toolkit'
import parse from 'html-react-parser'
@@ -8,10 +8,13 @@ import { resetOutput, updateCliCommandHistory } from 'uiSrc/slices/cli/cli-outpu
import { BrowserStorageItem, ICommands } from 'uiSrc/constants'
import { ModuleCommandPrefix } from 'uiSrc/pages/workbench/constants'
import { SelectCommand } from 'uiSrc/constants/cliOutput'
-import { ClusterNode, RedisDefaultModules, REDISEARCH_MODULES } from 'uiSrc/slices/interfaces'
+import {
+ ClusterNode,
+ RedisDefaultModules,
+ COMMAND_MODULES,
+} from 'uiSrc/slices/interfaces'
import { AdditionalRedisModule } from 'apiSrc/modules/database/models/additional.redis.module'
-import { Nullable } from './types'
import formatToText from './transformers/cliTextFormatter'
import { getDbIndex } from './longNames'
@@ -139,18 +142,42 @@ const checkUnsupportedCommand = (unsupportedCommands: string[], commandLine: str
const checkBlockingCommand = (blockingCommands: string[], commandLine: string) =>
blockingCommands?.find((command) => commandLine?.trim().toLowerCase().startsWith(command))
+const checkCommandModule = (command: string) => {
+ switch (true) {
+ case command.startsWith(ModuleCommandPrefix.RediSearch): {
+ return RedisDefaultModules.Search
+ }
+ case command.startsWith(ModuleCommandPrefix.JSON): {
+ return RedisDefaultModules.ReJSON
+ }
+ case command.startsWith(ModuleCommandPrefix.TimeSeries): {
+ return RedisDefaultModules.TimeSeries
+ }
+ case command.startsWith(ModuleCommandPrefix.Graph): {
+ return RedisDefaultModules.Graph
+ }
+ case command.startsWith(ModuleCommandPrefix.BF):
+ case command.startsWith(ModuleCommandPrefix.CF):
+ case command.startsWith(ModuleCommandPrefix.CMS):
+ case command.startsWith(ModuleCommandPrefix.TDIGEST):
+ case command.startsWith(ModuleCommandPrefix.TOPK): {
+ return RedisDefaultModules.Bloom
+ }
+ default: {
+ return null
+ }
+ }
+}
+
const checkUnsupportedModuleCommand = (loadedModules: AdditionalRedisModule[], commandLine: string) => {
const command = commandLine?.trim().toUpperCase()
- let commandModule: Nullable
= null
- if (command.startsWith(ModuleCommandPrefix.RediSearch)) {
- commandModule = RedisDefaultModules.Search
+ const commandModule = checkCommandModule(command)
+ if (!commandModule) {
+ return null
}
-
- const isModuleLoaded = loadedModules?.some(({ name }) => name === commandModule)
- // Redisearch has 4 names, need check all
- || loadedModules?.some(({ name }) =>
- REDISEARCH_MODULES.some((search) => name === search))
+ const isModuleLoaded = loadedModules?.some(({ name }) =>
+ COMMAND_MODULES[commandModule].some((module) => name === module))
if (isModuleLoaded) {
return null
@@ -195,6 +222,7 @@ export {
cliCommandWrapper,
clearOutput,
updateCliHistoryStorage,
+ checkCommandModule,
checkUnsupportedCommand,
checkBlockingCommand,
checkUnsupportedModuleCommand,
diff --git a/redisinsight/ui/src/utils/tests/cliHelper.spec.ts b/redisinsight/ui/src/utils/tests/cliHelper.spec.ts
index 515fe6a891..961c7f185e 100644
--- a/redisinsight/ui/src/utils/tests/cliHelper.spec.ts
+++ b/redisinsight/ui/src/utils/tests/cliHelper.spec.ts
@@ -3,11 +3,16 @@ import {
getCommandNameFromQuery,
cliParseCommandsGroupResult,
CliPrefix,
- wbSummaryCommand
+ wbSummaryCommand,
+ checkUnsupportedModuleCommand,
+ checkCommandModule,
+ checkUnsupportedCommand,
+ checkBlockingCommand,
} from 'uiSrc/utils'
import { MOCK_COMMANDS_SPEC } from 'uiSrc/constants'
import { render, screen } from 'uiSrc/utils/test-utils'
import { CommandExecutionStatus } from 'uiSrc/slices/interfaces/cli'
+import { RedisDefaultModules } from 'uiSrc/slices/interfaces'
const getDbIndexFromSelectQueryTests = [
{ input: 'select 0', expected: 0 },
@@ -21,6 +26,7 @@ const getDbIndexFromSelectQueryTests = [
{ input: 'info', expected: new Error('Invalid command') },
{ input: 'select "1 1231"', expected: new Error('Parsing error') },
{ input: 'select abc', expected: new Error('Parsing error') },
+ { input: 'select ', expected: new Error('Parsing error') },
]
describe('getDbIndexFromSelectQuery', () => {
@@ -49,6 +55,41 @@ const getCommandNameFromQueryTests = [
input: [`${' '.repeat(20)} CLIENT ${' '.repeat(100)} KILL`, MOCK_COMMANDS_SPEC, 500],
expected: 'CLIENT KILL'
},
+ { input: [1], expected: undefined },
+]
+
+const checkUnsupportedModuleCommandTests = [
+ { input: [[], 'FT.foo bar'], expected: RedisDefaultModules.Search },
+ { input: [[{ name: RedisDefaultModules.Search }], 'foo bar'], expected: null },
+ { input: [[{ name: RedisDefaultModules.Search }], 'ft.foo bar'], expected: null },
+ { input: [[{ name: RedisDefaultModules.SearchLight }], 'ft.foo bar'], expected: null },
+ { input: [[{ name: RedisDefaultModules.FT }], ' FT.foo bar'], expected: null },
+ { input: [[{ name: RedisDefaultModules.FTL }], ' ft.foo bar'], expected: null },
+]
+
+const checkCommandModuleTests = [
+ { input: 'FT.foo bar', expected: RedisDefaultModules.Search },
+ { input: 'JSON.foo bar', expected: RedisDefaultModules.ReJSON },
+ { input: 'TS.foo bar', expected: RedisDefaultModules.TimeSeries },
+ { input: 'GRAPH.foo bar', expected: RedisDefaultModules.Graph },
+ { input: 'BF.foo bar', expected: RedisDefaultModules.Bloom },
+ { input: 'CF.foo bar', expected: RedisDefaultModules.Bloom },
+ { input: 'CMS.foo bar', expected: RedisDefaultModules.Bloom },
+ { input: 'TDIGEST.foo bar', expected: RedisDefaultModules.Bloom },
+ { input: 'TOPK.foo bar', expected: RedisDefaultModules.Bloom },
+ { input: 'FOO.foo bar', expected: null },
+]
+
+const checkUnsupportedCommandTests = [
+ { input: [['FT'], 'FT.foo bar'], expected: 'FT' },
+ { input: [['FT'], ' ft.foo bar '], expected: 'FT' },
+ { input: [['FOO', 'BAR'], 'FT.foo bar'], expected: undefined },
+]
+
+const checkBlockingCommandTests = [
+ { input: [['ft'], 'FT.foo bar'], expected: 'ft' },
+ { input: [['ft'], ' ft.foo bar '], expected: 'ft' },
+ { input: [['foo', 'bar'], 'FT.foo bar'], expected: undefined },
]
describe('getCommandNameFromQuery', () => {
@@ -99,3 +140,30 @@ describe('wbSummaryCommand', () => {
expect(container).toHaveTextContent(expected)
})
})
+
+describe('checkUnsupportedModuleCommand', () => {
+ test.each(checkUnsupportedModuleCommandTests)('%j', ({ input, expected }) => {
+ // @ts-ignore
+ expect(checkUnsupportedModuleCommand(...input)).toEqual(expected)
+ })
+})
+
+describe('checkCommandModule', () => {
+ test.each(checkCommandModuleTests)('%j', ({ input, expected }) => {
+ expect(checkCommandModule(input)).toEqual(expected)
+ })
+})
+
+describe('checkUnsupportedCommand', () => {
+ test.each(checkUnsupportedCommandTests)('%j', ({ input, expected }) => {
+ // @ts-ignore
+ expect(checkUnsupportedCommand(...input)).toEqual(expected)
+ })
+})
+
+describe('checkBlockingCommand', () => {
+ test.each(checkBlockingCommandTests)('%j', ({ input, expected }) => {
+ // @ts-ignore
+ expect(checkBlockingCommand(...input)).toEqual(expected)
+ })
+})
diff --git a/tests/e2e/pageObjects/workbench-page.ts b/tests/e2e/pageObjects/workbench-page.ts
index bfc78ef5fe..f0e5675b10 100644
--- a/tests/e2e/pageObjects/workbench-page.ts
+++ b/tests/e2e/pageObjects/workbench-page.ts
@@ -29,6 +29,7 @@ export class WorkbenchPage {
//*The following categories are ordered alphabetically (Alerts, Buttons, Checkboxes, etc.).
//-------------------------------------------------------------------------------------------
//BUTTONS
+ welcomePageTitle = Selector('[data-testid=welcome-page-title]');
customTutorials = Selector('[data-testid=accordion-button-custom-tutorials]');
tutorialOpenUploadButton = Selector('[data-testid=open-upload-tutorial-btn]');
tutorialLinkField = Selector('[data-testid=tutorial-link-field]');
@@ -190,6 +191,7 @@ export class WorkbenchPage {
*/
async sendCommandInWorkbench(command: string, speed = 1, paste = true): Promise {
await t
+ .click(this.queryInput)
.typeText(this.queryInput, command, { replace: true, speed, paste })
.click(this.submitCommandButton);
}
diff --git a/tests/e2e/test-data/upload-tutorials/sample.zip b/tests/e2e/test-data/upload-tutorials/sample.zip
deleted file mode 100644
index 4ec02b360b..0000000000
Binary files a/tests/e2e/test-data/upload-tutorials/sample.zip and /dev/null differ
diff --git a/tests/e2e/test-data/upload-tutorials/testTutorials.zip b/tests/e2e/test-data/upload-tutorials/testTutorials.zip
new file mode 100644
index 0000000000..60192fedb8
Binary files /dev/null and b/tests/e2e/test-data/upload-tutorials/testTutorials.zip differ
diff --git a/tests/e2e/tests/critical-path/browser/bulk-delete.e2e.ts b/tests/e2e/tests/critical-path/browser/bulk-delete.e2e.ts
index 59ad57e6f1..922d7c5761 100644
--- a/tests/e2e/tests/critical-path/browser/bulk-delete.e2e.ts
+++ b/tests/e2e/tests/critical-path/browser/bulk-delete.e2e.ts
@@ -4,7 +4,6 @@ import { BrowserPage, BulkActionsPage, MyRedisDatabasePage } from '../../../page
import { commonUrl, ossStandaloneRedisearch } from '../../../helpers/conf';
import { deleteStandaloneDatabaseApi } from '../../../helpers/api/api-database';
import { Common } from '../../../helpers/common';
-import { addHashKeyApi, addSetKeyApi } from '../../../helpers/api/api-keys';
import { deleteAllKeysFromDB, populateDBWithHashes } from '../../../helpers/keys';
const browserPage = new BrowserPage();
@@ -13,8 +12,6 @@ const common = new Common();
const myRedisDatabasePage = new MyRedisDatabasePage();
const keyNames = [common.generateWord(20), common.generateWord(20)];
-const hashKeyParameters = { keyName: keyNames[0], fields: [{ field: common.generateWord(20), value: common.generateWord(20) }] };
-const setKeyParameters = { keyName: keyNames[1], members: [common.generateWord(20)] };
const dbParameters = { host: ossStandaloneRedisearch.host, port: ossStandaloneRedisearch.port };
const keyToAddParameters = { keysCount: 10000, keyNameStartWith: 'hashKey'};
const keyToAddParameters2 = { keysCount: 500000, keyNameStartWith: 'hashKey'};
@@ -24,8 +21,8 @@ fixture `Bulk Delete`
.page(commonUrl)
.beforeEach(async() => {
await acceptLicenseTermsAndAddDatabaseApi(ossStandaloneRedisearch, ossStandaloneRedisearch.databaseName);
- await addHashKeyApi(hashKeyParameters, ossStandaloneRedisearch);
- await addSetKeyApi(setKeyParameters, ossStandaloneRedisearch);
+ await browserPage.addHashKey(keyNames[0], '100000', common.generateWord(20), common.generateWord(20));
+ await browserPage.addSetKey(keyNames[1], '100000', common.generateWord(20));
})
.afterEach(async() => {
// Clear and delete database
@@ -134,7 +131,7 @@ test('Verify that when bulk deletion is completed, status Action completed is di
test
.before(async() => {
await acceptLicenseTermsAndAddDatabaseApi(ossStandaloneRedisearch, ossStandaloneRedisearch.databaseName);
- await addSetKeyApi(setKeyParameters, ossStandaloneRedisearch);
+ await browserPage.addSetKey(keyNames[1], '100000', common.generateWord(20));
// Add 10000 Hash keys
await populateDBWithHashes(dbParameters.host, dbParameters.port, keyToAddParameters);
// Filter by Hash keys
diff --git a/tests/e2e/tests/critical-path/browser/search-capabilities.e2e.ts b/tests/e2e/tests/critical-path/browser/search-capabilities.e2e.ts
index 46b5113ce7..e22e28021b 100644
--- a/tests/e2e/tests/critical-path/browser/search-capabilities.e2e.ts
+++ b/tests/e2e/tests/critical-path/browser/search-capabilities.e2e.ts
@@ -264,6 +264,7 @@ test
await myRedisDatabasePage.clickOnDBByName(simpleDbName); // click standalone database
await cliPage.sendCommandInCli(`FT.DROPINDEX ${indexNameSimpleDb}`);
await t.click(browserPage.patternModeBtn);
+ await t.click(browserPage.browserViewButton);
await browserPage.deleteKeysByNames(keyNames);
//delete database
diff --git a/tests/e2e/tests/critical-path/workbench/default-scripts-area.e2e.ts b/tests/e2e/tests/critical-path/workbench/default-scripts-area.e2e.ts
index 60b230fe12..d2269a3041 100644
--- a/tests/e2e/tests/critical-path/workbench/default-scripts-area.e2e.ts
+++ b/tests/e2e/tests/critical-path/workbench/default-scripts-area.e2e.ts
@@ -114,7 +114,8 @@ test('Verify that user can edit and run automatically added "Aggregate" script i
await t.expect(workbenchPage.queryTableResult.textContent).contains(aggregationResultField, 'The aggregation field name is not in the Search result');
await t.expect(workbenchPage.queryTableResult.textContent).contains('100', 'The aggregation max value is in not the Search result');
});
-test('Verify that when the “Manual” option clicked, user can see the Editor is automatically prepopulated with the information', async t => {
+// Outdated after https://redislabs.atlassian.net/browse/RI-4279
+test.skip('Verify that when the “Manual” option clicked, user can see the Editor is automatically prepopulated with the information', async t => {
const information = [
'// Workbench is the advanced Redis command-line interface that allows to send commands to Redis, read and visualize the replies sent by the server.',
'// Enter multiple commands at different rows to run them at once.',
diff --git a/tests/e2e/tests/critical-path/workbench/scripting-area.e2e.ts b/tests/e2e/tests/critical-path/workbench/scripting-area.e2e.ts
index a8a71829c5..88fa4e0333 100644
--- a/tests/e2e/tests/critical-path/workbench/scripting-area.e2e.ts
+++ b/tests/e2e/tests/critical-path/workbench/scripting-area.e2e.ts
@@ -30,7 +30,7 @@ fixture `Scripting area at Workbench`
// Update after resolving https://redislabs.atlassian.net/browse/RI-3299
test('Verify that user can resize scripting area in Workbench', async t => {
const commandForSend = 'info';
- const offsetY = 100;
+ const offsetY = 130;
await workbenchPage.sendCommandInWorkbench(commandForSend);
// Verify that user can run any script from CLI in Workbench and see the results
@@ -41,9 +41,9 @@ test('Verify that user can resize scripting area in Workbench', async t => {
const inputHeightStart = await workbenchPage.queryInput.clientHeight;
await t.hover(workbenchPage.resizeButtonForScriptingAndResults);
- await t.drag(workbenchPage.resizeButtonForScriptingAndResults, 0, offsetY, { speed: 0.4 });
+ await t.drag(workbenchPage.resizeButtonForScriptingAndResults, 0, offsetY, { speed: 0.1 });
// Verify that user can resize scripting area
- const inputHeightEnd = inputHeightStart + 20;
+ const inputHeightEnd = inputHeightStart + 15;
await t.expect(await workbenchPage.queryInput.clientHeight).gt(inputHeightEnd, 'Scripting area after resize has incorrect size');
});
test('Verify that user when he have more than 10 results can request to view more results in Workbench', async t => {
diff --git a/tests/e2e/tests/regression/workbench/command-results.e2e.ts b/tests/e2e/tests/regression/workbench/command-results.e2e.ts
index ae807aeb0d..a68bba1d54 100644
--- a/tests/e2e/tests/regression/workbench/command-results.e2e.ts
+++ b/tests/e2e/tests/regression/workbench/command-results.e2e.ts
@@ -2,7 +2,7 @@ import { acceptLicenseTermsAndAddDatabaseApi } from '../../../helpers/database';
import { WorkbenchPage, MyRedisDatabasePage } from '../../../pageObjects';
import {
commonUrl,
- ossStandaloneRedisearch
+ ossStandaloneRedisearch, ossStandaloneV5Config
} from '../../../helpers/conf';
import { env, rte } from '../../../helpers/constants';
import { deleteStandaloneDatabaseApi } from '../../../helpers/api/api-database';
@@ -134,3 +134,21 @@ test.before(async t => {
// verify table view row count match with text view after client list command
await workBenchActions.verifyClientListTableViewRowCount();
});
+// https://redislabs.atlassian.net/browse/RI-4230
+test.before(async t => {
+ await acceptLicenseTermsAndAddDatabaseApi(ossStandaloneV5Config, ossStandaloneV5Config.databaseName);
+ await t.click(myRedisDatabasePage.workbenchButton);
+}).after(async() => {
+ await deleteStandaloneDatabaseApi(ossStandaloneV5Config);
+})('Verify that user can see options on what can be done to work with capabilities in Workbench for docker', async t => {
+ const commandJSON = 'JSON.ARRAPPEND key value';
+ const commandFT = 'FT.LIST';
+ await workbenchPage.sendCommandInWorkbench(commandJSON);
+ // Verify change screens when capability not available - 'Search'
+ await t.expect(workbenchPage.welcomePageTitle.withText('Looks like RedisJSON is not available').visible)
+ .ok('Missing RedisJSON title is not visible');
+ await workbenchPage.sendCommandInWorkbench(commandFT);
+ // Verify change screens when capability not available - 'JSON'
+ await t.expect(workbenchPage.welcomePageTitle.withText('Looks like RediSearch is not available').visible)
+ .ok('Missing RedisJSON title is not visible');
+});
diff --git a/tests/e2e/tests/regression/workbench/import-tutorials.e2e.ts b/tests/e2e/tests/regression/workbench/import-tutorials.e2e.ts
index 88a7ce8840..89a171c057 100644
--- a/tests/e2e/tests/regression/workbench/import-tutorials.e2e.ts
+++ b/tests/e2e/tests/regression/workbench/import-tutorials.e2e.ts
@@ -6,14 +6,13 @@ import {
import { MyRedisDatabasePage, WorkbenchPage } from '../../../pageObjects';
import { commonUrl, ossStandaloneConfig } from '../../../helpers/conf';
import { deleteStandaloneDatabaseApi } from '../../../helpers/api/api-database';
-import {Common} from '../../../helpers/common';
const myRedisDatabasePage = new MyRedisDatabasePage();
const workbenchPage = new WorkbenchPage();
-const common = new Common();
-const filePath = path.join('..', '..', '..', 'test-data', 'upload-tutorials', 'sample.zip');
-const tutorialName = `tutorialName-${common.generateWord(10)}`;
-const link = 'https://drive.google.com/uc?export=download&id=1mlyDKWLu12L02FblOPh15EwG2Vy_FhJ7';
+const filePath = path.join('..', '..', '..', 'test-data', 'upload-tutorials', 'testTutorials.zip');
+const tutorialName = 'testTutorials';
+const tutorialName2 = 'tutorialTestByLink';
+const link = 'https://drive.google.com/uc?id=1puRUoT8HmyZCekkeWNxBzXe_48TzXcJc&export=download';
let folder1 = 'folder-1';
let folder2 = 'folder-2';
let internalLinkName1 = 'probably-1';
@@ -29,7 +28,7 @@ fixture `Upload custom tutorials`
.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-4186, https://redislabs.atlassian.net/browse/RI-4198, https://redislabs.atlassian.net/browse/RI-4302
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
folder1 = 'folder-1';
@@ -39,8 +38,6 @@ test('Verify that user can upload tutorial with local zip file without manifest.
// 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);
- // Verify that User can enter a tutorial name
- await t.typeText(workbenchPage.tutorialNameField, tutorialName);
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]);
@@ -50,10 +47,12 @@ test('Verify that user can upload tutorial with local zip file without manifest.
await t.click(workbenchPage.tutorialAccordionButton.withText(tutorialName));
await t.expect((await workbenchPage.getAccordionButtonWithName(folder1)).visible).ok(`${folder1} is not visible`);
await t.expect((await workbenchPage.getAccordionButtonWithName(folder2)).visible).ok(`${folder2} is not visible`);
+ await t.click(await workbenchPage.getAccordionButtonWithName(folder1));
await t.expect((await workbenchPage.getInternalLinkWithManifest(internalLinkName1)).visible)
.ok(`${internalLinkName1} is not visible`);
+ await t.click(await workbenchPage.getAccordionButtonWithName(folder2));
await t.expect((await workbenchPage.getInternalLinkWithManifest(internalLinkName2)).visible)
- .ok(`${internalLinkName2} is not visible`);
+ .ok(`${internalLinkName1} 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');
@@ -66,21 +65,23 @@ test('Verify that user can upload tutorial with local zip file without manifest.
await t.expect((workbenchPage.tutorialAccordionButton.withText(tutorialName).exists))
.notOk(`${tutorialName} tutorial is not uploaded`);
});
-// https://redislabs.atlassian.net/browse/RI-4186, https://redislabs.atlassian.net/browse/RI-4213
+// https://redislabs.atlassian.net/browse/RI-4186, https://redislabs.atlassian.net/browse/RI-4213, https://redislabs.atlassian.net/browse/RI-4302
test('Verify that user can upload tutorial with URL with manifest.json', async t => {
- internalLinkName1 = 'working_probably';
+ const labelFromManifest = 'LabelFromManifest';
+ internalLinkName1 = 'manifest-id';
await t.click(workbenchPage.tutorialOpenUploadButton);
- await t.typeText(workbenchPage.tutorialNameField, tutorialName);
// Verify that user can upload tutorials using a URL
await t.typeText(workbenchPage.tutorialLinkField, link);
await t.click(workbenchPage.tutorialSubmitButton);
- await t.expect(workbenchPage.tutorialAccordionButton.withText(tutorialName).with({ timeout: 20000 }).visible)
- .ok(`${tutorialName} tutorial is not uploaded`);
- await t.click(workbenchPage.tutorialAccordionButton.withText(tutorialName));
+ await t.expect(workbenchPage.tutorialAccordionButton.withText(tutorialName2).with({ timeout: 20000 }).visible)
+ .ok(`${tutorialName2} tutorial is not uploaded`);
+ await t.click(workbenchPage.tutorialAccordionButton.withText(tutorialName2));
// Verify that User can see the same structure in the tutorial uploaded as described in the .json manifest
await t.expect((await workbenchPage.getInternalLinkWithoutManifest(internalLinkName1)).visible)
- .ok(`${internalLinkName1} folder is not visible`);
+ .ok(`${internalLinkName1} folder specified in manifest is not visible`);
+ await t.expect(await (await workbenchPage.getInternalLinkWithoutManifest(internalLinkName1)).textContent)
+ .eql(labelFromManifest, `${labelFromManifest} tutorial specified in manifest is not visible`);
await t.click((await workbenchPage.getInternalLinkWithoutManifest(internalLinkName1)));
await t.expect(workbenchPage.scrolledEnablementArea.visible).ok('enablement area is not visible after clicked');
await t.click(workbenchPage.closeEnablementPage);
@@ -89,6 +90,6 @@ test('Verify that user can upload tutorial with URL with manifest.json', async t
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`);
+ await t.expect((workbenchPage.tutorialAccordionButton.withText(tutorialName2).exists))
+ .notOk(`${tutorialName2} tutorial is not uploaded`);
});