Skip to content

Commit 9330dea

Browse files
committed
Merge remote-tracking branch 'origin/feature/RIVS-31_Logical_databases' into feature/RIVS-299_Add_feature_from_RI
2 parents 5b2b3f3 + 31c9472 commit 9330dea

File tree

36 files changed

+711
-102
lines changed

36 files changed

+711
-102
lines changed

.env

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ RI_STDOUT_LOGGER=false
1515
RI_AUTO_BOOTSTRAP=false
1616
RI_MIGRATE_OLD_FOLDERS=false
1717
RI_BUILD_TYPE='VS_CODE'
18-
RI_ENCRYPTION_KEYTAR=false
1918
RI_ANALYTICS_START_EVENTS=true
2019
RI_AGREEMENTS_PATH='../../webviews/resources/agreements-spec.json'
20+
RI_ENCRYPTION_KEYTAR_SERVICE="redis-for-vscode"
2121
# RI_SEGMENT_WRITE_KEY='SEGMENT_WRITE_KEY'

.github/workflows/pipeline-build-linux.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
uses: ./.github/actions/download-backend
3636

3737
- name: Set RI_SEGMENT_WRITE_KEY to .env file
38-
run: echo "RI_SEGMENT_WRITE_KEY='$RI_SEGMENT_WRITE_KEY'" >> $envFile
38+
run: echo "RI_SEGMENT_WRITE_KEY='${{ env.RI_SEGMENT_WRITE_KEY }}'" >> ${{ env.envFile }}
3939

4040
- name: Build linux package (production)
4141
if: inputs.environment == 'production'
@@ -45,7 +45,7 @@ jobs:
4545
- name: Build linux package (staging)
4646
if: inputs.environment == 'staging'
4747
run: |
48-
sed -i "s/^RI_APP_FOLDER_NAME=.*/RI_APP_FOLDER_NAME='.redis-for-vscode-stage'/" $envFile
48+
sed -i "s/^RI_APP_FOLDER_NAME=.*/RI_APP_FOLDER_NAME='.redis-for-vscode-stage'/" ${{ env.envFile }}
4949
yarn package:stage --target linux-x64 --out ${packagePath}
5050
5151
- uses: actions/upload-artifact@v4

.github/workflows/pipeline-build-macos.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
uses: ./.github/actions/install-all-build-libs
2929

3030
- name: Set RI_SEGMENT_WRITE_KEY to .env file
31-
run: echo "RI_SEGMENT_WRITE_KEY='$RI_SEGMENT_WRITE_KEY'" >> $envFile
31+
run: echo "RI_SEGMENT_WRITE_KEY='${{ env.RI_SEGMENT_WRITE_KEY }}'" >> ${{ env.envFile }}
3232

3333
- name: Download backend x64
3434
uses: ./.github/actions/download-backend
@@ -38,7 +38,7 @@ jobs:
3838
- name: Build macos x64 package (staging)
3939
if: inputs.environment != 'production'
4040
run: |
41-
sed -i '' "s/^RI_APP_FOLDER_NAME=.*/RI_APP_FOLDER_NAME='.redis-for-vscode-stage'/" $envFile
41+
sed -i '' "s/^RI_APP_FOLDER_NAME=.*/RI_APP_FOLDER_NAME='.redis-for-vscode-stage'/" ${{ env.envFile }}
4242
4343
yarn package:stage --target darwin-x64 --out ${packagePath}-x64.vsix
4444
@@ -55,7 +55,7 @@ jobs:
5555
- name: Build macos arm64 package (staging)
5656
if: inputs.environment != 'production'
5757
run: |
58-
sed -i '' "s/^RI_APP_FOLDER_NAME=.*/RI_APP_FOLDER_NAME='.redis-for-vscode-stage'/" $envFile
58+
sed -i '' "s/^RI_APP_FOLDER_NAME=.*/RI_APP_FOLDER_NAME='.redis-for-vscode-stage'/" ${{ env.envFile }}
5959
6060
yarn package:stage --target darwin-arm64 --out ${packagePath}-arm64.vsix
6161

