1+ import fs from 'fs' ;
2+ import path from 'path' ;
13import { Browser , BrowserContext , Page } from "@playwright/test"
24import { Context } from "../types.js"
35import * as utils from "./utils.js"
@@ -133,4 +135,78 @@ export async function captureScreenshots(ctx: Context): Promise<Record<string,a
133135 utils . delDir ( 'screenshots' ) ;
134136
135137 return { capturedScreenshots, output } ;
136- }
138+ }
139+
140+ function getImageDimensions ( filePath : string ) : { width : number , height : number } | null {
141+ const buffer = fs . readFileSync ( filePath ) ;
142+ let width , height ;
143+
144+ if ( buffer . toString ( 'hex' , 0 , 2 ) === 'ffd8' ) {
145+ // JPEG
146+ let offset = 2 ;
147+ while ( offset < buffer . length ) {
148+ const marker = buffer . toString ( 'hex' , offset , offset + 2 ) ;
149+ offset += 2 ;
150+ const length = buffer . readUInt16BE ( offset ) ;
151+ if ( marker === 'ffc0' || marker === 'ffc2' ) {
152+ height = buffer . readUInt16BE ( offset + 3 ) ;
153+ width = buffer . readUInt16BE ( offset + 5 ) ;
154+ return { width, height } ;
155+ }
156+ offset += length ;
157+ }
158+ } else if ( buffer . toString ( 'hex' , 1 , 4 ) === '504e47' ) {
159+ // PNG
160+ width = buffer . readUInt32BE ( 16 ) ;
161+ height = buffer . readUInt32BE ( 20 ) ;
162+ return { width, height } ;
163+ }
164+
165+ return null ;
166+ }
167+
168+ export async function uploadScreenshots ( ctx : Context ) : Promise < void > {
169+ const allowedExtensions = ctx . options . fileExtension . map ( ext => `.${ ext . trim ( ) . toLowerCase ( ) } ` ) ;
170+
171+ async function processDirectory ( directory : string , relativePath : string = '' ) : Promise < void > {
172+ const files = fs . readdirSync ( directory ) ;
173+
174+ for ( let file of files ) {
175+ const filePath = path . join ( directory , file ) ;
176+ const stat = fs . statSync ( filePath ) ;
177+ const relativeFilePath = path . join ( relativePath , file ) ;
178+
179+ if ( stat . isDirectory ( ) && ctx . options . ignorePattern . includes ( relativeFilePath ) ) {
180+ continue ; // Skip this path
181+ }
182+
183+ if ( stat . isDirectory ( ) ) {
184+ await processDirectory ( filePath , relativeFilePath ) ; // Recursively process subdirectory
185+ } else {
186+ let fileExtension = path . extname ( file ) . toLowerCase ( ) ;
187+ if ( allowedExtensions . includes ( fileExtension ) ) {
188+ let ssId = relativeFilePath ;
189+ if ( ctx . options . stripExtension ) {
190+ ssId = path . join ( relativePath , path . basename ( file , fileExtension ) ) ;
191+ }
192+
193+ let viewport = 'default' ;
194+
195+ if ( ! ctx . options . ignoreResolutions ) {
196+ const dimensions = getImageDimensions ( filePath ) ;
197+ if ( ! dimensions ) {
198+ throw new Error ( `Unable to determine dimensions for image: ${ filePath } ` ) ;
199+ }
200+ const width = dimensions . width ;
201+ const height = dimensions . height ;
202+ viewport = `${ width } x${ height } ` ;
203+ }
204+
205+ await ctx . client . uploadScreenshot ( ctx . build , filePath , ssId , 'default' , viewport , ctx . log ) ;
206+ }
207+ }
208+ }
209+ }
210+
211+ await processDirectory ( ctx . uploadFilePath ) ;
212+ }
0 commit comments