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
11 changes: 11 additions & 0 deletions tests/e2e/common-actions/browser-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,15 @@ export class BrowserActions {
}
}
}

/**
* Verify toolip contains text
* @param expectedText Expected link that is compared with actual
* @param contains Should this tooltip contains or not contains text
*/
async verifyTooltipContainsText(expectedText: string, contains: boolean): Promise<void> {
contains
? await t.expect(browserPage.tooltip.textContent).contains(expectedText, `"${expectedText}" Text is incorrect in tooltip`)
: await t.expect(browserPage.tooltip.textContent).notContains(expectedText, `Tooltip still contains text "${expectedText}"`);
}
}
32 changes: 21 additions & 11 deletions tests/e2e/helpers/common.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { ClientFunction, RequestMock, t } from 'testcafe';
import { Chance } from 'chance';
import {apiUrl, commonUrl} from './conf';
import { apiUrl, commonUrl } from './conf';

const settingsApiUrl = `${commonUrl}/api/settings`;
const chance = new Chance();
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // lgtm[js/disabling-certificate-validation]

const settingsApiUrl = `${commonUrl}/api/settings`;
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // lgtm[js/disabling-certificate-validation]
const mockedSettingsResponse = {
agreements: {
version: '0',
eula: false,
analytics: false
}};
}
};

