1+ /* eslint-disable no-unused-vars */
2+ /* eslint-disable @typescript-eslint/no-explicit-any */
3+ /* eslint-disable unicorn/prefer-module */
14// eslint-disable-next-line unicorn/filename-case
25/* eslint-disable no-console */
3- /* eslint-disable import/no-extraneous-dependencies */
46/**
57 * Copyright (c) Microsoft Corporation.
68 *
@@ -20,11 +22,11 @@ import {test as base, type Page, _electron} from '@playwright/test'
2022import { downloadAndUnzipVSCode } from '@vscode/test-electron/out/download'
2123
2224export { expect } from '@playwright/test'
23- import { spawnSync } from 'child_process'
2425import dedent from 'dedent'
25- import * as fs from 'fs'
26- import * as os from 'os'
27- import * as path from 'path'
26+ import { SpawnSyncOptionsWithBufferEncoding , spawnSync } from 'node:child_process'
27+ import * as fs from 'node:fs'
28+ import * as os from 'node:os'
29+ import * as path from 'node:path'
2830
2931export type TestOptions = {
3032 vscodeVersion : string
@@ -39,13 +41,20 @@ type TestFixtures = TestOptions & {
3941export const test = base . extend < TestFixtures > ( {
4042 vscodeVersion : [ 'insiders' , { option : true } ] ,
4143 async workbox ( { vscodeVersion, createProject, createTempDir} , use ) {
44+ const titleSlug = slugify ( test . info ( ) . title )
4245 const defaultCachePath = await createTempDir ( )
4346 const vscodePath = await downloadAndUnzipVSCode ( vscodeVersion )
44- await fs . promises . cp (
45- '/Users/mmkal/.vscode/extensions/dbaeumer.vscode-eslint-2.4.2' ,
46- path . join ( defaultCachePath , 'extensions' , 'dbaeumer.vscode-eslint-2.4.2' ) ,
47- { recursive : true } ,
48- )
47+ const systemVscodeFolder = path . join ( os . homedir ( ) , '.vscode' )
48+ const systemExtensionsDirectory = path . join ( systemVscodeFolder , 'extensions' )
49+ const gifskiPath = path . join ( os . homedir ( ) , 'Downloads/gifski-1.32.0/mac/gifski' )
50+ const systemExtensionNames = await fs . promises . readdir ( systemExtensionsDirectory )
51+
52+ const systemEslintExtension = systemExtensionNames . find ( child => child . startsWith ( 'dbaeumer.vscode-eslint' ) ) !
53+ const systemEslintExtensionPath = path . join ( systemExtensionsDirectory , systemEslintExtension )
54+
55+ await fs . promises . cp ( systemEslintExtensionPath , path . join ( defaultCachePath , 'extensions' , systemEslintExtension ) , {
56+ recursive : true ,
57+ } )
4958 const projectPath = await createProject ( )
5059 const electronApp = await _electron . launch ( {
5160 executablePath : vscodePath ,
@@ -66,21 +75,63 @@ export const test = base.extend<TestFixtures>({
6675 `--user-data-dir=${ path . join ( defaultCachePath , 'user-data' ) } ` ,
6776 projectPath ,
6877 ] ,
69- recordVideo : { dir : 'test-results/videos' } ,
78+ recordVideo : {
79+ dir : `test-results/videos/${ titleSlug } ` ,
80+ // 942:707 is the default aspect ratio of the electron runner and I don't know how to change it.
81+ size : { width : 942 * 1.5 , height : 707 * 1.5 } ,
82+ } ,
7083 } )
7184 const workbox = await electronApp . firstWindow ( )
7285 await workbox . context ( ) . tracing . start ( { screenshots : true , snapshots : true , title : test . info ( ) . title } )
86+
87+ const exec = ( command : string , options ?: SpawnSyncOptionsWithBufferEncoding ) =>
88+ spawnSync ( command , { cwd : projectPath , stdio : 'inherit' , shell : true , ...options } )
89+
90+ if ( process . env . QUICKTIME_RECORD ) {
91+ exec ( 'osascript e2e/applescript/start-recording.applescript' , { cwd : process . cwd ( ) } )
92+ }
93+
7394 await use ( workbox )
95+
96+ let videoPath : string | undefined
97+ if ( process . env . QUICKTIME_RECORD ) {
98+ const before = new Date ( )
99+ exec ( 'osascript e2e/applescript/stop-recording.applescript' , { cwd : process . cwd ( ) } )
100+ const desktop = path . join ( os . homedir ( ) , 'Desktop' )
101+ await new Promise ( r => setTimeout ( r , 3000 ) ) // give it a few seconds to save
102+ const after = new Date ( )
103+ const desktopMovFiles = fs
104+ . readdirSync ( desktop )
105+ . filter ( f => f . endsWith ( '.mov' ) )
106+ . map ( f => {
107+ const filepath = path . join ( desktop , f )
108+ return { filepath, ctime : fs . statSync ( filepath ) . ctime }
109+ } )
110+ . filter ( f => Date . now ( ) - f . ctime . getTime ( ) < 1000 * 60 * 60 )
111+ . sort ( ( a , b ) => b . ctime . getTime ( ) - a . ctime . getTime ( ) )
112+ const likelyFiles = desktopMovFiles . filter ( f => f . ctime > before && f . ctime < after )
113+ if ( likelyFiles . length !== 1 ) {
114+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
115+ throw new Error (
116+ `Expected one file to be created between ${ before . toISOString ( ) } and ${ after . toISOString ( ) } , got ${ JSON . stringify ( desktopMovFiles , null , 2 ) } ` ,
117+ )
118+ }
119+ videoPath = likelyFiles [ 0 ] . filepath
120+ }
121+
74122 const tracePath = test . info ( ) . outputPath ( 'trace.zip' )
75123 await workbox . context ( ) . tracing . stop ( { path : tracePath } )
76124 test . info ( ) . attachments . push ( { name : 'trace' , path : tracePath , contentType : 'application/zip' } )
77125 await electronApp . close ( )
78126 const logPath = path . join ( defaultCachePath , 'user-data' )
79127 const video = workbox . video ( )
80128 if ( video ) {
81- // await workbox.video()?.saveAs(path.join(process.cwd(), 'videos', slugify(test.info().title) + '.webm'))
82- const exec = ( command : string ) => spawnSync ( command , { cwd : projectPath , stdio : 'inherit' , shell : true } )
83- exec ( `ffmpeg -y -i ${ await video . path ( ) } -pix_fmt rgb24 ${ process . cwd ( ) } /gifs/${ slugify ( test . info ( ) . title ) } .gif` )
129+ videoPath ||= await video . path ( )
130+ const targetVideoPath = path . join ( process . cwd ( ) , 'test-results' , 'videos' , titleSlug + path . extname ( videoPath ) )
131+ await fs . promises . cp ( videoPath , targetVideoPath )
132+ exec (
133+ `${ gifskiPath } --fps 32 ${ targetVideoPath } -o ${ path . join ( process . cwd ( ) , 'gifs' , titleSlug + '.gif' ) } --quality 100` ,
134+ )
84135 }
85136
86137 if ( fs . existsSync ( logPath ) ) {
@@ -102,57 +153,73 @@ export const test = base.extend<TestFixtures>({
102153 fs . writeFileSync ( fullpath , content )
103154 }
104155
105- // exec(`npm init playwright@latest --yes -- --quiet --browser=chromium --gha --install-deps`)
156+ const editJson = ( name : string , edit : ( json : unknown ) => void ) => {
157+ const fullpath = path . join ( projectPath , name )
158+ if ( ! fs . existsSync ( fullpath ) ) {
159+ fs . mkdirSync ( path . dirname ( fullpath ) , { recursive : true } )
160+ fs . cpSync ( path . join ( __dirname , '..' , name ) , fullpath )
161+ }
162+ const json = JSON . parse ( fs . readFileSync ( fullpath , 'utf8' ) ) as { }
163+ edit ( json )
164+ fs . writeFileSync ( fullpath , JSON . stringify ( json , null , 2 ) )
165+ }
166+
106167 exec ( 'npm init -y' )
107- exec ( `pnpm install eslint eslint-plugin-codegen eslint-plugin-mmkal typescript ts-node --save-dev` )
168+ exec ( `pnpm install eslint@8 react@18 @types/react typescript tsx --save-dev` )
108169
109- write ( 'tsconfig.json' , fs . readFileSync ( path . join ( __dirname , '..' , 'tsconfig.json' ) , 'utf8' ) )
170+ editJson ( 'tsconfig.json' , ( json : any ) => {
171+ json . compilerOptions . jsx = 'react'
172+ json . include . push ( 'node_modules/eslint-plugin-codegen' )
173+ } )
110174 write (
111- '.eslintrc.js' ,
112- // parserOptions: {project: null} // this is more of an eslint-plugin-mmkal thing but typescript-eslint doesn't like the processor I've got
113- // also, need to not pass fatal messages in the processor to eslint-plugin-markdown
175+ 'eslint.config.js' ,
114176 dedent `
115- module.exports = {
116- ...require('eslint-plugin-mmkal').getRecommended(),
117- parserOptions: {project: null},
118- plugins: ['codegen'],
119- extends: ['plugin:codegen/recommended'],
120- rules: {
121- 'mmkal/codegen/codegen': 'off',
122- 'codegen/codegen': 'warn',
123- },
124- }
177+ module.exports = [
178+ ...require(${ JSON . stringify ( path . join ( __dirname , '..' , 'eslint.config.js' ) ) } )
179+ .map(({rules, ...cfg}) => ({
180+ ...cfg,
181+ }))
182+ .filter(cfg => Object.keys(cfg).length > 0),
183+ {rules: {'codegen/codegen': 'warn'}},
184+ ]
125185 ` ,
126186 )
127187
128188 write ( 'src/barrel/a.ts' , 'export const a = 1' )
129189 write ( 'src/barrel/b.ts' , 'export const b = 1' )
130190 write ( 'src/barrel/index.ts' , '' )
131191 write ( 'src/custom/index.ts' , '' )
132- write ( 'README.md' , '' )
192+ write ( 'src/custom/component.tsx' , '' )
193+ write ( 'README.md' , 'Sample project' )
133194
195+ // use local eslint-plugin-codegen by inserting typescript directly into node_modules
196+ editJson ( 'package.json' , ( json : any ) => ( json . devDependencies [ 'eslint-plugin-codegen' ] = '*' ) )
134197 write (
135- '.vscode/settings.json' ,
136- fs
137- . readFileSync ( path . join ( __dirname , '../.vscode/settings.json' ) )
138- . toString ( )
139- . replace ( '{' , '{\n "editor.autoClosingBrackets": "never",' )
140- . replace ( '{' , '{\n "editor.autoIndent": "none",' )
141- . replace ( '{' , '{\n "editor.insertSpaces": false,' ) ,
198+ 'node_modules/eslint-plugin-codegen/index.ts' ,
199+ `export * from ${ JSON . stringify ( path . join ( __dirname , '..' , 'src' ) ) } ` ,
142200 )
143201
202+ editJson ( '.vscode/settings.json' , ( json : any ) => {
203+ json [ 'editor.inlineSuggest.enabled' ] = false
204+ json [ 'editor.autoClosingBrackets' ] = 'never'
205+ json [ 'editor.autoClosingQuotes' ] = 'never'
206+ json [ 'editor.autoIndent' ] = 'none'
207+ json [ 'editor.insertSpaces' ] = false
208+ json [ 'eslint.experimental.useFlatConfig' ] = true
209+ } )
210+
144211 return projectPath
145212 } )
146213 } ,
147214 // eslint-disable-next-line no-empty-pattern
148215 async createTempDir ( { } , use ) {
149- const tempDirs : string [ ] = [ ]
216+ const temporaryDirectories : string [ ] = [ ]
150217 await use ( async ( ) => {
151- const tempDir = await fs . promises . realpath ( await fs . promises . mkdtemp ( path . join ( os . tmpdir ( ) , 'pwtest-' ) ) )
152- tempDirs . push ( tempDir )
153- return tempDir
218+ const tempo = await fs . promises . realpath ( await fs . promises . mkdtemp ( path . join ( os . tmpdir ( ) , 'pwtest-' ) ) )
219+ temporaryDirectories . push ( tempo )
220+ return tempo
154221 } )
155- for ( const tempDir of tempDirs ) await fs . promises . rm ( tempDir , { recursive : true } )
222+ for ( const tempo of temporaryDirectories ) await fs . promises . rm ( tempo , { recursive : true } )
156223 } ,
157224} )
158225
0 commit comments