|
1 | 1 | import { Snapshot, Context, DiscoveryErrors } from "../types.js"; |
2 | | -import { scrollToBottomAndBackToTop, getRenderViewports, getRenderViewportsForOptions, validateCoordinates } from "./utils.js" |
| 2 | +import { scrollToBottomAndBackToTop, getRenderViewports, getRenderViewportsForOptions, validateCoordinates, resolveCustomCSS, parseCSSFile, validateCSSSelectors, generateCSSInjectionReport } from "./utils.js" |
3 | 3 | import { chromium, Locator } from "@playwright/test" |
4 | 4 | import constants from "./constants.js"; |
5 | 5 | import { updateLogContext } from '../lib/logger.js' |
6 | 6 | import NodeCache from 'node-cache'; |
| 7 | +import chalk from "chalk"; |
7 | 8 |
|
8 | 9 | const globalCache = new NodeCache({ stdTTL: 3600, checkperiod: 600 }); |
9 | 10 | const MAX_RESOURCE_SIZE = 15 * (1024 ** 2); // 15MB |
@@ -168,6 +169,20 @@ export async function prepareSnapshot(snapshot: Snapshot, ctx: Context): Promise |
168 | 169 | processedOptions.useExtendedViewport = true; |
169 | 170 | } |
170 | 171 |
|
| 172 | + try { |
| 173 | + if (options?.customCSS) { |
| 174 | + const resolvedCSS = resolveCustomCSS(options.customCSS, '', ctx.log); |
| 175 | + processedOptions.customCSS = resolvedCSS; |
| 176 | + ctx.log.debug('Using per-snapshot customCSS (overriding config)'); |
| 177 | + } else if (ctx.config.customCSS) { |
| 178 | + processedOptions.customCSS = ctx.config.customCSS; |
| 179 | + ctx.log.debug('Using config customCSS'); |
| 180 | + } |
| 181 | + } catch (error: any) { |
| 182 | + ctx.log.warn(`customCSS warning: ${error.message}`); |
| 183 | + chalk.yellow(`[SmartUI] warning: ${error.message}`); |
| 184 | + } |
| 185 | + |
171 | 186 | processedOptions.allowedAssets = ctx.config.allowedAssets; |
172 | 187 | processedOptions.selectors = selectors; |
173 | 188 |
|
@@ -611,6 +626,19 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): |
611 | 626 | processedOptions.useExtendedViewport = true; |
612 | 627 | } |
613 | 628 |
|
| 629 | + try { |
| 630 | + if (options?.customCSS) { |
| 631 | + const resolvedCSS = resolveCustomCSS(options.customCSS, '', ctx.log); |
| 632 | + processedOptions.customCSS = resolvedCSS; |
| 633 | + } else if (ctx.config.customCSS) { |
| 634 | + processedOptions.customCSS = ctx.config.customCSS; |
| 635 | + } |
| 636 | + } catch (error: any) { |
| 637 | + optionWarnings.add(`${error.message}`); |
| 638 | + } |
| 639 | + |
| 640 | + ctx.log.debug(`Processed options: ${JSON.stringify(processedOptions)}`); |
| 641 | + |
614 | 642 | // process for every viewport |
615 | 643 | let navigated: boolean = false; |
616 | 644 | let previousDeviceType: string | null = null; |
@@ -896,6 +924,24 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): |
896 | 924 | await checkPending(); |
897 | 925 | } |
898 | 926 |
|
| 927 | + // Validate and report CSS injection after selector processing |
| 928 | + if (processedOptions.customCSS) { |
| 929 | + try { |
| 930 | + const cssRules = parseCSSFile(processedOptions.customCSS); |
| 931 | + const validationResult = await validateCSSSelectors(page, cssRules, ctx.log); |
| 932 | + const report = generateCSSInjectionReport(validationResult, ctx.log); |
| 933 | + |
| 934 | + if (validationResult.failedSelectors.length > 0) { |
| 935 | + validationResult.failedSelectors.forEach(selector => { |
| 936 | + optionWarnings.add(`customCSS selector not found: ${selector}`); |
| 937 | + }); |
| 938 | + } |
| 939 | + } catch (error: any) { |
| 940 | + ctx.log.warn(`CSS validation failed: ${error.message}`); |
| 941 | + optionWarnings.add(`CSS validation error: ${error.message}`); |
| 942 | + } |
| 943 | + } |
| 944 | + |
899 | 945 |
|
900 | 946 | let hasBrowserErrors = false; |
901 | 947 | for (let browser in discoveryErrors.browsers) { |
|
0 commit comments