export class Common {
mock = RequestMock()
Expand All @@ -27,7 +28,7 @@ export class Common {
* @param length The amount of array elements
*/
createArrayWithKeys(length: number): string[] {
return Array.from({length}, (_, i) => `key${i}`);
return Array.from({ length }, (_, i) => `key${i}`);
}

/**
Expand All @@ -36,7 +37,7 @@ export class Common {
*/
async createArrayWithKeyValue(length: number): Promise<string[]> {
const arr: string[] = [];
for(let i = 1; i <= length * 2; i++) {
for (let i = 1; i <= length * 2; i++) {
arr[i] = `${chance.word({ length: 10 })}-key${i}`;
arr[i + 1] = `${chance.word({ length: 10 })}-value${i}`;
i++;
Expand All @@ -50,7 +51,7 @@ export class Common {
*/
async createArrayWithKeyValueAndDelimiter(length: number): Promise<string[]> {
const keyNameArray: string[] = [];
for(let i = 1; i <= length; i++) {
for (let i = 1; i <= length; i++) {
const key = `"key${i}:test${i}"`;
const value = `"value${this.generateSentence(i * 2)}"`;
keyNameArray.push(key, value);
Expand All @@ -64,7 +65,7 @@ export class Common {
*/
async createArrayWithKeyAndDelimiter(length: number): Promise<string[]> {
const keyNameArray: string[] = [];
for(let i = 1; i <= length; i++) {
for (let i = 1; i <= length; i++) {
const key = `"key${i}:test${i}"`;
keyNameArray.push(key);
}
Expand All @@ -77,7 +78,7 @@ export class Common {
*/
async createArrayWithKeyValueForOSSCluster(length: number): Promise<string[]> {
const arr: string[] = [];
for(let i = 1; i <= length * 2; i++) {
for (let i = 1; i <= length * 2; i++) {
arr[i] = `{user1}:${chance.word({ length: 10 })}-key${i}`;
arr[i + 1] = `${chance.word({ length: 10 })}-value${i}`;
i++;
Expand All @@ -92,7 +93,7 @@ export class Common {
*/
async createArrayWithKeyValueAndKeyname(length: number, keyName: string): Promise<string[]> {
const keyNameArray: string[] = [];
for(let i = 1; i <= length; i++) {
for (let i = 1; i <= length; i++) {
const key = `${keyName}${i}`;
const value = `value${i}`;
keyNameArray.push(key, value);
Expand All @@ -114,7 +115,7 @@ export class Common {
*/
async createArray(length: number): Promise<string[]> {
const arr: string[] = [];
for(let i = 1; i <= length; i++) {
for (let i = 1; i <= length; i++) {
arr[i] = `${i}`;
}
return arr;
Expand Down Expand Up @@ -166,4 +167,13 @@ export class Common {
const getPageUrl = ClientFunction(() => window.location.href);
await t.expect(getPageUrl()).eql(expectedUrl, 'Opened URL is not correct');
}

/**
* Check opened URL contains text
* @param expectedText Expected link that is compared with actual
*/
async checkURLContainsText(expectedText: string): Promise<void> {
const getPageUrl = ClientFunction(() => window.location.href);
await t.expect(getPageUrl()).contains(expectedText, `Opened URL not contains text ${expectedText}`);
}
}
8 changes: 8 additions & 0 deletions tests/e2e/helpers/conf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,11 @@ export const ossStandaloneNoPermissionsConfig = {
databaseUsername: process.env.OSS_STANDALONE_USERNAME || 'noperm',
databasePassword: process.env.OSS_STANDALONE_PASSWORD
};

export const ossStandaloneForSSH = {
host: process.env.OSS_STANDALONE_HOST || '172.33.100.10',
port: process.env.OSS_STANDALONE_PORT || '6379',
databaseName: `${process.env.OSS_STANDALONE_DATABASE_NAME || 'oss-standalone-for-ssh'}-${uniqueId}`,
databaseUsername: process.env.OSS_STANDALONE_USERNAME,
databasePassword: process.env.OSS_STANDALONE_PASSWORD
};
79 changes: 76 additions & 3 deletions tests/e2e/pageObjects/add-redis-database-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class AddRedisDatabasePage {
//*Target any element/component via data-id, if possible!
//*The following categories are ordered alphabetically (Alerts, Buttons, Checkboxes, etc.).
//-------------------------------------------------------------------------------------------
//BUTTONS
// BUTTONS
addDatabaseButton = Selector('[data-testid=add-redis-database]');
addRedisDatabaseButton = Selector('[data-testid=btn-submit]');
addDatabaseManually = Selector('[data-testid=add-manual]');
Expand All @@ -29,7 +29,7 @@ export class AddRedisDatabasePage {
cloneSentinelDatabaseNavigation = Selector('[data-testid=database-nav-group-clone]');
cancelButton = Selector('[data-testid=btn-cancel]');
showPasswordBtn = Selector('[aria-label^="Show password"]');
//TEXT INPUTS (also referred to as 'Text fields')
// TEXT INPUTS (also referred to as 'Text fields')
hostInput = Selector('[data-testid=host]');
portInput = Selector('[data-testid=port]');
databaseAliasInput = Selector('[data-testid=name]');
Expand All @@ -45,13 +45,24 @@ export class AddRedisDatabasePage {
masterGroupPassword = Selector('[data-testid=sentinel-master-password]');
connectionType = Selector('[data-testid=connection-type]');
sentinelForm = Selector('[data-testid=form]');
//Links
sshHostInput = Selector('[data-testid=sshHost]');
sshPortInput = Selector('[data-testid=sshPort]');
sshUsernameInput = Selector('[data-testid=sshUsername]');
sshPasswordInput = Selector('[data-testid=sshPassword]');
sshPrivateKeyInput = Selector('[data-testid=sshPrivateKey]');
sshPassphraseInput = Selector('[data-testid=sshPassphrase]');
// Links
buildFromSource = Selector('a').withExactText('Build from source');
buildFromDocker = Selector('a').withExactText('Docker');
buildFromHomebrew = Selector('a').withExactText('Homebrew');
// DROPDOWNS
caCertField = Selector('[data-testid=select-ca-cert]', {timeout: 500});
clientCertField = Selector('[data-testid=select-cert]', {timeout: 500});
// CHECKBOXES
useSSHCheckbox = Selector('[data-testid=use-ssh]~div', {timeout: 500});
// RADIO BUTTONS
sshPasswordRadioBtn = Selector('#password~div', {timeout: 500});
sshPrivateKeyRadioBtn = Selector('#privateKey~div', {timeout: 500});

/**
* Adding a new redis database
Expand Down Expand Up @@ -99,6 +110,50 @@ export class AddRedisDatabasePage {
// Click for saving
await t.click(this.addRedisDatabaseButton);
}

/**
* Adding a new standalone database with SSH
* @param databaseParameters the parameters of the database
* @param sshParameters the parameters of ssh
*/
async addStandaloneSSHDatabase(databaseParameters: AddNewDatabaseParameters, sshParameters: SSHParameters): Promise<void> {
await t
.click(this.addDatabaseButton)
.click(this.addDatabaseManually);
await t
.typeText(this.hostInput, databaseParameters.host, { replace: true, paste: true })
.typeText(this.portInput, databaseParameters.port, { replace: true, paste: true })
.typeText(this.databaseAliasInput, databaseParameters.databaseName!, { replace: true, paste: true });
if (!!databaseParameters.databaseUsername) {
await t.typeText(this.usernameInput, databaseParameters.databaseUsername, { replace: true, paste: true });
}
if (!!databaseParameters.databasePassword) {
await t.typeText(this.passwordInput, databaseParameters.databasePassword, { replace: true, paste: true });
}
// Select SSH Tunnel checkbox
await t.click(this.useSSHCheckbox);
// Enter SSH fields
await t
.typeText(this.sshHostInput, sshParameters.sshHost, { replace: true, paste: true })
.typeText(this.sshPortInput, sshParameters.sshPort, { replace: true, paste: true })
.typeText(this.sshUsernameInput, sshParameters.sshUsername, { replace: true, paste: true });
if (!!sshParameters.sshPassword) {
await t.typeText(this.sshPasswordInput, sshParameters.sshPassword, { replace: true, paste: true });
}
if (!!sshParameters.sshPrivateKey) {
await t
.click(this.sshPrivateKeyRadioBtn)
.typeText(this.sshPrivateKeyInput, sshParameters.sshPrivateKey, { replace: true, paste: true });
}
if (!!sshParameters.sshPassphrase) {
await t
.click(this.sshPrivateKeyRadioBtn)
.typeText(this.sshPrivateKeyInput, sshParameters.sshPrivateKey!, { replace: true, paste: true })
.typeText(this.sshPassphraseInput, sshParameters.sshPassphrase, { replace: true, paste: true });
}
// Click for saving
await t.click(this.addRedisDatabaseButton);
}

/**
* Auto-discover Master Groups from Sentinel
Expand Down Expand Up @@ -240,3 +295,21 @@ export type ClusterNodes = {
host: string,
port: string
};

/**
* SSH parameters
* @param sshHost The hostname of ssh
* @param sshPort The port of ssh
* @param sshUsername The username of ssh
* @param sshPassword The password of ssh
* @param sshPrivateKey The private key of ssh
* @param sshPassphrase The passphrase of ssh
*/
export type SSHParameters = {
sshHost: string,
sshPort: string,
sshUsername: string,
sshPassword?: string,
sshPrivateKey?: string,
sshPassphrase?: string
};
32 changes: 31 additions & 1 deletion tests/e2e/rte.docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
version: "3.4"

services:
# ssh
ssh:
image: lscr.io/linuxserver/openssh-server:latest
environment:
- PASSWORD_ACCESS=true
- USER_PASSWORD=pass
- USER_NAME=u
- DOCKER_MODS=linuxserver/mods:openssh-server-ssh-tunnel
- PUBLIC_KEY_DIR=/keys/pub
volumes:
- ./rte/ssh/keys:/keys
ports:
- 2222:2222
networks:
default:
ipv4_address: 172.31.100.245
ssh:
ipv4_address: 172.33.100.245

# oss standalone
oss-standalone:
image: redislabs/redismod
Expand All @@ -19,7 +38,11 @@ services:
image: redis:5
ports:
- 8101:6379

networks:
default:
ipv4_address: 172.31.100.10
ssh:
ipv4_address: 172.33.100.10
# oss standalone redisearch
oss-standalone-redisearch:
image: redislabs/redismod
Expand Down Expand Up @@ -93,3 +116,10 @@ networks:
config:
- subnet: 172.31.100.0/24
gateway: 172.31.100.1
ssh:
name: "e2e-ssh-network"
ipam:
driver: default
config:
- subnet: 172.33.100.0/24
gateway: 172.33.100.1
1 change: 1 addition & 0 deletions tests/e2e/rte/ssh/keys/pub/test.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPXS0xkxY7o+MUNBJJnf6fKh6AFFpzB0YIfifHSSseXw
1 change: 1 addition & 0 deletions tests/e2e/rte/ssh/keys/pub/testp.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEs/ewkUXl0+uDr7hxSM2vURqdRNFHm7+x05azzW/Yzu
7 changes: 7 additions & 0 deletions tests/e2e/rte/ssh/keys/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACD10tMZMWO6PjFDQSSZ3+nyoegBRacwdGCH4nx0krHl8AAAAKBv1saEb9bG
hAAAAAtzc2gtZWQyNTUxOQAAACD10tMZMWO6PjFDQSSZ3+nyoegBRacwdGCH4nx0krHl8A
AAAEDyew1DnmWamAr0OrUM87FauJfFfea+pi8ctpKNnurNi/XS0xkxY7o+MUNBJJnf6fKh
6AFFpzB0YIfifHSSseXwAAAAG3pvem9Aem96by1IUC1Qcm9Cb29rLTQ1MC1HNwEC
-----END OPENSSH PRIVATE KEY-----
8 changes: 8 additions & 0 deletions tests/e2e/rte/ssh/keys/testp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBPcEHCGN
DrMHhpQnPwc0XwAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIEs/ewkUXl0+uDr7
hxSM2vURqdRNFHm7+x05azzW/YzuAAAAoEhNzctHXM6YBV0z4zzvdniQ5cLwsv8TfMZp2G
WUhZU05yugvKlRu1pml5q3XGSP5wYCF4vvi4BE563PMDKZWAqFFGtiTotEn+XuD/eP+P8H
xdf91tV5kE+1yvVwxUNMcijHY0uYopnG2NN3bdjOH/4YmW0WLyDu10EoMZKVnrP0qBbOrR
xKIy5lqa39SrAnUnGSoTEJsEWGLiIS2rBhkVc=
-----END OPENSSH PRIVATE KEY-----
18 changes: 18 additions & 0 deletions tests/e2e/test-data/ssh/sshPrivateKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const sshPrivateKey = `
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACD10tMZMWO6PjFDQSSZ3+nyoegBRacwdGCH4nx0krHl8AAAAKBv1saEb9bG
hAAAAAtzc2gtZWQyNTUxOQAAACD10tMZMWO6PjFDQSSZ3+nyoegBRacwdGCH4nx0krHl8A
AAAEDyew1DnmWamAr0OrUM87FauJfFfea+pi8ctpKNnurNi/XS0xkxY7o+MUNBJJnf6fKh
6AFFpzB0YIfifHSSseXwAAAAG3pvem9Aem96by1IUC1Qcm9Cb29rLTQ1MC1HNwEC
-----END OPENSSH PRIVATE KEY-----`;

export const sshPrivateKeyWithPasscode = `
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBPcEHCGN
DrMHhpQnPwc0XwAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIEs/ewkUXl0+uDr7
hxSM2vURqdRNFHm7+x05azzW/YzuAAAAoEhNzctHXM6YBV0z4zzvdniQ5cLwsv8TfMZp2G
WUhZU05yugvKlRu1pml5q3XGSP5wYCF4vvi4BE563PMDKZWAqFFGtiTotEn+XuD/eP+P8H
xdf91tV5kE+1yvVwxUNMcijHY0uYopnG2NN3bdjOH/4YmW0WLyDu10EoMZKVnrP0qBbOrR
xKIy5lqa39SrAnUnGSoTEJsEWGLiIS2rBhkVc=
-----END OPENSSH PRIVATE KEY-----`;
Loading