diff --git a/tests/e2e/specs/dashboard-samples/RecommendedExtensions.spec.ts b/tests/e2e/specs/dashboard-samples/RecommendedExtensions.spec.ts index fe052148287..1daa06e27e2 100644 --- a/tests/e2e/specs/dashboard-samples/RecommendedExtensions.spec.ts +++ b/tests/e2e/specs/dashboard-samples/RecommendedExtensions.spec.ts @@ -205,8 +205,16 @@ async function getVisibleFilteredItemsAndCompareWithRecommended(recommendations: Logger.debug(`Found authors: ${allFoundRecommendedAuthors.join(', ')}`); Logger.debug(`Expected authors: ${recommendations.join(', ')}`); - const allFoundAuthorsAsSortedString: string = allFoundRecommendedAuthors.sort().toString(); - const allPublisherNamesAsSortedString: string = recommendations.sort().toString(); + // normalize authors for comparison: UI shows display names (e.g. "Red Hat"), + // while extensions.json contains publisher IDs (e.g. "redhat") + const allFoundAuthorsAsSortedString: string = allFoundRecommendedAuthors + .map((author: string): string => author.toLowerCase().replace(/\s+/g, '')) + .sort() + .toString(); + const allPublisherNamesAsSortedString: string = recommendations + .map((name: string): string => name.toLowerCase().replace(/\s+/g, '')) + .sort() + .toString(); Logger.debug(`Sorted found authors: ${allFoundAuthorsAsSortedString}`); Logger.debug(`Sorted expected authors: ${allPublisherNamesAsSortedString}`); @@ -232,10 +240,15 @@ async function getVisibleFilteredItemsAndCompareWithInstalled(recommendations: s allFoundRecommendedItems.map(async (item: ExtensionsViewItem): Promise => await item.getAuthor()) ); - const allFoundAuthorsAsSortedString: string = allFoundRecommendedAuthors.sort().toString(); - const allPublisherNamesAsSortString: string = recommendations.sort().toString(); + // normalize authors for comparison: UI shows display names (e.g. "Red Hat"), + // while extensions.json contains publisher IDs (e.g. "redhat") + const normalizedAuthors: string[] = allFoundRecommendedAuthors + .map((author: string): string => author.toLowerCase().replace(/\s+/g, '')) + .sort(); + const normalizedPublishers: string[] = recommendations.map((name: string): string => name.toLowerCase().replace(/\s+/g, '')).sort(); + // in some cases we can have installed not only recommended extensions with some samples (for example .Net) - return allFoundAuthorsAsSortedString.includes(allPublisherNamesAsSortString); + return normalizedPublishers.every((publisher: string): boolean => normalizedAuthors.includes(publisher)); } for (const sample of samples) { @@ -322,6 +335,9 @@ for (const sample of samples) { await projectAndFileTests.getProjectTreeItem(projectSection, pathToExtensionsListFileName, vsCodeFolderItemLevel) )?.select(); + // wait for the folder to expand and the file to appear in the project tree + await projectAndFileTests.waitProjectTreeItem(projectSection, extensionsListFileName, vsCodeFolderItemLevel + 1); + await ( await projectAndFileTests.getProjectTreeItem(projectSection, extensionsListFileName, vsCodeFolderItemLevel + 1) )?.select(); @@ -329,6 +345,9 @@ for (const sample of samples) { await new EditorView().openEditor(extensionsListFileName); await driverHelper.waitVisibility(webCheCodeLocators.Editor.inputArea); + // click on the editor input area to ensure it has focus before copying text + await driverHelper.waitAndClick(webCheCodeLocators.Editor.inputArea); + Logger.debug('Select and copy all text in the editor'); const text: string = await getText(); recommendedExtensions = JSON.parse(text.replace(/\/\*[\s\S]*?\*\/|(?<=[^:])\/\/.*|^\/\/.*/g, '').trim()); @@ -424,6 +443,7 @@ for (const sample of samples) { await driverHelper.wait(TIMEOUT_CONSTANTS.TS_EXPAND_PROJECT_TREE_ITEM_TIMEOUT); await findItem(extensionSection, '@installed '); } + expect(await getVisibleFilteredItemsAndCompareWithInstalled(publisherNames)).to.be.true; Logger.debug(`All recommended extensions were found by @installed filter: ---- ${publisherNames} ----`); }); diff --git a/tests/e2e/tests-library/ProjectAndFileTests.ts b/tests/e2e/tests-library/ProjectAndFileTests.ts index 1f6e0e27989..75306ed98d7 100644 --- a/tests/e2e/tests-library/ProjectAndFileTests.ts +++ b/tests/e2e/tests-library/ProjectAndFileTests.ts @@ -198,6 +198,32 @@ export class ProjectAndFileTests { return projectTreeItem; } + /** + * wait for a specific item to appear in the project tree by polling. + * useful after expanding a folder to wait until its children are rendered. + * @param projectSection ViewSection with project tree files. + * @param label Label of the item to wait for. + * @param itemLevel Depth level of the item in the tree. + */ + async waitProjectTreeItem(projectSection: ViewSection, label: string, itemLevel: number = 2): Promise { + Logger.debug(`waiting for "${label}" at level ${itemLevel}`); + + const timeout: number = TIMEOUT_CONSTANTS.TS_EXPAND_PROJECT_TREE_ITEM_TIMEOUT; + const polling: number = TIMEOUT_CONSTANTS.TS_SELENIUM_DEFAULT_POLLING; + const attempts: number = Math.ceil(timeout / polling); + + for (let i: number = 0; i < attempts; i++) { + const item: ViewItem | undefined = await projectSection.findItem(label, itemLevel); + if (item) { + Logger.debug(`"${label}" found after ${i + 1} attempt(s)`); + return; + } + await this.driverHelper.wait(polling); + } + + throw new Error(`Item "${label}" not found in the project tree at level ${itemLevel} after ${timeout}ms`); + } + /** * @returns {string} Branch name of cloned repository */