.github/workflows/pipeline-build-windows.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
uses: ./.github/actions/download-backend
2626

2727
- name: Set RI_SEGMENT_WRITE_KEY to .env file
28-
run: echo "RI_SEGMENT_WRITE_KEY='$RI_SEGMENT_WRITE_KEY'" >> $envFile
28+
run: echo "RI_SEGMENT_WRITE_KEY='${{ env.RI_SEGMENT_WRITE_KEY }}'" >> ${{ env.envFile }}
2929

3030
- name: Build windows package (production)
3131
if: inputs.environment == 'production'
@@ -35,7 +35,7 @@ jobs:
3535
- name: Build windows package (staging)
3636
if: inputs.environment == 'staging'
3737
run: |
38-
sed -i "s/^RI_APP_FOLDER_NAME=.*/RI_APP_FOLDER_NAME='.redis-for-vscode-stage'/" $envFile
38+
sed -i "s/^RI_APP_FOLDER_NAME=.*/RI_APP_FOLDER_NAME='.redis-for-vscode-stage'/" ${{ env.envFile }}
3939
yarn package:stage --target win32-x64 --out ${{ env.packagePath }}
4040
4141
- uses: actions/upload-artifact@v4

.github/workflows/tests-e2e-linux.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
- name: Setup Node
2929
uses: actions/setup-node@v4
3030
with:
31-
node-version: '20.15'
31+
node-version: '20.18.0'
3232

3333
- name: Download linux artifact
3434
uses: actions/download-artifact@v4

.github/workflows/tests-frontend.yml

+32-15
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@ on:
33
workflow_call:
44

55
env:
6+
SLACK_AUDIT_REPORT_CHANNEL: ${{ secrets.SLACK_AUDIT_REPORT_CHANNEL }}
67
SLACK_AUDIT_REPORT_KEY: ${{ secrets.SLACK_AUDIT_REPORT_KEY }}
8+
AWS_BUCKET_NAME_TEST: ${{ vars.AWS_BUCKET_NAME_TEST }}
9+
AWS_DEFAULT_REGION: ${{ vars.AWS_DEFAULT_REGION }}
10+
AWS_DISTRIBUTION_ID: ${{ secrets.AWS_DISTRIBUTION_ID }}
11+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
12+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
13+
REPORT_NAME: "report-vscode-fe"
14+
S3_PATH: "report-vscode-fe"
715

816
jobs:
917
unit-tests:
@@ -18,21 +26,30 @@ jobs:
1826
- name: Unit tests UI
1927
run: yarn test:cov
2028

21-
- name: Publish Test Results
22-
uses: EnricoMi/publish-unit-test-result-action@v2
29+
- name: Get current date
30+
id: date
2331
if: always()
24-
with:
25-
check_name: 'FE Unit tests summary'
26-
comment_mode: 'failures'
27-
files: reports/junit.xml
32+
uses: RedisInsight/RedisInsight/.github/actions/get-current-date@873a0ebf55c85d3127bb4efb4d0636d9ab838226
2833

