UNPKG

@progress/kendo-e2e

Version:

Kendo UI end-to-end test utilities.

236 lines 12.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Crawler = void 0; const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); const browser_1 = require("../selenium/browser"); /** * Crawler class that extends Browser to provide crawling functionality for testing links and components. */ class Crawler extends browser_1.Browser { /** * Scans the current page to find and log custom component tags. * * It extracts the component name from the URL path, constructs a CSS selector * using the `kendo-` prefix, and optionally checks for a singular form. * * Logs a warning if no matching elements are found or if a search fails. * * Special case: if the extracted name is "general", it uses the previous path segment instead. * * @param customTags - Optional array of custom CSS selectors to search for instead of URL-based extraction * * @example * ```typescript * // Use URL-based extraction (default behavior) * await crawler.crawlForTags(); * * // Use custom tags * await crawler.crawlForTags(['kendo-grid', 'kendo-chart']); * * // Use complex selectors * await crawler.crawlForTags(['kendo-grid .k-grid-header', 'kendo-button[type="submit"]']); * ``` */ crawlForTags(customTags) { return __awaiter(this, void 0, void 0, function* () { const currentUrl = yield this.getCurrentUrl(); let cssSelector; if (customTags && customTags.length > 0) { // Use provided custom tags cssSelector = customTags.join(', '); console.log(`Using Custom Tags: ${cssSelector}`); } else { // Extract component name from URL (existing logic) const segments = currentUrl.split('/').filter(Boolean); // remove empty parts let extractedComponent = segments[segments.length - 2]; // default: second-to-last if (extractedComponent === 'general') { extractedComponent = segments[segments.length - 3]; // one level up } const expectedTag = `kendo-${extractedComponent}`; const expectedTagSingular = extractedComponent.endsWith('s') ? `kendo-${extractedComponent.slice(0, -1)}` : null; cssSelector = expectedTag; if (expectedTagSingular) { cssSelector = `${expectedTag}, ${expectedTagSingular}`; } } try { const elements = yield this.findAll(browser_1.By.css(cssSelector)); if (elements.length === 0) { console.warn(`Warning during search for tags: No elements found with tags ${cssSelector} on page ${currentUrl}`); } else { console.log(`Successful search for tags: Found ${elements.length} elements with tags ${cssSelector} on page ${currentUrl}`); } } catch (error) { console.warn(`Error during search for tags: Failed to find elements with tags ${cssSelector}:`, error); } }); } /** * Crawls all link elements (`<a href>`) on the current page, opens each link in a new tab, * checks for console errors, and optionally verifies component tags. * * Logs detailed information, including errors and successes, to a timestamped log file. * * Ensures browser cleanup by closing new tabs and returning to the original one after each link. * * @param crawlForTags - If true, also runs `crawlForTags()` on each visited link. * @param customTags - Optional array of custom CSS selectors to pass to `crawlForTags()` method. * * @example * ```typescript * beforeAll(async () => { * crawler = new Crawler(); * await crawler.navigateTo('http://localhost:4200/'); * }); * * afterAll(async () => { * await crawler.close(); * }); * * it('crawl all links with URL-based tag detection', async () => { * await crawler.crawlForErrors(); * }); * * it('crawl all links with custom tags', async () => { * await crawler.crawlForErrors(true, ['kendo-grid', 'kendo-button']); * }); * * it('crawl all links without tag checking', async () => { * await crawler.crawlForErrors(false); * }); * ``` */ crawlForErrors() { return __awaiter(this, arguments, void 0, function* (crawlForTags = true, customTags) { // Create log file with timestamp const now = new Date(); const timestamp = now.toISOString().slice(0, 19).replace(/:/g, '-').replace('T', '-'); const logFileName = `crawl-log-${timestamp}.txt`; const logFilePath = path_1.default.join(process.cwd(), logFileName); // Helper function to log both to console and file const logToFile = (message) => { const timestampedMessage = `[${new Date().toISOString()}] ${message}\n`; console.log(message); fs_1.default.appendFileSync(logFilePath, timestampedMessage); }; logToFile(`Starting crawl for errors at ${new Date().toISOString()}`); logToFile(`Log file: ${logFileName}`); logToFile(`Crawl for tags enabled: ${crawlForTags}`); expect(yield this.isVisible(browser_1.By.css("a[href]"))).toBe(true); yield this.sleep(500); const allLinksElements = (yield this.findAll(browser_1.By.css("a[href]"))); logToFile(`Found ${allLinksElements.length} links to process.`); let processedCount = 0; let successCount = 0; let errorCount = 0; for (const linkElement of allLinksElements) { processedCount++; try { // Get link href for logging const linkHref = yield linkElement.getAttribute('href'); logToFile(`\n--- Processing link ${processedCount}/${allLinksElements.length}: ${linkHref} ---`); // Get current window handles before clicking const initialWindowHandles = yield this.driver.getAllWindowHandles(); const currentWindowHandle = yield this.driver.getWindowHandle(); yield this.click(linkElement); yield this.sleep(10); // Check if a new tab/window was opened const newWindowHandles = yield this.driver.getAllWindowHandles(); if (newWindowHandles.length > initialWindowHandles.length) { // New tab was opened, switch to it const newWindowHandle = newWindowHandles.find(handle => !initialWindowHandles.includes(handle)); if (newWindowHandle) { yield this.driver.switchTo().window(newWindowHandle); // Wait for the page to load by finding the body element yield this.find(browser_1.By.css('body')); // Get the new tab's URL for logging const newTabUrl = yield this.getCurrentUrl(); logToFile(`New tab opened: ${newTabUrl}`); // Check for console errors in the new tab const errorLogs = yield this.getErrorLogs(); if (errorLogs.length > 0) { logToFile(`New console errors found (${errorLogs.length}):`); errorLogs.forEach((error, index) => { logToFile(` > Error ${index + 1}: ${error}`); }); } else { logToFile('No console errors found!'); } expect(errorLogs).toEqual([]); if (crawlForTags) { // Optionally crawl for tags in the new tab based on the new tab's URL logToFile('Crawling for tags...'); yield this.crawlForTags(customTags); } // Close the new tab and switch back to the original yield this.driver.close(); yield this.driver.switchTo().window(currentWindowHandle); logToFile(`Link ${processedCount} processed successfully`); successCount++; } } else { const warningMsg = "No new window opened after clicking the link, continuing with next link."; logToFile(`WARNING: ${warningMsg}`); console.warn(warningMsg); } } catch (error) { errorCount++; const errorMsg = `Error processing link ${processedCount}: ${error instanceof Error ? error.message : String(error)}`; logToFile(`ERROR: ${errorMsg}`); console.warn(errorMsg); // Ensure we're back on the original tab even if something went wrong try { const currentHandles = yield this.driver.getAllWindowHandles(); // If we have multiple windows, close any extra ones and return to the first if (currentHandles.length > 1) { logToFile('Cleaning up extra windows...'); for (const handle of currentHandles) { if (handle !== currentHandles[0]) { yield this.driver.switchTo().window(handle); yield this.driver.close(); } } yield this.driver.switchTo().window(currentHandles[0]); logToFile('Cleanup completed, returned to original tab'); } } catch (cleanupError) { const cleanupMsg = `Error during cleanup: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`; logToFile(`CLEANUP ERROR: ${cleanupMsg}`); console.warn(cleanupMsg); } // Continue with the next link to avoid failing the entire test continue; } } // Log final summary logToFile(`\n=== CRAWL SUMMARY ===`); logToFile(`Total links processed: ${processedCount}`); logToFile(`Successful: ${successCount}`); logToFile(`Errors: ${errorCount}`); logToFile(`Crawl completed at ${new Date().toISOString()}`); logToFile(`Log saved to: ${logFilePath}`); }); } } exports.Crawler = Crawler; //# sourceMappingURL=crawler.js.map