@applitools/eyes-storybook
Version:
152 lines (136 loc) • 4.26 kB
JavaScript
;
function createPagePool({initPage, logger, freePageTimeout = 300_000}) {
let counter = 0;
const fullPageObjs = [];
logger.log(`[page pool] created`);
let currWaitOnFreePage = Promise.resolve();
const pagePool = {
getFreePage,
removeAndAddPage,
createPage: async () => {
const fullPageObj = await createPage();
fullPageObjs.push(fullPageObj);
return toSmallPageObj(fullPageObj);
},
addToPool: pageId => {
const fullPageObj = fullPageObjs.find(p => p.pageId === pageId);
if (fullPageObj) {
fullPageObj.addToPool();
}
},
removePage: pageId => {
const fullPageObj = fullPageObjs.find(p => p.pageId === pageId);
if (fullPageObj) {
fullPageObj.removePage();
}
},
isInPool: pageId => {
const fullPageObj = fullPageObjs.find(p => p.pageId === pageId);
return fullPageObj && fullPageObj.isInPool();
},
drain: async () => {
// only call pagePool.drain if you KNOW all pages are free
logger.log('[page pool] draining pool');
for (const {page, pageId} of [...fullPageObjs]) {
await page.close();
await removeAndAddPage(pageId);
}
},
isClosed: false,
};
return pagePool;
async function removeAndAddPage(pageId) {
pagePool.removePage(pageId);
const {pageId: newPageId} = await pagePool.createPage();
pagePool.addToPool(newPageId);
}
async function getFreePage(tryCount = 0) {
logger.log(`[page pool] waiting for free page, tryCount=${tryCount}`);
await currWaitOnFreePage;
const availablePages = fullPageObjs.filter(p => p.isInPool());
// wait up to 5 minutes
if (availablePages.length === 0) {
const INTERVAL = 500;
if (tryCount < freePageTimeout / INTERVAL) {
logger.log(`[page pool] no available pages, retrying to get free page...`);
await new Promise(r => setTimeout(r, INTERVAL));
return getFreePage(tryCount + 1);
} else {
throw new Error('Could not find free page, timed out after waiting 5 minutes');
}
}
logger.log(`[page pool] waiting on pages ${availablePages.map(({pageId}) => pageId)}`);
currWaitOnFreePage = Promise.race(
availablePages.map(async p => {
await p.waitUntilFree();
return p;
}),
);
const fullPageObj = await currWaitOnFreePage;
if (fullPageObj.isInPool()) {
fullPageObj.occupyPage();
logger.log(`[page pool] free page found: ${fullPageObj.pageId}`);
return toSmallPageObj(fullPageObj);
} else {
logger.log(`[page pool] free page found, but it is no longer in pool: ${fullPageObj.pageId}`);
return getFreePage(tryCount + 1);
}
}
async function createPage() {
const pageId = counter++;
let workPromise = Promise.resolve();
let resolveWork;
let isActive;
let createdAt;
const page = await initPage({pageId, pagePool});
return {
page,
pageId,
markPageAsFree,
waitUntilFree,
occupyPage,
removePage,
isInPool,
addToPool,
getCreatedAt,
};
function markPageAsFree() {
logger.log('[page pool] marking page as free', pageId);
resolveWork();
}
async function waitUntilFree() {
logger.log(`[page ${pageId}] waitUntilFree before`);
await workPromise;
logger.log(`[page ${pageId}] waitUntilFree after`);
}
function occupyPage() {
workPromise = new Promise(resolve => {
resolveWork = resolve;
});
}
function removePage() {
logger.log(`[page ${pageId}] removePage`);
fullPageObjs.splice(
fullPageObjs.findIndex(p => p.pageId === pageId),
1,
);
isActive = false;
resolveWork && resolveWork();
}
function isInPool() {
return isActive;
}
function addToPool() {
logger.log(`[page ${pageId}] addToPool`);
isActive = true;
createdAt = Date.now();
}
function getCreatedAt() {
return createdAt;
}
}
function toSmallPageObj({page, pageId, markPageAsFree, removePage, getCreatedAt}) {
return {page, pageId, markPageAsFree, removePage, getCreatedAt};
}
}
module.exports = createPagePool;