@eclipse-che/che-e2e 7.105.0 → 7.106.0-next-2338445
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/configs/inversify.config.ts +8 -0
- package/configs/inversify.types.ts +5 -1
- package/constants/TIMEOUT_CONSTANTS.ts +6 -0
- package/dist/configs/inversify.config.js +8 -0
- package/dist/configs/inversify.config.js.map +1 -1
- package/dist/configs/inversify.types.js +5 -1
- package/dist/configs/inversify.types.js.map +1 -1
- package/dist/constants/TIMEOUT_CONSTANTS.js +4 -0
- package/dist/constants/TIMEOUT_CONSTANTS.js.map +1 -1
- package/dist/driver/ChromeDriver.js +5 -1
- package/dist/driver/ChromeDriver.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/pageobjects/ide/CommandPalette.js +117 -0
- package/dist/pageobjects/ide/CommandPalette.js.map +1 -0
- package/dist/pageobjects/ide/ExplorerView.js +108 -0
- package/dist/pageobjects/ide/ExplorerView.js.map +1 -0
- package/dist/pageobjects/ide/ExtensionsView.js +162 -0
- package/dist/pageobjects/ide/ExtensionsView.js.map +1 -0
- package/dist/pageobjects/ide/NotificationHandler.js +83 -0
- package/dist/pageobjects/ide/NotificationHandler.js.map +1 -0
- package/dist/pageobjects/ide/ViewsMoreActionsButton.js +9 -0
- package/dist/pageobjects/ide/ViewsMoreActionsButton.js.map +1 -1
- package/dist/specs/miscellaneous/VsixInstallationDisableTest.spec.js +205 -0
- package/dist/specs/miscellaneous/VsixInstallationDisableTest.spec.js.map +1 -0
- package/driver/ChromeDriver.ts +5 -1
- package/index.ts +4 -0
- package/package.json +2 -3
- package/pageobjects/ide/CommandPalette.ts +114 -0
- package/pageobjects/ide/ExplorerView.ts +106 -0
- package/pageobjects/ide/ExtensionsView.ts +167 -0
- package/pageobjects/ide/NotificationHandler.ts +70 -0
- package/pageobjects/ide/ViewsMoreActionsButton.ts +11 -0
- package/resources/configmap-disable-vsix-installation.yaml +22 -0
- package/resources/configmap-enable-vsix-installation.yaml +22 -0
- package/resources/default-extensions-configmap.yaml +12 -0
- package/specs/miscellaneous/VsixInstallationDisableTest.spec.ts +243 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/** *******************************************************************
|
|
2
|
+
* copyright (c) 2025 Red Hat, Inc.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made
|
|
5
|
+
* available under the terms of the Eclipse Public License 2.0
|
|
6
|
+
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
|
7
|
+
*
|
|
8
|
+
* SPDX-License-Identifier: EPL-2.0
|
|
9
|
+
**********************************************************************/
|
|
10
|
+
import { inject, injectable } from 'inversify';
|
|
11
|
+
import { CLASSES } from '../../configs/inversify.types';
|
|
12
|
+
import { By, Key, WebElement } from 'selenium-webdriver';
|
|
13
|
+
import { ActivityBar, ExtensionsViewItem, ExtensionsViewSection, SideBarView, ViewControl } from 'monaco-page-objects';
|
|
14
|
+
import { DriverHelper } from '../../utils/DriverHelper';
|
|
15
|
+
import { Logger } from '../../utils/Logger';
|
|
16
|
+
import { ViewsMoreActionsButton } from './ViewsMoreActionsButton';
|
|
17
|
+
import { TIMEOUT_CONSTANTS } from '../../constants/TIMEOUT_CONSTANTS';
|
|
18
|
+
|
|
19
|
+
@injectable()
|
|
20
|
+
export class ExtensionsView {
|
|
21
|
+
private static readonly EXTENSIONS_VIEW: By = By.css('.extensions-viewlet');
|
|
22
|
+
private static readonly MENU_ITEM: By = By.css('.context-view .monaco-menu .action-item');
|
|
23
|
+
private static readonly SEARCH_BOX: By = By.css('input.monaco-inputbox-input');
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
@inject(CLASSES.DriverHelper)
|
|
27
|
+
private readonly driverHelper: DriverHelper,
|
|
28
|
+
@inject(CLASSES.ViewsMoreActionsButton)
|
|
29
|
+
private readonly viewsMoreActionsButton: ViewsMoreActionsButton
|
|
30
|
+
) {}
|
|
31
|
+
|
|
32
|
+
async openExtensionsView(): Promise<void> {
|
|
33
|
+
Logger.debug();
|
|
34
|
+
|
|
35
|
+
const viewCtrl: ViewControl | undefined = await new ActivityBar().getViewControl('Extensions');
|
|
36
|
+
await viewCtrl?.openView();
|
|
37
|
+
|
|
38
|
+
const extensionsViewVisible: boolean = await this.driverHelper.waitVisibilityBoolean(ExtensionsView.EXTENSIONS_VIEW);
|
|
39
|
+
|
|
40
|
+
if (!extensionsViewVisible) {
|
|
41
|
+
throw new Error('Extensions view could not be opened');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async openMoreActionsMenu(): Promise<void> {
|
|
46
|
+
Logger.debug();
|
|
47
|
+
|
|
48
|
+
await this.viewsMoreActionsButton.clickViewsMoreActionsButton();
|
|
49
|
+
await this.viewsMoreActionsButton.waitForContextMenu();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* get all items in the More Actions menu
|
|
54
|
+
*
|
|
55
|
+
* @returns Array of menu item texts
|
|
56
|
+
*/
|
|
57
|
+
async getMoreActionsMenuItems(): Promise<string[]> {
|
|
58
|
+
Logger.debug();
|
|
59
|
+
|
|
60
|
+
const menuItems: WebElement[] = await this.driverHelper.getDriver().findElements(ExtensionsView.MENU_ITEM);
|
|
61
|
+
const menuTexts: string[] = [];
|
|
62
|
+
|
|
63
|
+
for (const item of menuItems) {
|
|
64
|
+
try {
|
|
65
|
+
const text: string = await item.getText();
|
|
66
|
+
menuTexts.push(text);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
// skip items that cannot be read
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return menuTexts;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* check if a specific item is visible in the More Actions menu
|
|
77
|
+
*
|
|
78
|
+
* @param menuItemText Text to check for
|
|
79
|
+
* @returns True if item is visible
|
|
80
|
+
*/
|
|
81
|
+
async isMoreActionsMenuItemVisible(menuItemText: string): Promise<boolean> {
|
|
82
|
+
Logger.debug(`"${menuItemText}"`);
|
|
83
|
+
|
|
84
|
+
const menuItems: string[] = await this.getMoreActionsMenuItems();
|
|
85
|
+
return menuItems.some((item: string): boolean => item.includes(menuItemText));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async closeMoreActionsMenu(): Promise<void> {
|
|
89
|
+
Logger.debug();
|
|
90
|
+
|
|
91
|
+
await this.driverHelper.getDriver().actions().sendKeys(Key.ESCAPE).perform();
|
|
92
|
+
await this.driverHelper.wait(TIMEOUT_CONSTANTS.TS_SELENIUM_DEFAULT_POLLING);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* get names of all installed extensions
|
|
97
|
+
*/
|
|
98
|
+
async getInstalledExtensionNames(): Promise<string[]> {
|
|
99
|
+
Logger.debug();
|
|
100
|
+
|
|
101
|
+
await this.openExtensionsView();
|
|
102
|
+
|
|
103
|
+
const extensionsView: SideBarView | undefined = await (await new ActivityBar().getViewControl('Extensions'))?.openView();
|
|
104
|
+
if (!extensionsView) {
|
|
105
|
+
throw new Error('Could not open Extensions view');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const [extensionSection]: ExtensionsViewSection[] = (await extensionsView.getContent().getSections()) as ExtensionsViewSection[];
|
|
109
|
+
if (!extensionSection) {
|
|
110
|
+
throw new Error('Could not find Extensions section');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// search for installed extensions
|
|
114
|
+
await this.searchInExtensions(extensionSection, '@installed');
|
|
115
|
+
|
|
116
|
+
// wait for results to load
|
|
117
|
+
await this.driverHelper.wait(TIMEOUT_CONSTANTS.TS_SELENIUM_DEFAULT_POLLING);
|
|
118
|
+
|
|
119
|
+
const installedItems: ExtensionsViewItem[] = await extensionSection.getVisibleItems();
|
|
120
|
+
const extensionNames: string[] = [];
|
|
121
|
+
|
|
122
|
+
for (const item of installedItems) {
|
|
123
|
+
try {
|
|
124
|
+
const title: string = await item.getTitle();
|
|
125
|
+
extensionNames.push(title);
|
|
126
|
+
} catch (err) {
|
|
127
|
+
// skip items that cannot be read
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
Logger.debug(`Found ${extensionNames.length} installed extensions: ${extensionNames.join(', ')}`);
|
|
132
|
+
return extensionNames;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* search for extensions using a search term
|
|
137
|
+
*
|
|
138
|
+
* @param extensionSection The ExtensionsViewSection to search in
|
|
139
|
+
* @param searchText Text to search for
|
|
140
|
+
*/
|
|
141
|
+
private async searchInExtensions(extensionSection: ExtensionsViewSection, searchText: string): Promise<void> {
|
|
142
|
+
Logger.debug(`Searching for: "${searchText}"`);
|
|
143
|
+
|
|
144
|
+
const enclosingItem: WebElement = extensionSection.getEnclosingElement();
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const searchField: WebElement = await enclosingItem.findElement(ExtensionsView.SEARCH_BOX);
|
|
148
|
+
|
|
149
|
+
// clear existing search
|
|
150
|
+
await this.driverHelper.getDriver().actions().click(searchField).perform();
|
|
151
|
+
await this.driverHelper
|
|
152
|
+
.getDriver()
|
|
153
|
+
.actions()
|
|
154
|
+
.keyDown(Key.CONTROL)
|
|
155
|
+
.sendKeys('a')
|
|
156
|
+
.keyUp(Key.CONTROL)
|
|
157
|
+
.sendKeys(Key.DELETE)
|
|
158
|
+
.perform();
|
|
159
|
+
|
|
160
|
+
// enter new search text
|
|
161
|
+
await this.driverHelper.getDriver().actions().sendKeys(searchText).perform();
|
|
162
|
+
await this.driverHelper.wait(TIMEOUT_CONSTANTS.TS_SELENIUM_DEFAULT_POLLING);
|
|
163
|
+
} catch (err) {
|
|
164
|
+
Logger.debug(`Could not interact with search field: ${err}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** *******************************************************************
|
|
2
|
+
* copyright (c) 2025 Red Hat, Inc.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made
|
|
5
|
+
* available under the terms of the Eclipse Public License 2.0
|
|
6
|
+
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
|
7
|
+
*
|
|
8
|
+
* SPDX-License-Identifier: EPL-2.0
|
|
9
|
+
**********************************************************************/
|
|
10
|
+
import { inject, injectable } from 'inversify';
|
|
11
|
+
import { CLASSES } from '../../configs/inversify.types';
|
|
12
|
+
import { By } from 'selenium-webdriver';
|
|
13
|
+
import { DriverHelper } from '../../utils/DriverHelper';
|
|
14
|
+
import { Logger } from '../../utils/Logger';
|
|
15
|
+
import { TIMEOUT_CONSTANTS } from '../../constants/TIMEOUT_CONSTANTS';
|
|
16
|
+
import { WebElement } from 'monaco-page-objects';
|
|
17
|
+
|
|
18
|
+
@injectable()
|
|
19
|
+
export class NotificationHandler {
|
|
20
|
+
private static readonly NOTIFICATION_MESSAGE: By = By.css('.notification-list-item-message');
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
@inject(CLASSES.DriverHelper)
|
|
24
|
+
private readonly driverHelper: DriverHelper
|
|
25
|
+
) {}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* check for notifications containing specific text
|
|
29
|
+
*/
|
|
30
|
+
async checkForNotification(expectedText: string, timeoutMs: number = TIMEOUT_CONSTANTS.TS_NOTIFICATION_WAIT_TIMEOUT): Promise<boolean> {
|
|
31
|
+
Logger.debug(`Checking for notification containing: "${expectedText}"`);
|
|
32
|
+
|
|
33
|
+
const startTime: number = Date.now();
|
|
34
|
+
|
|
35
|
+
if (await this.findInExistingNotifications(expectedText)) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
40
|
+
if (await this.findInExistingNotifications(expectedText)) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
await this.driverHelper.wait(TIMEOUT_CONSTANTS.TS_SELENIUM_DEFAULT_POLLING);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
Logger.debug(`Notification containing "${expectedText}" not found after ${timeoutMs}ms`);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private async findInExistingNotifications(expectedText: string): Promise<boolean> {
|
|
51
|
+
try {
|
|
52
|
+
const elements: WebElement[] = await this.driverHelper.getDriver().findElements(NotificationHandler.NOTIFICATION_MESSAGE);
|
|
53
|
+
|
|
54
|
+
for (const element of elements) {
|
|
55
|
+
try {
|
|
56
|
+
const text: string = await element.getText();
|
|
57
|
+
if (text && text.toLowerCase().includes(expectedText.toLowerCase())) {
|
|
58
|
+
Logger.debug(`Found matching notification: "${text}"`);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
} catch (err) {
|
|
62
|
+
// continue to next element
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {
|
|
66
|
+
// no notifications found
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -53,4 +53,15 @@ export class ViewsMoreActionsButton {
|
|
|
53
53
|
|
|
54
54
|
return viewsActionsButton;
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
async clickViewsMoreActionsButton(): Promise<void> {
|
|
58
|
+
Logger.debug();
|
|
59
|
+
await this.driverHelper.waitAndClick(ViewsMoreActionsButton.VIEWS_AND_MORE_ACTIONS_BUTTON);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async waitForContextMenu(): Promise<void> {
|
|
63
|
+
const cheCodeLocatorLoader: CheCodeLocatorLoader = e2eContainer.get(CLASSES.CheCodeLocatorLoader);
|
|
64
|
+
const webCheCodeLocators: Locators = cheCodeLocatorLoader.webCheCodeLocators;
|
|
65
|
+
await this.driverHelper.waitVisibility(webCheCodeLocators.ContextMenu.contextView);
|
|
66
|
+
}
|
|
56
67
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
kind: ConfigMap
|
|
2
|
+
apiVersion: v1
|
|
3
|
+
metadata:
|
|
4
|
+
name: vscode-editor-configurations
|
|
5
|
+
namespace: openshift-devspaces
|
|
6
|
+
labels:
|
|
7
|
+
app.kubernetes.io/part-of: che.eclipse.org
|
|
8
|
+
app.kubernetes.io/component: workspaces-config
|
|
9
|
+
data:
|
|
10
|
+
configurations.json: |
|
|
11
|
+
{
|
|
12
|
+
"extensions.install-from-vsix-enabled": false
|
|
13
|
+
}
|
|
14
|
+
settings.json: |
|
|
15
|
+
{
|
|
16
|
+
"window.header": "VSIX INSTALL = DISABLED",
|
|
17
|
+
"window.commandCenter": false,
|
|
18
|
+
"workbench.colorCustomizations": {
|
|
19
|
+
"titleBar.activeBackground": "#CCA700",
|
|
20
|
+
"titleBar.activeForeground": "#ffffff"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
kind: ConfigMap
|
|
2
|
+
apiVersion: v1
|
|
3
|
+
metadata:
|
|
4
|
+
name: vscode-editor-configurations
|
|
5
|
+
namespace: openshift-devspaces
|
|
6
|
+
labels:
|
|
7
|
+
app.kubernetes.io/part-of: che.eclipse.org
|
|
8
|
+
app.kubernetes.io/component: workspaces-config
|
|
9
|
+
data:
|
|
10
|
+
configurations.json: |
|
|
11
|
+
{
|
|
12
|
+
"extensions.install-from-vsix-enabled": true
|
|
13
|
+
}
|
|
14
|
+
settings.json: |
|
|
15
|
+
{
|
|
16
|
+
"window.header": "VSIX INSTALL = ENABLED",
|
|
17
|
+
"window.commandCenter": false,
|
|
18
|
+
"workbench.colorCustomizations": {
|
|
19
|
+
"titleBar.activeBackground": "#CCA700",
|
|
20
|
+
"titleBar.activeForeground": "#ffffff"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
kind: ConfigMap
|
|
2
|
+
apiVersion: v1
|
|
3
|
+
metadata:
|
|
4
|
+
name: default-extensions
|
|
5
|
+
namespace: admin-devspaces
|
|
6
|
+
labels:
|
|
7
|
+
controller.devfile.io/mount-to-devworkspace: 'true'
|
|
8
|
+
controller.devfile.io/watch-configmap: 'true'
|
|
9
|
+
annotations:
|
|
10
|
+
controller.devfile.io/mount-as: env
|
|
11
|
+
data:
|
|
12
|
+
DEFAULT_EXTENSIONS: '/projects/web-nodejs-sample-with-disabled-vsix/redhat.vscode-yaml-1.17.0.vsix'
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/** *******************************************************************
|
|
2
|
+
* copyright (c) 2025 Red Hat, Inc.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made
|
|
5
|
+
* available under the terms of the Eclipse Public License 2.0
|
|
6
|
+
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
|
7
|
+
*
|
|
8
|
+
* SPDX-License-Identifier: EPL-2.0
|
|
9
|
+
**********************************************************************/
|
|
10
|
+
|
|
11
|
+
import 'reflect-metadata';
|
|
12
|
+
import { e2eContainer } from '../../configs/inversify.config';
|
|
13
|
+
import { CLASSES, TYPES } from '../../configs/inversify.types';
|
|
14
|
+
import { expect } from 'chai';
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
|
|
18
|
+
import { WorkspaceHandlingTests } from '../../tests-library/WorkspaceHandlingTests';
|
|
19
|
+
import { ProjectAndFileTests } from '../../tests-library/ProjectAndFileTests';
|
|
20
|
+
import { LoginTests } from '../../tests-library/LoginTests';
|
|
21
|
+
import { registerRunningWorkspace } from '../MochaHooks';
|
|
22
|
+
|
|
23
|
+
import { KubernetesCommandLineToolsExecutor } from '../../utils/KubernetesCommandLineToolsExecutor';
|
|
24
|
+
import { ShellExecutor } from '../../utils/ShellExecutor';
|
|
25
|
+
import { ITestWorkspaceUtil } from '../../utils/workspace/ITestWorkspaceUtil';
|
|
26
|
+
|
|
27
|
+
import { Dashboard } from '../../pageobjects/dashboard/Dashboard';
|
|
28
|
+
import { NotificationHandler } from '../../pageobjects/ide/NotificationHandler';
|
|
29
|
+
import { CommandPalette } from '../../pageobjects/ide/CommandPalette';
|
|
30
|
+
import { ExtensionsView } from '../../pageobjects/ide/ExtensionsView';
|
|
31
|
+
import { ExplorerView } from '../../pageobjects/ide/ExplorerView';
|
|
32
|
+
|
|
33
|
+
import { DriverHelper } from '../../utils/DriverHelper';
|
|
34
|
+
import { BrowserTabsUtil } from '../../utils/BrowserTabsUtil';
|
|
35
|
+
import { BASE_TEST_CONSTANTS } from '../../constants/BASE_TEST_CONSTANTS';
|
|
36
|
+
import { Logger } from '../../utils/Logger';
|
|
37
|
+
|
|
38
|
+
suite(`Verify VSIX installation can be disabled via configuration ${BASE_TEST_CONSTANTS.TEST_ENVIRONMENT}`, function (): void {
|
|
39
|
+
const workspaceHandlingTests: WorkspaceHandlingTests = e2eContainer.get(CLASSES.WorkspaceHandlingTests);
|
|
40
|
+
const projectAndFileTests: ProjectAndFileTests = e2eContainer.get(CLASSES.ProjectAndFileTests);
|
|
41
|
+
const loginTests: LoginTests = e2eContainer.get(CLASSES.LoginTests);
|
|
42
|
+
const kubernetesCommandLineToolsExecutor: KubernetesCommandLineToolsExecutor = e2eContainer.get(
|
|
43
|
+
CLASSES.KubernetesCommandLineToolsExecutor
|
|
44
|
+
);
|
|
45
|
+
const shellExecutor: ShellExecutor = e2eContainer.get(CLASSES.ShellExecutor);
|
|
46
|
+
const testWorkspaceUtil: ITestWorkspaceUtil = e2eContainer.get(TYPES.WorkspaceUtil);
|
|
47
|
+
const dashboard: Dashboard = e2eContainer.get(CLASSES.Dashboard);
|
|
48
|
+
const driverHelper: DriverHelper = e2eContainer.get(CLASSES.DriverHelper);
|
|
49
|
+
const browserTabsUtil: BrowserTabsUtil = e2eContainer.get(CLASSES.BrowserTabsUtil);
|
|
50
|
+
const notificationHandler: NotificationHandler = e2eContainer.get(CLASSES.NotificationHandler);
|
|
51
|
+
const commandPalette: CommandPalette = e2eContainer.get(CLASSES.CommandPalette);
|
|
52
|
+
const extensionsView: ExtensionsView = e2eContainer.get(CLASSES.ExtensionsView);
|
|
53
|
+
const explorerView: ExplorerView = e2eContainer.get(CLASSES.ExplorerView);
|
|
54
|
+
|
|
55
|
+
const TEST_REPO_URL: string = 'https://github.com/crw-qe/web-nodejs-sample-with-disabled-vsix/tree/install-from-vsix-disabled-7-100';
|
|
56
|
+
const RESOURCES_PATH: string = path.join(__dirname, '../../../resources');
|
|
57
|
+
const DEFAULT_EXTENSIONS: string[] = ['YAML'];
|
|
58
|
+
const VSIX_FILE_NAME: string = 'redhat.vscode-yaml-1.17.0.vsix';
|
|
59
|
+
|
|
60
|
+
const CONFIG_MAP_NAMES: { VSIX_CONFIG: string; DEFAULT_EXTENSIONS: string } = {
|
|
61
|
+
VSIX_CONFIG: 'vscode-editor-configurations',
|
|
62
|
+
DEFAULT_EXTENSIONS: 'default-extensions'
|
|
63
|
+
};
|
|
64
|
+
const CONFIG_MAP_NAMESPACES: { VSIX_CONFIG: string; DEFAULT_EXTENSIONS: string } = {
|
|
65
|
+
VSIX_CONFIG: 'openshift-devspaces',
|
|
66
|
+
DEFAULT_EXTENSIONS: 'admin-devspaces'
|
|
67
|
+
};
|
|
68
|
+
const CONFIG_FILES: { ENABLE_VSIX: string; DEFAULT_EXTENSIONS: string; DISABLE_VSIX: string } = {
|
|
69
|
+
DISABLE_VSIX: 'configmap-disable-vsix-installation.yaml',
|
|
70
|
+
ENABLE_VSIX: 'configmap-enable-vsix-installation.yaml',
|
|
71
|
+
DEFAULT_EXTENSIONS: 'default-extensions-configmap.yaml'
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
let workspaceName: string = '';
|
|
75
|
+
|
|
76
|
+
function applyConfigMap(configFileName: string): void {
|
|
77
|
+
const configPath: string = path.join(RESOURCES_PATH, configFileName);
|
|
78
|
+
const configContent: string = fs.readFileSync(configPath, 'utf8');
|
|
79
|
+
shellExecutor.executeCommand(`oc apply -f - <<EOF\n${configContent}\nEOF`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function cleanupConfigMaps(): void {
|
|
83
|
+
Logger.debug(`Deleting ConfigMap ${CONFIG_MAP_NAMES.VSIX_CONFIG} from namespace ${CONFIG_MAP_NAMESPACES.VSIX_CONFIG}`);
|
|
84
|
+
shellExecutor.executeCommand(
|
|
85
|
+
`oc delete configmap ${CONFIG_MAP_NAMES.VSIX_CONFIG} -n ${CONFIG_MAP_NAMESPACES.VSIX_CONFIG} --ignore-not-found=true`
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
Logger.debug(
|
|
89
|
+
`Deleting ConfigMap ${CONFIG_MAP_NAMES.DEFAULT_EXTENSIONS} from namespace ${CONFIG_MAP_NAMESPACES.DEFAULT_EXTENSIONS}`
|
|
90
|
+
);
|
|
91
|
+
shellExecutor.executeCommand(
|
|
92
|
+
`oc delete configmap ${CONFIG_MAP_NAMES.DEFAULT_EXTENSIONS} -n ${CONFIG_MAP_NAMESPACES.DEFAULT_EXTENSIONS} --ignore-not-found=true`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ========== Test Execution ==========
|
|
97
|
+
|
|
98
|
+
suiteSetup('Login to cluster and setup initial state', function (): void {
|
|
99
|
+
kubernetesCommandLineToolsExecutor.loginToOcp();
|
|
100
|
+
cleanupConfigMaps();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
suiteSetup('Login to application', async function (): Promise<void> {
|
|
104
|
+
await loginTests.loginIntoChe();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('Apply ConfigMaps that disable VSIX and set default extensions', function (): void {
|
|
108
|
+
applyConfigMap(CONFIG_FILES.DISABLE_VSIX);
|
|
109
|
+
applyConfigMap(CONFIG_FILES.DEFAULT_EXTENSIONS);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('Create and open workspace from Git repository', async function (): Promise<void> {
|
|
113
|
+
await workspaceHandlingTests.createAndOpenWorkspaceFromGitRepository(TEST_REPO_URL);
|
|
114
|
+
await workspaceHandlingTests.obtainWorkspaceNameFromStartingPage();
|
|
115
|
+
workspaceName = WorkspaceHandlingTests.getWorkspaceName();
|
|
116
|
+
registerRunningWorkspace(workspaceName);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('Wait workspace readiness', async function (): Promise<void> {
|
|
120
|
+
await projectAndFileTests.waitWorkspaceReadinessForCheCodeEditor();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('Verify VSIX disabled notifications', async function (): Promise<void> {
|
|
124
|
+
const hasDisableNotification: boolean = await notificationHandler.checkForNotification('install from vsix command is disabled');
|
|
125
|
+
expect(hasDisableNotification).to.be.true;
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('Perform trust author dialog', async function (): Promise<void> {
|
|
129
|
+
await projectAndFileTests.performTrustAuthorDialog();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('Verify VSIX installation is disabled in UI and extensions are not installed', async function (): Promise<void> {
|
|
133
|
+
// check Command Palette
|
|
134
|
+
await commandPalette.openCommandPalette();
|
|
135
|
+
await commandPalette.searchCommand('Install from VSIX');
|
|
136
|
+
const commandAvailable: boolean = await commandPalette.isCommandVisible('Extensions: Install from VSIX...');
|
|
137
|
+
await commandPalette.closeCommandPalette();
|
|
138
|
+
expect(commandAvailable).to.equal(false, 'Command Palette should not contain Install from VSIX command');
|
|
139
|
+
|
|
140
|
+
// check Extensions view menu
|
|
141
|
+
await extensionsView.openExtensionsView();
|
|
142
|
+
await extensionsView.openMoreActionsMenu();
|
|
143
|
+
const extensionMenuAvailable: boolean = await extensionsView.isMoreActionsMenuItemVisible('Install from VSIX');
|
|
144
|
+
await extensionsView.closeMoreActionsMenu();
|
|
145
|
+
expect(extensionMenuAvailable).to.equal(false, 'Extensions view should not contain Install from VSIX action');
|
|
146
|
+
|
|
147
|
+
// check Explorer context menu
|
|
148
|
+
await explorerView.openFileContextMenu(VSIX_FILE_NAME);
|
|
149
|
+
const contextMenuAvailable: boolean = await explorerView.isContextMenuItemVisible('Install Extension VSIX');
|
|
150
|
+
await explorerView.closeContextMenu();
|
|
151
|
+
expect(contextMenuAvailable).to.equal(false, 'Explorer context menu should not contain Install Extension VSIX action');
|
|
152
|
+
|
|
153
|
+
// verify default extensions are not auto-installed
|
|
154
|
+
await extensionsView.openExtensionsView();
|
|
155
|
+
const installedExtensions: string[] = await extensionsView.getInstalledExtensionNames();
|
|
156
|
+
|
|
157
|
+
Logger.debug(`Found installed extensions: ${installedExtensions.join(', ')}`);
|
|
158
|
+
|
|
159
|
+
for (const extensionName of DEFAULT_EXTENSIONS) {
|
|
160
|
+
const isInstalled: boolean = installedExtensions.some((installed: string): boolean =>
|
|
161
|
+
installed.toLowerCase().includes(extensionName.toLowerCase())
|
|
162
|
+
);
|
|
163
|
+
expect(isInstalled).to.equal(false, `Default VSIX extension "${extensionName}" should not be auto-installed`);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('Enable VSIX installation and create new workspace', async function (): Promise<void> {
|
|
168
|
+
// clean up current workspace
|
|
169
|
+
await dashboard.openDashboard();
|
|
170
|
+
await testWorkspaceUtil.deleteWorkspaceByName(workspaceName);
|
|
171
|
+
registerRunningWorkspace('');
|
|
172
|
+
|
|
173
|
+
// apply ConfigMaps that enable VSIX and set default extensions
|
|
174
|
+
applyConfigMap(CONFIG_FILES.ENABLE_VSIX);
|
|
175
|
+
applyConfigMap(CONFIG_FILES.DEFAULT_EXTENSIONS);
|
|
176
|
+
|
|
177
|
+
Logger.info('Waiting for new ConfigMap settings to take effect...');
|
|
178
|
+
await driverHelper.wait(30000);
|
|
179
|
+
|
|
180
|
+
// create new workspace
|
|
181
|
+
await workspaceHandlingTests.createAndOpenWorkspaceFromGitRepository(TEST_REPO_URL);
|
|
182
|
+
await workspaceHandlingTests.obtainWorkspaceNameFromStartingPage();
|
|
183
|
+
workspaceName = WorkspaceHandlingTests.getWorkspaceName();
|
|
184
|
+
registerRunningWorkspace(workspaceName);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('Wait workspace readiness', async function (): Promise<void> {
|
|
188
|
+
await projectAndFileTests.waitWorkspaceReadinessForCheCodeEditor();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('Verify default extension installation success notifications', async function (): Promise<void> {
|
|
192
|
+
const hasSuccessNotification: boolean = await notificationHandler.checkForNotification('Completed installing extension');
|
|
193
|
+
expect(hasSuccessNotification).to.be.true;
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test('Perform trust author dialog', async function (): Promise<void> {
|
|
197
|
+
await projectAndFileTests.performTrustAuthorDialog();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('Verify VSIX installation is enabled in UI and extensions are installed', async function (): Promise<void> {
|
|
201
|
+
// check Command Palette
|
|
202
|
+
await commandPalette.openCommandPalette();
|
|
203
|
+
await commandPalette.searchCommand('Install from VSIX');
|
|
204
|
+
const commandAvailable: boolean = await commandPalette.isCommandVisible('Extensions: Install from VSIX...');
|
|
205
|
+
await commandPalette.closeCommandPalette();
|
|
206
|
+
expect(commandAvailable).to.equal(true, 'Command Palette should contain Install from VSIX command');
|
|
207
|
+
|
|
208
|
+
// check Extensions view menu
|
|
209
|
+
await extensionsView.openExtensionsView();
|
|
210
|
+
await extensionsView.openMoreActionsMenu();
|
|
211
|
+
const extensionMenuAvailable: boolean = await extensionsView.isMoreActionsMenuItemVisible('Install from VSIX');
|
|
212
|
+
await extensionsView.closeMoreActionsMenu();
|
|
213
|
+
expect(extensionMenuAvailable).to.equal(true, 'Extensions view should contain Install from VSIX action');
|
|
214
|
+
|
|
215
|
+
// check Explorer context menu
|
|
216
|
+
await explorerView.openFileContextMenu(VSIX_FILE_NAME);
|
|
217
|
+
const contextMenuAvailable: boolean = await explorerView.isContextMenuItemVisible('Install Extension VSIX');
|
|
218
|
+
await explorerView.closeContextMenu();
|
|
219
|
+
expect(contextMenuAvailable).to.equal(true, 'Explorer context menu should contain Install Extension VSIX action');
|
|
220
|
+
|
|
221
|
+
// verify default extensions are auto-installed when VSIX enabled
|
|
222
|
+
await extensionsView.openExtensionsView();
|
|
223
|
+
const installedExtensions: string[] = await extensionsView.getInstalledExtensionNames();
|
|
224
|
+
|
|
225
|
+
Logger.debug(`Found installed extensions: ${installedExtensions.join(', ')}`);
|
|
226
|
+
|
|
227
|
+
for (const extensionName of DEFAULT_EXTENSIONS) {
|
|
228
|
+
const isInstalled: boolean = installedExtensions.some((installed: string): boolean =>
|
|
229
|
+
installed.toLowerCase().includes(extensionName.toLowerCase())
|
|
230
|
+
);
|
|
231
|
+
expect(isInstalled).to.equal(true, `Default VSIX extension "${extensionName}" should be auto-installed`);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
suiteTeardown('Clean up ConfigMaps and workspace', async function (): Promise<void> {
|
|
236
|
+
cleanupConfigMaps();
|
|
237
|
+
await dashboard.openDashboard();
|
|
238
|
+
await browserTabsUtil.closeAllTabsExceptCurrent();
|
|
239
|
+
await testWorkspaceUtil.stopAndDeleteWorkspaceByName(workspaceName);
|
|
240
|
+
registerRunningWorkspace('');
|
|
241
|
+
Logger.info('Cleanup completed');
|
|
242
|
+
});
|
|
243
|
+
});
|