29-
- name: Generate test results
30-
uses: dorny/test-reporter@v1
34+
35+
- name: Deploy 🚀
36+
if: always()
37+
run: |
38+
39+
GZIP_FILE=html.meta.json.gz
40+
S3_SUB_PATH="test-reports/${{ steps.date.outputs.date }}/${{ github.run_id }}/${{ env.REPORT_NAME }}"
41+
42+
aws s3 cp report/ s3://${AWS_BUCKET_NAME_TEST}/public/${S3_SUB_PATH} --recursive --exclude "*.gz"
43+
44+
# s3 modified "gzip" content-type
45+
# https://github.com/aws/aws-cli/issues/1131
46+
aws s3 cp report/${GZIP_FILE} s3://${AWS_BUCKET_NAME_TEST}/public/${S3_SUB_PATH}/${GZIP_FILE} --content-type "application/x-gzip" --metadata-directive REPLACE
47+
48+
echo "S3_SUB_PATH=${S3_SUB_PATH}" >> $GITHUB_ENV
49+
50+
51+
- name: Add link to report in the workflow summary
3152
if: always()
32-
with:
33-
name: 'Test results: FE unit tests'
34-
path: reports/junit.xml
35-
reporter: jest-junit
36-
list-tests: 'failed'
37-
list-suites: 'failed'
38-
fail-on-error: 'false'
53+
run: |
54+
link="${{ vars.DEFAULT_TEST_REPORTS_URL }}/${S3_SUB_PATH}/index.html"
55+
echo "[${link}](${link})" >> $GITHUB_STEP_SUMMARY

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@
237237
"ts-node": "^10.9.1",
238238
"tsconfig-paths": "^4.2.0",
239239
"typescript": "^5.4.5",
240+
"upath": "^2.0.1",
240241
"uuid": "^9.0.1",
241242
"vite": "^5.2.10",
242243
"vite-plugin-react-click-to-component": "^3.0.0",

scripts/downloadBackend.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import * as fs from 'fs'
33
import * as path from 'path'
44
import * as cp from 'child_process'
55
import * as dotenv from 'dotenv'
6+
import * as upath from 'upath'
67
import { parse as parseUrl } from 'url'
78

