mirror of
https://github.com/ThatGuySam/doesitarm.git
synced 2026-05-18 06:44:46 -07:00
Add a typed Playwright harness for Pagefind and the Apple Silicon app-test flow so scanner work has browser-level protection. Keep the rollout plan in the same stack so the TypeScript conversion stays staged and reviewable. Constraint: Must not change production runtime behavior in this commit Rejected: Leave the old JS browser test and add a second harness | duplicates setup and leaves the targeted browser script broken Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep browser-only helpers under test/playwright/support until the runtime scanner surface is fully typed Tested: pnpm run typecheck; pnpm run test:browser; pnpm run test:browser:pagefind Not-tested: Live browser checks against doesitarm.com
178 lines
5.5 KiB
TypeScript
178 lines
5.5 KiB
TypeScript
import type { Browser, Page } from 'playwright-core'
|
|
import {
|
|
afterAll,
|
|
beforeAll,
|
|
describe,
|
|
expect,
|
|
it
|
|
} from 'vitest'
|
|
|
|
import {
|
|
launchBrowser,
|
|
startAstroDevServer,
|
|
stopChildProcess,
|
|
type AstroDevServer
|
|
} from './support/astro-browser-test'
|
|
import {
|
|
createNativeAppArchive,
|
|
type PlaywrightUploadFile
|
|
} from './support/app-archive-fixture'
|
|
|
|
const appTestVariants = [
|
|
{
|
|
name: 'legacy scanner',
|
|
routeSuffix: ''
|
|
},
|
|
{
|
|
name: 'worker scanner',
|
|
routeSuffix: '?version=2'
|
|
}
|
|
] as const
|
|
|
|
describe( 'Apple Silicon app test page', () => {
|
|
let browser: Browser
|
|
let devServer: AstroDevServer
|
|
let appArchive: PlaywrightUploadFile
|
|
|
|
beforeAll( async () => {
|
|
appArchive = await createNativeAppArchive()
|
|
|
|
devServer = await startAstroDevServer({
|
|
env: {
|
|
TEST_RESULT_STORE: '/api/test-results'
|
|
},
|
|
preferConfiguredBaseUrl: false
|
|
})
|
|
|
|
browser = await launchBrowser()
|
|
await Promise.all( appTestVariants.map( variant => {
|
|
return warmAppTestRoute( browser, devServer.baseUrl, variant.routeSuffix )
|
|
} ) )
|
|
} )
|
|
|
|
afterAll( async () => {
|
|
await browser?.close()
|
|
await stopChildProcess( devServer?.process || null )
|
|
} )
|
|
|
|
it.each( appTestVariants )( 'uploads an app archive through the %s path and renders a native result', async ( variant ) => {
|
|
const page = await browser.newPage()
|
|
const consoleErrors: string[] = []
|
|
const pageErrors: string[] = []
|
|
const submittedScans: Record<string, unknown>[] = []
|
|
|
|
page.on( 'console', message => {
|
|
if ( message.type() === 'error' ) {
|
|
consoleErrors.push( message.text() )
|
|
}
|
|
} )
|
|
|
|
page.on( 'pageerror', error => {
|
|
pageErrors.push( error.message )
|
|
} )
|
|
|
|
await stubResultStore( page, submittedScans )
|
|
|
|
await page.goto( `${ devServer.baseUrl }/apple-silicon-app-test/${ variant.routeSuffix }`, {
|
|
waitUntil: 'load'
|
|
} )
|
|
|
|
await page.waitForFunction( () => {
|
|
const island = document.querySelector( 'astro-island[component-url="/pages/apple-silicon-app-test.vue"]' )
|
|
|
|
return Boolean( island && !island.hasAttribute( 'ssr' ) )
|
|
}, {
|
|
timeout: 30 * 1000
|
|
} )
|
|
|
|
await page.locator( 'input[type="file"]' ).setInputFiles( appArchive )
|
|
await waitForBodyText( page, 'Total Files: 1', {
|
|
consoleErrors,
|
|
devServerOutput: devServer.output.text,
|
|
pageErrors
|
|
} )
|
|
|
|
const firstScanRow = page.locator( '.results-container li' ).first()
|
|
|
|
await waitForBodyText( page, 'Playwright Native App', {
|
|
consoleErrors,
|
|
devServerOutput: devServer.output.text,
|
|
pageErrors
|
|
} )
|
|
await waitForBodyText( page, '✅ This app is natively compatible with Apple Silicon!', {
|
|
consoleErrors,
|
|
devServerOutput: devServer.output.text,
|
|
pageErrors
|
|
} )
|
|
|
|
await firstScanRow.locator( 'summary' ).click()
|
|
|
|
const rowText = await firstScanRow.textContent()
|
|
|
|
expect( rowText ).toContain( 'Bundle Identifier' )
|
|
expect( rowText ).toContain( 'com.doesitarm.playwright-native-app' )
|
|
|
|
expect( submittedScans.length, devServer.output.text ).toBe( 1 )
|
|
expect( submittedScans[ 0 ]?.filename, JSON.stringify( submittedScans[ 0 ] ) ).toBe( 'Playwright Native App.app.zip' )
|
|
expect( submittedScans[ 0 ]?.result, JSON.stringify( submittedScans[ 0 ] ) ).toBe( '✅' )
|
|
expect( pageErrors, devServer.output.text ).toEqual( [] )
|
|
expect( consoleErrors, devServer.output.text ).toEqual( [] )
|
|
} )
|
|
} )
|
|
|
|
async function stubResultStore ( page: Page, submittedScans: Record<string, unknown>[] ) {
|
|
await page.route( '**/api/test-results', async route => {
|
|
const postData = route.request().postDataJSON()
|
|
|
|
if ( postData && typeof postData === 'object' ) {
|
|
submittedScans.push( postData as Record<string, unknown> )
|
|
}
|
|
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
supportedVersionNumber: null
|
|
})
|
|
})
|
|
} )
|
|
}
|
|
|
|
async function waitForBodyText ( page: Page, expectedText: string, debugContext: {
|
|
consoleErrors: string[]
|
|
devServerOutput: string
|
|
pageErrors: string[]
|
|
} ) {
|
|
try {
|
|
await page.waitForFunction( textToFind => {
|
|
return Boolean( document.body?.textContent?.includes( textToFind ) )
|
|
}, expectedText, {
|
|
timeout: 30 * 1000
|
|
} )
|
|
} catch ( error ) {
|
|
const bodyText = await page.locator( 'body' ).textContent()
|
|
|
|
throw new Error( [
|
|
`Timed out waiting for body text: ${ expectedText }`,
|
|
bodyText || '',
|
|
debugContext.pageErrors.join( '\n' ),
|
|
debugContext.consoleErrors.join( '\n' ),
|
|
debugContext.devServerOutput
|
|
].filter( Boolean ).join( '\n\n' ), {
|
|
cause: error
|
|
} )
|
|
}
|
|
}
|
|
|
|
async function warmAppTestRoute ( browser: Browser, baseUrl: string, routeSuffix = '' ) {
|
|
const warmPage = await browser.newPage()
|
|
|
|
try {
|
|
await warmPage.goto( `${ baseUrl }/apple-silicon-app-test/${ routeSuffix }`, {
|
|
waitUntil: 'load'
|
|
} )
|
|
await warmPage.waitForTimeout( 5000 )
|
|
} finally {
|
|
await warmPage.close()
|
|
}
|
|
}
|