Files
Imhotep/packages/imhotep-playwright/src/page-pool.test.ts
T

197 lines
5.6 KiB
TypeScript

/**
* Tests for the PagePool module.
*
* Uses mock BrowserContext and Page objects to verify pooling,
* borrowing, returning, resetting, and disposal behavior.
*/
import { describe, it } from 'node:test'
import assert from 'node:assert'
import type { Page, BrowserContext } from 'playwright'
import { PagePool, PageEntry, ContextPool } from './page-pool.js'
function createMockContext(): BrowserContext & Record<string, any> {
const context: any = {
newPageCalls: 0,
clearCookiesCalls: 0,
closeCalls: 0,
createdPages: [],
newPage: async () => {
context.newPageCalls++
const page = createMockPage(context)
context.createdPages.push(page)
return page
},
clearCookies: async () => {
context.clearCookiesCalls++
},
close: async () => {
context.closeCalls++
},
}
return context as BrowserContext & Record<string, any>
}
function createMockPage(context?: any): Page & Record<string, any> {
const page: any = {
gotoCalls: [],
evaluateCalls: 0,
setViewportSizeCalls: [],
closeCalls: 0,
goto: async (url: string) => {
page.gotoCalls.push(url)
},
context: () => context,
evaluate: async () => {
page.evaluateCalls++
},
setViewportSize: async (size: any) => {
page.setViewportSizeCalls.push(size)
},
close: async () => {
page.closeCalls++
},
addInitScript: async () => {},
}
return page as Page & Record<string, any>
}
describe('PagePool', () => {
it('warm creates specified number of pages', async () => {
const context = createMockContext()
const pool = new PagePool({} as ContextPool)
await pool.warm(context, 3)
assert.strictEqual(context.newPageCalls, 3)
const entries: PageEntry[] = (pool as any).pools.get(context) ?? []
assert.strictEqual(entries.length, 3)
assert.strictEqual(entries.every((e) => !e.inUse), true)
})
it('borrow returns warmed page', async () => {
const context = createMockContext()
const pool = new PagePool({} as ContextPool)
await pool.warm(context, 1)
const entriesBefore: PageEntry[] = (pool as any).pools.get(context) ?? []
const warmedPage = entriesBefore[0].imhotepPage
const borrowed = await pool.borrow(context)
assert.strictEqual(borrowed, warmedPage)
const entriesAfter: PageEntry[] = (pool as any).pools.get(context) ?? []
assert.strictEqual(entriesAfter[0].inUse, true)
})
it('borrow creates new page when none available', async () => {
const context = createMockContext()
const pool = new PagePool({} as ContextPool)
const borrowed = await pool.borrow(context)
assert.strictEqual(context.newPageCalls, 1)
assert.ok(borrowed)
const entries: PageEntry[] = (pool as any).pools.get(context) ?? []
assert.strictEqual(entries.length, 1)
assert.strictEqual(entries[0].inUse, true)
})
it('borrow throws when at maxPagesPerContext and all in use', async () => {
const context = createMockContext()
const pool = new PagePool({} as ContextPool, { maxPagesPerContext: 2 })
await pool.borrow(context)
await pool.borrow(context)
await assert.rejects(
async () => pool.borrow(context),
/Max pages per context \(2\) reached and all pages are in use/
)
})
it('return resets page and marks available', async () => {
const context = createMockContext()
const pool = new PagePool({} as ContextPool)
const borrowed = await pool.borrow(context)
const mockPage = context.createdPages[0]
// Reset was called once during borrow
assert.strictEqual(mockPage.evaluateCalls, 1)
await pool.return(borrowed)
// Return called reset again
assert.strictEqual(mockPage.evaluateCalls, 2)
const entries: PageEntry[] = (pool as any).pools.get(context) ?? []
assert.strictEqual(entries[0].inUse, false)
})
it('reset clears cookies and localStorage', async () => {
const context = createMockContext()
const pool = new PagePool({} as ContextPool)
await pool.borrow(context)
const mockPage = context.createdPages[0]
assert.strictEqual(mockPage.gotoCalls.includes('about:blank'), true)
assert.strictEqual(context.clearCookiesCalls, 1)
assert.strictEqual(mockPage.evaluateCalls, 1)
assert.deepStrictEqual(mockPage.setViewportSizeCalls, [{ width: 1280, height: 720 }])
})
it('dispose closes all pages', async () => {
const context = createMockContext()
const pool = new PagePool({} as ContextPool)
await pool.warm(context, 2)
await pool.dispose()
assert.strictEqual(context.createdPages[0].closeCalls, 1)
assert.strictEqual(context.createdPages[1].closeCalls, 1)
})
it('idempotent dispose', async () => {
const context = createMockContext()
const pool = new PagePool({} as ContextPool)
await pool.warm(context, 1)
await pool.dispose()
await pool.dispose()
assert.strictEqual(context.createdPages[0].closeCalls, 1)
})
it('borrowed page is inUse', async () => {
const context = createMockContext()
const pool = new PagePool({} as ContextPool, { maxPagesPerContext: 1 })
await pool.warm(context, 1)
await pool.borrow(context)
await assert.rejects(
async () => pool.borrow(context),
/Max pages per context \(1\) reached and all pages are in use/
)
})
it('returned page is not inUse', async () => {
const context = createMockContext()
const pool = new PagePool({} as ContextPool, { maxPagesPerContext: 1 })
await pool.warm(context, 1)
const first = await pool.borrow(context)
await pool.return(first)
const second = await pool.borrow(context)
assert.strictEqual(context.newPageCalls, 1)
assert.strictEqual(second, first)
})
})