@progress/kendo-e2e
Version:
Kendo UI end-to-end test utilities.
236 lines • 12.3 kB
JavaScript
;
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