@@ -28,10 +28,10 @@ function assertSnapshotContent(actual: string, expected: string): void {
2828 *
2929 * Note: karma.conf.ts implements the server logic (see snapshotTesterFactory).
3030 */
31- export class SnapshotTester {
31+ class BaseSnapshotTester {
3232 static #updateMode: boolean | null = null ;
3333
34- # snapshotPath: string ;
34+ protected snapshotPath : string ;
3535 #expected = new Map < string , string > ( ) ;
3636 #actual = new Map < string , string > ( ) ;
3737 #anyFailures = false ;
@@ -41,27 +41,18 @@ export class SnapshotTester {
4141 // out/Default/gen/third_party/devtools-frontend/src/front_end/testing/SnapshotTester.test.js?8ee4f2b123e221040a4aa075a28d0e5b41d3d3ed
4242 // ->
4343 // front_end/testing/SnapshotTester.snapshot.txt
44- this . # snapshotPath =
44+ this . snapshotPath =
4545 meta . url . substring ( meta . url . lastIndexOf ( 'front_end' ) ) . replace ( '.test.js' , '.snapshot.txt' ) . split ( '?' ) [ 0 ] ;
4646 }
4747
4848 async load ( ) {
49- if ( SnapshotTester . #updateMode === null ) {
50- SnapshotTester . #updateMode = await this . # checkIfUpdateMode( ) ;
49+ if ( BaseSnapshotTester . #updateMode === null ) {
50+ BaseSnapshotTester . #updateMode = await this . checkIfUpdateMode ( ) ;
5151 }
52-
53- const url = new URL ( '/snapshot' , import . meta. url ) ;
54- url . searchParams . set ( 'snapshotPath' , this . #snapshotPath) ;
55- const response = await fetch ( url ) ;
56- if ( response . status === 404 ) {
57- console . warn ( `Snapshot file not found: ${ this . #snapshotPath} . Will create it for you.` ) ;
58- return ;
52+ const content = await this . loadSnapshot ( this . snapshotPath ) ;
53+ if ( content ) {
54+ this . #parseSnapshotFileContent( content ) ;
5955 }
60- if ( response . status !== 200 ) {
61- throw new Error ( 'failed to load snapshot' ) ;
62- }
63-
64- this . #parseSnapshotFileContent( await response . text ( ) ) ;
6556 }
6657
6758 assert ( context : Mocha . Context , actual : string ) {
@@ -81,7 +72,7 @@ export class SnapshotTester {
8172 const expected = this . #expected. get ( title ) ;
8273 if ( expected === undefined ) {
8374 this . #newTests = true ;
84- if ( SnapshotTester . #updateMode) {
75+ if ( BaseSnapshotTester . #updateMode) {
8576 return ;
8677 }
8778
@@ -93,22 +84,12 @@ export class SnapshotTester {
9384 const isDifferent = actual !== expected ;
9485 if ( isDifferent ) {
9586 this . #anyFailures = true ;
96- if ( ! SnapshotTester . #updateMode) {
87+ if ( ! BaseSnapshotTester . #updateMode) {
9788 assertSnapshotContent ( actual , expected ) ;
9889 }
9990 }
10091 }
10192
102- async #postUpdate( ) : Promise < void > {
103- const url = new URL ( '/update-snapshot' , import . meta. url ) ;
104- url . searchParams . set ( 'snapshotPath' , this . #snapshotPath) ;
105- const content = this . #serializeSnapshotFileContent( ) ;
106- const response = await fetch ( url , { method : 'POST' , body : content } ) ;
107- if ( response . status !== 200 ) {
108- throw new Error ( `Unable to update snapshot ${ url } ` ) ;
109- }
110- }
111-
11293 async finish ( ) {
11394 let didAnyTestNotRun = false ;
11495 for ( const title of this . #expected. keys ( ) ) {
@@ -124,8 +105,8 @@ export class SnapshotTester {
124105 }
125106
126107 // If the update flag is on, post any and all changes (failures, new tests, removals).
127- if ( SnapshotTester . #updateMode) {
128- await this . # postUpdate( ) ;
108+ if ( BaseSnapshotTester . #updateMode) {
109+ await this . postUpdate ( ) ;
129110 return ;
130111 }
131112
@@ -137,7 +118,20 @@ export class SnapshotTester {
137118 }
138119 }
139120
140- #serializeSnapshotFileContent( ) : string {
121+ #parseSnapshotFileContent( content : string ) : void {
122+ const sections = content . split ( '=== end content' ) . map ( s => s . trim ( ) ) . filter ( Boolean ) ;
123+ for ( const section of sections ) {
124+ const [ titleField , contentField , ...contentLines ] = section . split ( '\n' ) ;
125+ const title = titleField . replace ( 'Title:' , '' ) . trim ( ) ;
126+ if ( contentField !== 'Content:' ) {
127+ throw new Error ( 'unexpected snapshot file' ) ;
128+ }
129+ const content = contentLines . join ( '\n' ) . trim ( ) ;
130+ this . #expected. set ( title , content ) ;
131+ }
132+ }
133+
134+ protected serializeSnapshotFileContent ( ) : string {
141135 if ( ! this . #actual. size ) {
142136 return '' ;
143137 }
@@ -153,22 +147,81 @@ export class SnapshotTester {
153147 return lines . join ( '\n' ) . trim ( ) + '\n' ;
154148 }
155149
156- #parseSnapshotFileContent( content : string ) : void {
157- const sections = content . split ( '=== end content' ) . map ( s => s . trim ( ) ) . filter ( Boolean ) ;
158- for ( const section of sections ) {
159- const [ titleField , contentField , ...contentLines ] = section . split ( '\n' ) ;
160- const title = titleField . replace ( 'Title:' , '' ) . trim ( ) ;
161- if ( contentField !== 'Content:' ) {
162- throw new Error ( 'unexpected snapshot file' ) ;
163- }
164- const content = contentLines . join ( '\n' ) . trim ( ) ;
165- this . #expected. set ( title , content ) ;
166- }
150+ protected async checkIfUpdateMode ( ) : Promise < boolean > {
151+ return false ;
152+ }
153+
154+ protected async postUpdate ( ) : Promise < void > {
155+ throw new Error ( `Not implemented` ) ;
167156 }
168157
169- async #checkIfUpdateMode( ) : Promise < boolean > {
158+ protected async loadSnapshot ( _snapshotPath : string ) : Promise < string | undefined > {
159+ throw new Error ( 'not implemented' ) ;
160+ }
161+ }
162+
163+ class WebSnapshotTester extends BaseSnapshotTester {
164+ protected override async checkIfUpdateMode ( ) : Promise < boolean > {
170165 const response = await fetch ( '/snapshot-update-mode' ) ;
171166 const data = await response . json ( ) ;
172167 return data . updateMode === true ;
173168 }
169+
170+ protected override async postUpdate ( ) : Promise < void > {
171+ const url = new URL ( '/update-snapshot' , import . meta. url ) ;
172+ url . searchParams . set ( 'snapshotPath' , this . snapshotPath ) ;
173+ const content = this . serializeSnapshotFileContent ( ) ;
174+ const response = await fetch ( url , { method : 'POST' , body : content } ) ;
175+ if ( response . status !== 200 ) {
176+ throw new Error ( `Unable to update snapshot ${ url } ` ) ;
177+ }
178+ }
179+
180+ protected override async loadSnapshot ( snapshotPath : string ) : Promise < string | undefined > {
181+ const url = new URL ( '/snapshot' , import . meta. url ) ;
182+ url . searchParams . set ( 'snapshotPath' , snapshotPath ) ;
183+ const response = await fetch ( url ) ;
184+ if ( response . status === 404 ) {
185+ console . warn ( `Snapshot file not found: ${ snapshotPath } . Will create it for you.` ) ;
186+ return ;
187+ }
188+ if ( response . status !== 200 ) {
189+ throw new Error ( 'failed to load snapshot' ) ;
190+ }
191+ return await response . text ( ) ;
192+ }
193+ }
194+
195+ class NodeSnapshotTester extends BaseSnapshotTester {
196+ protected override async checkIfUpdateMode ( ) : Promise < boolean > {
197+ // cannot update in node mode yet.
198+ return false ;
199+ }
200+
201+ protected override async postUpdate ( ) : Promise < void > {
202+ const content = this . serializeSnapshotFileContent ( ) ;
203+ // @ts -expect-error no node types here.
204+ const fs = await import ( 'node:fs/promises' ) ;
205+ await fs . writeFile ( await this . #getSnapshotPath( this . snapshotPath ) , content ) ;
206+ }
207+
208+ protected override async loadSnapshot ( snapshotPath : string ) : Promise < string | undefined > {
209+ // @ts -expect-error no node types here.
210+ const fs = await import ( 'node:fs/promises' ) ;
211+ return await fs . readFile ( await this . #getSnapshotPath( snapshotPath ) , 'utf-8' ) ;
212+ }
213+
214+ async #getSnapshotPath( snapshotPath : string ) : Promise < string > {
215+ // @ts -expect-error no node types here.
216+ const path = await import ( 'node:path' ) ;
217+ // @ts -expect-error no ESM types here.
218+ const SOURCE_ROOT = path . join ( import . meta. dirname , '..' , '..' ) ;
219+ return path . join ( SOURCE_ROOT , snapshotPath ) ;
220+ }
174221}
222+
223+ export type SnapshotTester = NodeSnapshotTester | WebSnapshotTester ;
224+
225+ const SnapshotTesterValue = ( typeof window === 'undefined' ) ? NodeSnapshotTester : WebSnapshotTester ;
226+
227+ export { SnapshotTesterValue as SnapshotTester } ;
0 commit comments