doesitarm/test/playwright/support/app-archive-fixture.ts
ThatGuySam 689fc0d13d refactor(scanner): type the worker path and align app-test results
Move the worker scanner surface into TypeScript, add a direct worker regression, and make the version=2 app-test path populate the same visible result data and final status as the legacy scanner. This keeps the refactor bounded while making the worker route safe to exercise.

Constraint: Must preserve the existing Apple Silicon app-test behavior while changing the worker internals
Rejected: Flip production to the worker path immediately | still needs the normal deploy path and broader production soak
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep the version=2 adapter using the shared finishFileScan path until the legacy scanner can be removed entirely
Tested: pnpm run typecheck; pnpm exec vitest run test/scanner/client.test.ts; pnpm run test:browser (original workspace); netlify build --context deploy-preview (original workspace)
Not-tested: Browser suite from the clean clone environment (local Astro dev server startup timed out there)
2026-04-04 14:58:25 -05:00

76 lines
3.2 KiB
TypeScript

import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { Zip } from 'zip-lib'
const machoObjectBase64 =
'z/rt/gwAAAEAAAAAAQAAAAQAAABoAQAAACAAAAAAAAAZAAAA6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAiAEAAAAAAAA4AAAAAAAAAAcAAAAHAAAAAgAAAAAAAABfX3RleHQAAAAAAAAAAAAAX19URVhUAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAACIAQAAAgAAAAAAAAAAAAAAAAQAgAAAAAAAAAAAAAAAAF9fY29tcGFjdF91bndpbmRfX0xEAAAAAAAAAAAAAAAAGAAAAAAAAAAgAAAAAAAAAKABAAADAAAAwAEAAAEAAAAAAAACAAAAAAAAAAAAAAAAMgAAABgAAAABAAAAAAALAAACGgAAAAAAAgAAABgAAADIAQAAAwAAAPgBAAAYAAAACwAAAFAAAAAAAAAAAgAAAAIAAAABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QwDRAACAUv8PALn/QwCRwANf1gAAAAAAAAAAAAAAABQAAAAAEAACAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAGDQAAAA4BAAAAAAAAAAAAAAcAAAAOAgAAGAAAAAAAAAABAAAADwEAAAAAAAAAAAAAAF9tYWluAGx0bXAxAGx0bXAwAAAAAAAA'
export interface PlaywrightUploadFile {
arrayBuffer: ArrayBuffer
buffer: Buffer
mimeType: string
name: string
type: string
}
function makeInfoPlist ( appName: string ) {
return [
'<?xml version="1.0" encoding="UTF-8"?>',
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
'<plist version="1.0">',
'<dict>',
' <key>CFBundleDisplayName</key>',
` <string>${ appName }</string>`,
' <key>CFBundleExecutable</key>',
` <string>${ appName }</string>`,
' <key>CFBundleIdentifier</key>',
` <string>com.doesitarm.${ appName.toLowerCase().replaceAll( ' ', '-' ) }</string>`,
' <key>CFBundleName</key>',
` <string>${ appName }</string>`,
' <key>CFBundleShortVersionString</key>',
' <string>1.0.0</string>',
'</dict>',
'</plist>',
''
].join( '\n' )
}
export async function createNativeAppArchive ( appName = 'Playwright Native App' ): Promise<PlaywrightUploadFile> {
const tempRoot = await mkdtemp( join( tmpdir(), 'doesitarm-playwright-' ) )
const appBundlePath = join( tempRoot, `${ appName }.app` )
const contentsPath = join( appBundlePath, 'Contents' )
const executablePath = join( contentsPath, 'MacOS', appName )
const archivePath = join( tempRoot, `${ appName }.app.zip` )
try {
const executableBytes = new Uint8Array( Buffer.from( machoObjectBase64, 'base64' ) )
await mkdir( join( contentsPath, 'MacOS' ), { recursive: true } )
await writeFile( join( contentsPath, 'Info.plist' ), makeInfoPlist( appName ) )
await writeFile( executablePath, executableBytes, { mode: 0o755 } )
const zip = new Zip()
zip.addFolder( appBundlePath, `${ appName }.app` )
await zip.archive( archivePath )
const archiveBuffer = await readFile( archivePath )
const archiveArrayBuffer = new Uint8Array( archiveBuffer ).slice().buffer
return {
arrayBuffer: archiveArrayBuffer,
buffer: archiveBuffer,
mimeType: 'application/zip',
name: `${ appName }.app.zip`,
type: 'application/zip'
}
} finally {
await rm( tempRoot, {
force: true,
recursive: true
} )
}
}