9+
810
dotenv.config({
911
path: [
1012
path.join(__dirname, '..', '.env'),
@@ -80,13 +82,28 @@ async function downloadRedisBackendArchive(
8082
})
8183
}
8284

85+
function getNormalizedString(string: string) {
86+
return string?.startsWith('D:')
87+
? upath.normalize(string).replace('D:', '/d')
88+
: string
89+
}
90+
8391
function unzipRedisServer(redisInsideArchivePath: string, extractDir: string) {
8492
// tar does not create extractDir by default
8593
if (!fs.existsSync(extractDir)) {
8694
fs.mkdirSync(extractDir)
8795
}
8896

89-
cp.spawnSync('tar', ['-xf', redisInsideArchivePath, '-C', extractDir, '--strip-components', '1', 'api'])
97+
cp.spawnSync('tar', [
98+
'-xf',
99+
getNormalizedString(redisInsideArchivePath),
100+
'-C',
101+
getNormalizedString(extractDir),
102+
'--strip-components',
103+
'1',
104+
'api',
105+
])
106+
90107

91108
// remove tutorials
92109
fs.rmSync(tutorialsPath, { recursive: true, force: true });

src/resources/agreements-spec.json

+32-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "1.0.1",
2+
"version": "1.0.2",
33
"agreements": {
44
"analytics": {
55
"defaultValue": false,
@@ -35,6 +35,37 @@
3535
"title": "Server Side Public License",
3636
"label": "I have read and understood the Terms",
3737
"requiredText": "Accept the Server Side Public License"
38+
},
39+
"encryption": {
40+
"conditional": true,
41+
"checker": "KEYTAR",
42+
"defaultOption": "false",
43+
"options": {
44+
"true": {
45+
"defaultValue": true,
46+
"displayInSetting": false,
47+
"required": false,
48+
"editable": true,
49+
"disabled": false,
50+
"category": "privacy",
51+
"since": "1.0.2",
52+
"title": "Encryption",
53+
"label": "Encrypt sensitive information",
54+
"description": "Select to encrypt sensitive information using system keychain. Otherwise, this information is stored locally in plain text, which may incur security risk."
55+
},
56+
"false": {
57+
"defaultValue": false,
58+
"displayInSetting": false,
59+
"required": false,
60+
"editable": true,
61+
"disabled": true,
62+
"category": "privacy",
63+
"since": "1.0.2",
64+
"title": "Encryption",
65+
"label": "Encrypt sensitive information",
66+
"description": "Install or enable the system keychain to encrypt and securely store your sensitive information added before using the application. Otherwise, this information will be stored locally in plain text and may lead to security risks."
67+
}
68+
}
3869
}
3970
}
4071
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import React from 'react'
2+
import { instance, mock } from 'ts-mockito'
3+
import { fireEvent, screen, render, act } from 'testSrc/helpers'
4+
import { AutoRefresh, Props } from './AutoRefresh'
5+
import { DEFAULT_REFRESH_RATE } from './utils'
6+
7+
const mockedProps = mock<Props>()
8+
9+
const INLINE_ITEM_EDITOR = 'inline-item-editor'
10+
11+
describe('AutoRefresh', () => {
12+
it('should render', () => {
13+
expect(render(<AutoRefresh {...instance(mockedProps)} />)).toBeTruthy()
14+
})
15+
16+
it('prop "displayText = true" should show Refresh text', () => {
17+
const { queryByTestId } = render(<AutoRefresh {...instance(mockedProps)} displayText />)
18+
19+
expect(queryByTestId('refresh-message-label')).toBeInTheDocument()
20+
})
21+
22+
it('prop "displayText = false" should hide Refresh text', () => {
23+
const { queryByTestId } = render(<AutoRefresh {...instance(mockedProps)} displayText={false} />)
24+
25+
expect(queryByTestId('refresh-message-label')).not.toBeInTheDocument()
26+
})
27+
28+
it('should call onRefresh', () => {
29+
const onRefresh = vi.fn()
30+
render(<AutoRefresh {...instance(mockedProps)} onRefresh={onRefresh} />)
31+
32+
fireEvent.click(screen.getByTestId('refresh-btn'))
33+
expect(onRefresh).toBeCalled()
34+
})
35+
36+
it('refresh text should contain "Last refresh" time with disabled auto-refresh', async () => {
37+
render(<AutoRefresh {...instance(mockedProps)} displayText />)
38+
39+
expect(screen.getByTestId('refresh-message-label')).toHaveTextContent(/Last refresh:/i)
40+
expect(screen.getByTestId('refresh-message')).toHaveTextContent('now')
41+
})
42+
43+
it('refresh text should contain "Auto-refresh" time with enabled auto-refresh', async () => {
44+
render(<AutoRefresh {...instance(mockedProps)} displayText />)
45+
46+
fireEvent.click(screen.getByTestId('auto-refresh-config-btn'))
47+
fireEvent.click(screen.getByTestId('auto-refresh-switch'))
48+
49+
expect(screen.getByTestId('refresh-message-label')).toHaveTextContent(/Auto refresh:/i)
50+
expect(screen.getByTestId('refresh-message')).toHaveTextContent(DEFAULT_REFRESH_RATE)
51+
})
52+
53+
it('should locate refresh message label when testid is provided', () => {
54+
render(<AutoRefresh {...instance(mockedProps)} displayText testid="testid" />)
55+
56+
expect(screen.getByTestId('testid-refresh-message-label')).toBeInTheDocument()
57+
})
58+
59+
it('should locate refresh message when testid is provided', () => {
60+
render(<AutoRefresh {...instance(mockedProps)} displayText testid="testid" />)
61+
62+
expect(screen.getByTestId('testid-refresh-message')).toBeInTheDocument()
63+
})
64+
65+
it('should locate refresh button when testid is provided', () => {
66+
render(<AutoRefresh {...instance(mockedProps)} testid="testid" />)
67+
68+
expect(screen.getByTestId('testid-refresh-btn')).toBeInTheDocument()
69+
})
70+
71+
it('should locate auto-refresh config button when testid is provided', () => {
72+
render(<AutoRefresh {...instance(mockedProps)} testid="testid" />)
73+
74+
expect(screen.getByTestId('testid-auto-refresh-config-btn')).toBeInTheDocument()
75+
})
76+
77+
it('should locate auto-refresh switch when testid is provided', () => {
78+
render(<AutoRefresh {...instance(mockedProps)} testid="testid" />)
79+
80+
fireEvent.click(screen.getByTestId('testid-auto-refresh-config-btn'))
81+
expect(screen.getByTestId('testid-auto-refresh-switch')).toBeInTheDocument()
82+
})
83+
84+
describe('AutoRefresh Config', () => {
85+
it('Auto refresh config should render', () => {
86+
const { queryByTestId } = render(<AutoRefresh {...instance(mockedProps)} />)
87+
88+
fireEvent.click(screen.getByTestId('auto-refresh-config-btn'))
89+
expect(queryByTestId('auto-refresh-switch')).toBeInTheDocument()
90+
})
91+
92+
it('should call onRefresh after enable auto-refresh and set 1 sec', async () => {
93+
const onRefresh = vi.fn()
94+
render(<AutoRefresh {...instance(mockedProps)} onRefresh={onRefresh} />)
95+
96+
fireEvent.click(screen.getByTestId('auto-refresh-config-btn'))
97+
fireEvent.click(screen.getByTestId('auto-refresh-switch'))
98+
fireEvent.click(screen.getByTestId(INLINE_ITEM_EDITOR))
99+
100+
fireEvent.input(screen.getByTestId(INLINE_ITEM_EDITOR), { target: { value: '1' } })
101+
expect(screen.getByTestId(INLINE_ITEM_EDITOR)).toHaveValue('1')
102+
103+
screen.getByTestId(/apply-btn/).click()
104+
105+
await act(async () => {
106+
await new Promise((r) => setTimeout(r, 1300))
107+
})
108+
expect(onRefresh).toBeCalledTimes(1)
109+
110+
await act(async () => {
111+
await new Promise((r) => setTimeout(r, 1300))
112+
})
113+
expect(onRefresh).toBeCalledTimes(2)
114+
115+
await act(async () => {
116+
await new Promise((r) => setTimeout(r, 1300))
117+
})
118+
expect(onRefresh).toBeCalledTimes(3)
119+
})
120+
})
121+
122+
it('should NOT call onRefresh with disabled state', async () => {
123+
const onRefresh = vi.fn()
124+
const { rerender } = render(<AutoRefresh {...instance(mockedProps)} onRefresh={onRefresh} />)
125+
126+
fireEvent.click(screen.getByTestId('auto-refresh-config-btn'))
127+
fireEvent.click(screen.getByTestId('auto-refresh-switch'))
128+
fireEvent.click(screen.getByTestId(INLINE_ITEM_EDITOR))
129+
fireEvent.input(screen.getByTestId(INLINE_ITEM_EDITOR), { target: { value: '1' } })
130+
131+
expect(screen.getByTestId(INLINE_ITEM_EDITOR)).toHaveValue('1')
132+
133+
screen.getByTestId(/apply-btn/).click()
134+
135+
await act(() => {
136+
rerender(<AutoRefresh {...instance(mockedProps)} onRefresh={onRefresh} disabled />)
137+
})
138+
139+
await act(async () => {
140+
await new Promise((r) => setTimeout(r, 1300))
141+
})
142+
expect(onRefresh).toBeCalledTimes(0)
143+
144+
await act(async () => {
145+
await new Promise((r) => setTimeout(r, 1300))
146+
})
147+
expect(onRefresh).toBeCalledTimes(0)
148+
149+
await act(() => {
150+
rerender(<AutoRefresh {...instance(mockedProps)} onRefresh={onRefresh} disabled={false} />)
151+
})
152+
153+
await act(async () => {
154+
await new Promise((r) => setTimeout(r, 1300))
155+
})
156+
expect(onRefresh).toBeCalledTimes(1)
157+
})
158+
})

0 commit comments

Comments
 (0)