Add WebGL page cache and runtime checks
This commit is contained in:
@@ -27,6 +27,14 @@ const pageFormatPath = path.join(__dirname, '..', 'public', 'js', 'book-page-for
|
||||
const pageFormatSource = fs.readFileSync(pageFormatPath, 'utf8');
|
||||
const stylePath = path.join(__dirname, '..', 'public', 'css', 'style.css');
|
||||
const styleSource = fs.readFileSync(stylePath, 'utf8');
|
||||
const optionsUiPath = path.join(__dirname, '..', 'public', 'js', 'options-ui-module.js');
|
||||
const optionsUiSource = fs.readFileSync(optionsUiPath, 'utf8');
|
||||
const persistencePath = path.join(__dirname, '..', 'public', 'js', 'persistence-manager-module.js');
|
||||
const persistenceSource = fs.readFileSync(persistencePath, 'utf8');
|
||||
const webglPageCachePath = path.join(__dirname, '..', 'public', 'js', 'webgl-page-cache-module.js');
|
||||
const webglPageCacheSource = fs.readFileSync(webglPageCachePath, 'utf8');
|
||||
const ttsFactoryPath = path.join(__dirname, '..', 'public', 'js', 'tts-factory-module.js');
|
||||
const ttsFactorySource = fs.readFileSync(ttsFactoryPath, 'utf8');
|
||||
|
||||
function dependencyList(source, moduleId) {
|
||||
const classStart = source.indexOf(`super('${moduleId}'`);
|
||||
@@ -129,7 +137,18 @@ const checks = [
|
||||
['sentence queue starts lookahead when items arrive during playback', /else \{\s*this\.prefetchAhead\(4, this\.queueGeneration\);/.test(sentenceQueueSource)],
|
||||
['pagination can prepare future spreads without activating visible spread', /preparePendingBlock\(block = \{\}, options = \{\}\)/.test(bookPaginationSource) && /options\.activate !== false/.test(bookPaginationSource) && /includeUnrenderedHistory/.test(bookPaginationSource)],
|
||||
['pagination preserves active inline style tags for texture lines', /getActiveStyleTags/.test(bookPaginationSource) && /activeStyleTags/.test(bookPaginationSource) && /updateStyleTagStack/.test(bookPaginationSource)],
|
||||
['texture renderer caches preload-only reveal canvases for later reuse', /preparedRevealCache/.test(textureRendererSource) && /preloadOnly/.test(textureRendererSource) && /publishPreparedReveal/.test(textureRendererSource) && /reusedPreparedCanvas/.test(textureRendererSource)],
|
||||
['texture renderer caches preload-only reveal canvases for later reuse', /preparedRevealCache/.test(textureRendererSource) && /preloadOnly/.test(textureRendererSource) && /publishPreparedReveal/.test(textureRendererSource) && /reusedPreparedCanvas/.test(textureRendererSource) && /hasPreparedRevealBlock/.test(textureRendererSource)],
|
||||
['webgl page cache is loaded through module infrastructure', /webgl-page-cache-module\.js/.test(loaderSource) && /super\('webgl-page-cache'/.test(webglPageCacheSource) && /reportProgress\(20, 'Opening WebGL page texture cache'\)/.test(webglPageCacheSource)],
|
||||
['webgl page cache uses an isolated browser database without upgrading tts history state', /this\.dbName = 'webglPageTextureCacheDB'/.test(webglPageCacheSource) && /this\.dbVersion = 1/.test(webglPageCacheSource) && /this\.dbVersion = 3/.test(ttsFactorySource) && /this\.dbVersion = 3/.test(storyHistorySource) && !/webglPageTextureStore/.test(ttsFactorySource) && !/webglPageTextureStore/.test(storyHistorySource)],
|
||||
['texture renderer persists frozen completed page canvases without blocking publish', /webgl-page-cache/.test(textureRendererSource) && /cachePublishedPages/.test(textureRendererSource) && /schedulePageCacheWrite/.test(textureRendererSource) && /const frozenCanvas = this\.cloneCanvas\(canvas\)/.test(textureRendererSource) && /requestIdleCallback/.test(textureRendererSource) && /cachePageCanvas/.test(textureRendererSource)],
|
||||
['webgl lab prewarms cached page textures into bounded vram before flips', /residentPageTextures/.test(source) && /maxResidentPageTextures/.test(source) && /preloadCachedPageTexture/.test(source) && /prewarmFlipTextures/.test(source) && /await prewarmFlipTextures\(direction, targetSpread\)/.test(source) && /getResidentPageTexture\(targetBackPageIndex\)/.test(source)],
|
||||
['webgl lab reuses resident cached page textures for direct stack switches', /uploadPageTextureDirect\(side, sourceCanvas, pageMeta = null\)/.test(source) && /getResidentPageTexture\(pageMeta\.pageIndex\)/.test(source) && /usedResidentTexture/.test(source) && /uploadPageTextureDirect\('left', detail\.left, currentPageMeta\.left\)/.test(source) && /uploadPageTextureDirect\('right', detail\.right, currentPageMeta\.right\)/.test(source)],
|
||||
['webgl page cache preserves explicit cache keys across writes and reads', /cacheKey: pageMeta\.cacheKey/.test(webglPageCacheSource) && /makePageKey\(pageMeta\)/.test(webglPageCacheSource)],
|
||||
['targeted page flips commit target spread before emitting finished event', /bookPaginationState = \{[\s\S]*spreadIndex: Math\.max\(0, Math\.round\(Number\(flip\.targetSpread\)\)\)[\s\S]*document\.dispatchEvent\(new CustomEvent\('webgl-book:page-flip-finished'/.test(source) && /targetSpread: Number\.isFinite\(Number\(flip\.targetSpread\)\)/.test(source)],
|
||||
['webgl debug test hook awaits the same async page flip path', /startPageFlipForTest\(direction, options = \{\}\) \{[\s\S]*return startPageFlip\(direction, options\)/.test(source)],
|
||||
['webgl debug test hook can deterministically finish an active page flip', /advancePageFlipForTest\(elapsedMs = normalFlipDuration \+ 16\)/.test(source) && /updateActiveFlips\(targetNow\)/.test(source)],
|
||||
['sentence queue skips duplicate current-item 3D book presentation when reveal is cached', /isWebGLBookPresentationPrepared/.test(sentenceQueueSource) && /if \(!this\.isWebGLBookPresentationPrepared\(sentence\)\) \{\s*await this\.prefetchWebGLBookPresentation/.test(sentenceQueueSource) && /sentence\.webglBookPresentation = \{\s*prepared: true/.test(sentenceQueueSource)],
|
||||
['3D overflow reveal waits for page flip before activating future spread', /sentence\.webglBookPresentation\?\.spread/.test(uiDisplayHandlerSource) && /preparePendingBlock\(sentence, \{\s*activate: false,\s*publish: false,\s*includeUnrenderedHistory: true\s*\}/.test(uiDisplayHandlerSource) && /waitForWebGLPageFlip/.test(uiDisplayHandlerSource) && /targetSpread: previewSpread\.index/.test(uiDisplayHandlerSource) && /webgl-book:request-page-flip/.test(uiDisplayHandlerSource) && /const targetSpread = Number\.isFinite\(Number\(detail\.targetSpread\)\)/.test(source) && /startPageFlip\(direction, \{[\s\S]*targetSpread/.test(source)],
|
||||
['texture renderer paints inline bold and italic styles', /getInlineStyleState/.test(textureRendererSource) && /updateInlineStyleState/.test(textureRendererSource) && /getCanvasFont/.test(textureRendererSource) && /segment\?\.style/.test(textureRendererSource)],
|
||||
['webgl lab can preload page textures without swapping visible page material', /preparedPageTextures/.test(source) && /preloadPageTexture/.test(source) && /renderer\.initTexture\(texture\)/.test(source) && /takePreparedPageTexture/.test(source)],
|
||||
['webgl page text textures avoid mipmap generation', /function configurePageCanvasTexture/.test(source) && /texture\.minFilter = THREE\.LinearFilter/.test(source) && /texture\.generateMipmaps = false/.test(source)],
|
||||
@@ -138,6 +157,7 @@ const checks = [
|
||||
['webgl reveal words consume the allotted time until the next word', /nextTiming/.test(source) && /allottedDuration/.test(source) && /nextDelay - delay/.test(source)],
|
||||
['webgl page format reduces only outer margins from previous value', /outerBaseIn: 0\.27/.test(pageFormatSource) && /outerThicknessFactor: 0\.015/.test(pageFormatSource) && /outerMaxIn: 0\.315/.test(pageFormatSource) && /innerBaseIn: 0\.42/.test(pageFormatSource)],
|
||||
['webgl mode enlarges and inverts DOM overlay text without touching 2D mode', /body\.webgl-mode \{[\s\S]*font-size: 18px;/.test(styleSource) && /body\.webgl-mode \.choice-list \.choice-button/.test(styleSource) && /rgba\(246, 231, 201/.test(styleSource)],
|
||||
['webgl choice overlay hides title clutter and prevents horizontal scrollbar', /body\.webgl-mode #page_left #game_title/.test(styleSource) && /body\.webgl-mode #page_left #start_prompt/.test(styleSource) && /overflow-x: hidden/.test(styleSource) && /book\.style\.width = 'min\(44rem/.test(webglSceneSource)],
|
||||
['drop-cap remaining text does not reinsert discretionary hyphen markers', /extractRemainingLayoutText/.test(bookPaginationSource) && !bookPaginationSource.includes("fragments.push('|')")],
|
||||
['drop-cap reservation keeps a normal text gap beside the initial', /measureDropCapReservation/.test(bookPaginationSource) && /measureNormalTextGap\(fontPx\)/.test(bookPaginationSource)],
|
||||
['drop-cap reservation uses both ink bounds and font advance width', /const advanceWidth = metrics\.width \|\| 0/.test(bookPaginationSource) && /Math\.max\(inkRight, advanceWidth, lineHeightPx \* 1\.08\)/.test(bookPaginationSource)],
|
||||
@@ -148,7 +168,17 @@ const checks = [
|
||||
['pagination opens with blank left and title right spread', /this\.createBlankPage\(0, \{ section: 'frontmatter' \}\)/.test(bookPaginationSource) && /this\.createTitlePage\(1\)/.test(bookPaginationSource) && /this\.createBlankPage\(2, \{ section: 'frontmatter' \}\)/.test(bookPaginationSource)],
|
||||
['pagination publishes page metadata and advances near the end of physical flips', /pageMeta/.test(bookPaginationSource) && /webgl-book:page-flip-near-end/.test(bookPaginationSource) && /this\.setCurrentSpread\(this\.currentSpreadIndex \+ direction\)/.test(bookPaginationSource)],
|
||||
['texture renderer draws title page and page numbers from page metadata', /drawTitlePage/.test(textureRendererSource) && /game_title/.test(textureRendererSource) && /drawPageNumber/.test(textureRendererSource) && /pageMeta: this\.currentSpread\?\.pageMeta/.test(textureRendererSource)],
|
||||
['texture renderer uses plural page margin metrics for page numbers', /this\.metrics\.margins\.bottom/.test(textureRendererSource) && !/this\.metrics\.margin\.bottom/.test(textureRendererSource)],
|
||||
['webgl flip borrows resident page texture and blanks right stack before forward animation', /prepareStaticPageForFlip/.test(source) && /materials\.flipPageSurface\.map = sourceTexture/.test(source) && /materials\.rightPage\.map = blankTexture/.test(source) && /webgl-book:page-flip-near-end/.test(source)],
|
||||
['webgl flip page is a two-sided body using paper-thickness constants', /flipPageBackSurface/.test(source) && /flipPageEdge/.test(source) && /new THREE\.Mesh\(geometry, \[\s*materials\.flipPageSurface,\s*materials\.flipPageBackSurface,\s*materials\.flipPageEdge\s*\]\)/.test(source) && /PROCEDURAL_BOOK\.SHEET_THICKNESS_MODEL/.test(source) && /geometry\.addGroup\(0, topIndices\.length, 0\)/.test(source)],
|
||||
['webgl page navigation is page-count based with explicit spread mapping', /function pageToSpreadIndex/.test(source) && /Math\.floor\(page \/ 2\) \+ 1/.test(source) && /function spreadIndexToPagePosition/.test(source) && /\(spread - 1\) \* 2/.test(source)],
|
||||
['webgl reading progress sync does not rebuild pagination as a page-count change', /function syncReadingProgressToCurrentPage/.test(source) && !/notifyBookPageCountChanged/.test(methodBody(source, 'syncReadingProgressToCurrentPage'))],
|
||||
['webgl page reserve grows book size without shrinking', /function growBookIfWritableLimitReached/.test(source) && /bookPageCount < PROCEDURAL_BOOK\.PAGE_COUNT_MAX/.test(source) && /snapProceduralPageCount\(bookPageCount \+ PROCEDURAL_BOOK\.PAGE_COUNT_STEP\)/.test(source) && /bookPageCount = Math\.max\(nextPageCount, bookPageCount\)/.test(source)],
|
||||
['webgl bottom navigation shows media buttons and endpoint labels', /webgl_book_navigation/.test(source) && /webgl_book_nav_min_label/.test(source) && /webgl_book_nav_max_label/.test(source) && /webgl-book-nav-slider-track/.test(styleSource)],
|
||||
['webgl page reserve options replace old progress slider and hide fixed metadata values', /data-pref-bind': 'webgl\.pageReserve'/.test(optionsUiSource) && /hasFixedBookPageCount/.test(optionsUiSource) && /hasFixedPageReserve/.test(optionsUiSource) && !/data-pref-bind': 'webgl\.bookProgress'/.test(optionsUiSource)],
|
||||
['webgl page reserve persists with sane defaults', /bookPageCount: 300/.test(persistenceSource) && /bookProgress: 0/.test(persistenceSource) && /pageReserve: 50/.test(persistenceSource)],
|
||||
['markup parser strips and stores pagereserve directives', /parsePageReserveDirective/.test(markupParserSource) && /#pagereserve\\\[/.test(markupParserSource) && /unit: match\[2\] === '%' \? 'percent' : 'pages'/.test(markupParserSource)],
|
||||
['game loop persists webgl book state in save slots', /webglBookState: this\.getWebGLBookState\(\)/.test(fs.readFileSync(path.join(__dirname, '..', 'public', 'js', 'game-loop-module.js'), 'utf8')) && /applyWebGLBookState\(browserSave\.webglBookState\)/.test(fs.readFileSync(path.join(__dirname, '..', 'public', 'js', 'game-loop-module.js'), 'utf8'))],
|
||||
['webgl right-page completion arms a flip without bypassing choices', /handleRevealCommittedForPageFlip/.test(source) && /isRightBodyPageComplete/.test(source) && /isChoiceAwaitingPlayer/.test(source) && /pendingRightPageFlip/.test(source)],
|
||||
['markup and 3d pagination accept full-page images', /'full'/.test(markupParserSource) && /size === 'full'/.test(bookPaginationSource)],
|
||||
['story history can persist 3d pagination decisions', /persistPaginationMetrics/.test(bookPaginationSource) && /collectPaginationMetrics/.test(bookPaginationSource) && /pageStart/.test(storyHistorySource) && /pagination: metrics\.pagination/.test(storyHistorySource)]
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
const targetUrl = process.env.WEBGL_RUNTIME_URL || 'http://localhost:3001/';
|
||||
|
||||
async function main() {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage({ viewport: { width: 1920, height: 1080 } });
|
||||
const errors = [];
|
||||
|
||||
page.on('console', (message) => {
|
||||
if (message.type() === 'error') errors.push(message.text());
|
||||
});
|
||||
page.on('pageerror', (error) => errors.push(error.message));
|
||||
|
||||
await page.addInitScript(() => {
|
||||
localStorage.removeItem('ai-interactive-fiction-preferences');
|
||||
});
|
||||
await page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeout: 60000 });
|
||||
await page.waitForFunction(() => window.BookTextureRenderer && window.BookLabDebug, null, { timeout: 180000 });
|
||||
|
||||
const result = await page.evaluate(async () => {
|
||||
window.BookTextureRenderer.publishSpread();
|
||||
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
||||
|
||||
const nav = document.getElementById('webgl_book_navigation');
|
||||
const slider = document.getElementById('webgl_book_nav_position');
|
||||
const minLabel = document.getElementById('webgl_book_nav_min_label');
|
||||
const maxLabel = document.getElementById('webgl_book_nav_max_label');
|
||||
const textureInfo = window.BookLabDebug.getTextureInfo();
|
||||
const initialBookState = window.BookLabDebug.getBookState();
|
||||
const initialSliderMax = slider?.max || null;
|
||||
const initialMinLabel = minLabel?.textContent || '';
|
||||
const initialMaxLabel = maxLabel?.textContent || '';
|
||||
const pageSpreadMap = [0, 1, 2, 3, 4, 5].map(page => [page, window.BookLabDebug.mapPageToSpread(page)]);
|
||||
const spreadPageMap = [0, 1, 2, 3].map(spread => [spread, window.BookLabDebug.mapSpreadToPage(spread)]);
|
||||
const pageCache = window.WebGLPageCache || window.moduleRegistry?.getModule?.('webgl-page-cache');
|
||||
const cacheProbeCanvas = document.createElement('canvas');
|
||||
cacheProbeCanvas.width = 8;
|
||||
cacheProbeCanvas.height = 8;
|
||||
const cacheProbeContext = cacheProbeCanvas.getContext('2d');
|
||||
cacheProbeContext.fillStyle = '#000';
|
||||
cacheProbeContext.fillRect(0, 0, 8, 8);
|
||||
const cacheProbeMeta = { pageIndex: 9999, width: 8, height: 8, cacheKey: 'runtime-probe' };
|
||||
const cacheStoreResult = await pageCache?.cachePageCanvas?.(cacheProbeMeta, cacheProbeCanvas);
|
||||
const cacheProbeResult = await pageCache?.getPageCanvas?.(cacheProbeMeta);
|
||||
|
||||
window.BookLabDebug.setPaginationStateForTest({
|
||||
spreadIndex: 0,
|
||||
spreadCount: 126,
|
||||
writtenPageLimit: 250
|
||||
});
|
||||
const grownBookState = window.BookLabDebug.getBookState();
|
||||
|
||||
window.BookLabDebug.setPaginationStateForTest({
|
||||
spreadIndex: 0,
|
||||
spreadCount: 8,
|
||||
writtenPageLimit: 10
|
||||
});
|
||||
slider.value = '100';
|
||||
slider.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await new Promise(resolve => {
|
||||
const startedAt = Date.now();
|
||||
const check = () => {
|
||||
if ((window.BookLabDebug?.activeFlips || 0) === 0 || Date.now() - startedAt > 2200) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
requestAnimationFrame(check);
|
||||
};
|
||||
requestAnimationFrame(check);
|
||||
});
|
||||
const clampedSliderValue = slider.value;
|
||||
|
||||
document.dispatchEvent(new CustomEvent('webgl-book:page-reserve-directive', {
|
||||
detail: {
|
||||
value: 20,
|
||||
unit: 'percent'
|
||||
}
|
||||
}));
|
||||
const percentReserveState = window.BookLabDebug.getBookState();
|
||||
|
||||
document.body.classList.add('webgl-mode');
|
||||
if (!document.getElementById('page_left')) {
|
||||
window.moduleRegistry?.getModule?.('ui-display-handler')?.initializeContainers?.();
|
||||
}
|
||||
window.moduleRegistry?.getModule?.('webgl-book-scene')?.moveBookToControlOverlay?.();
|
||||
const pageLeft = document.getElementById('page_left');
|
||||
let choicesPanel = document.getElementById('choices');
|
||||
if (!choicesPanel && pageLeft) {
|
||||
choicesPanel = document.createElement('div');
|
||||
choicesPanel.id = 'choices';
|
||||
choicesPanel.className = 'container';
|
||||
pageLeft.appendChild(choicesPanel);
|
||||
}
|
||||
const choicesGroup = document.createElement('div');
|
||||
choicesGroup.className = 'choices-group';
|
||||
const choiceButton = document.createElement('button');
|
||||
choiceButton.className = 'choice-button';
|
||||
choiceButton.textContent = 'A deliberately long choice label that must stay inside the WebGL overlay without creating horizontal scrolling';
|
||||
choicesGroup.appendChild(choiceButton);
|
||||
choicesPanel?.appendChild(choicesGroup);
|
||||
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
||||
const gameTitle = document.getElementById('game_title');
|
||||
const startPrompt = document.getElementById('start_prompt');
|
||||
const titleDisplay = gameTitle ? window.getComputedStyle(gameTitle).display : 'absent';
|
||||
const startPromptDisplay = startPrompt ? window.getComputedStyle(startPrompt).display : 'absent';
|
||||
const pageLeftStyle = pageLeft ? window.getComputedStyle(pageLeft) : null;
|
||||
const choicesStyle = choicesPanel ? window.getComputedStyle(choicesPanel) : null;
|
||||
const buttonStyle = window.getComputedStyle(choiceButton);
|
||||
const overlayLayout = {
|
||||
pageLeftExists: Boolean(pageLeft),
|
||||
choicesPanelExists: Boolean(choicesPanel),
|
||||
pageLeftNoHorizontalScrollbar: pageLeft ? pageLeft.scrollWidth <= pageLeft.clientWidth + 1 : false,
|
||||
choicesNoHorizontalScrollbar: choicesPanel ? choicesPanel.scrollWidth <= choicesPanel.clientWidth + 1 : false,
|
||||
pageLeftOverflowX: pageLeftStyle?.overflowX || null,
|
||||
choicesOverflowX: choicesStyle?.overflowX || null,
|
||||
titleDisplay,
|
||||
startPromptDisplay,
|
||||
buttonColor: buttonStyle.color,
|
||||
buttonBackground: buttonStyle.backgroundColor
|
||||
};
|
||||
|
||||
window.BookLabDebug.setPaginationStateForTest({
|
||||
spreadIndex: 1,
|
||||
spreadCount: 8,
|
||||
writtenPageLimit: 10
|
||||
});
|
||||
if (window.BookPagination) {
|
||||
window.BookPagination.spreads = Array.from({ length: 8 }, (_, index) => ({
|
||||
index,
|
||||
left: [],
|
||||
right: [],
|
||||
pageMeta: {}
|
||||
}));
|
||||
window.BookPagination.currentSpreadIndex = 1;
|
||||
}
|
||||
let targetFlipEventDetail = null;
|
||||
const flipFinished = new Promise(resolve => {
|
||||
document.addEventListener('webgl-book:page-flip-finished', (event) => {
|
||||
targetFlipEventDetail = event.detail || null;
|
||||
resolve(true);
|
||||
}, { once: true });
|
||||
});
|
||||
const requestedFlip = await window.BookLabDebug.startPageFlipForTest(1, {
|
||||
force: true,
|
||||
targetSpread: 2
|
||||
});
|
||||
const activeFlipsAfterRequest = window.BookLabDebug.activeFlips;
|
||||
let postAdvanceState = null;
|
||||
if (requestedFlip && window.BookLabDebug.activeFlips > 0) {
|
||||
postAdvanceState = window.BookLabDebug.advancePageFlipForTest();
|
||||
}
|
||||
const activeFlipsAfterAdvance = window.BookLabDebug.activeFlips;
|
||||
const targetFlipFinished = targetFlipEventDetail
|
||||
? true
|
||||
: await Promise.race([
|
||||
flipFinished,
|
||||
new Promise(resolve => window.setTimeout(() => resolve(false), 5000))
|
||||
]);
|
||||
const postTargetFlipState = window.BookLabDebug.getBookState();
|
||||
|
||||
return {
|
||||
navExists: Boolean(nav),
|
||||
initialSliderMax,
|
||||
initialMinLabel,
|
||||
initialMaxLabel,
|
||||
finalSliderMax: slider?.max || null,
|
||||
finalMaxLabel: maxLabel?.textContent || '',
|
||||
initialBookState,
|
||||
pageSpreadMap,
|
||||
spreadPageMap,
|
||||
pageCacheReady: pageCache?.cacheStatus === 'ready',
|
||||
pageCacheProbe: {
|
||||
stored: cacheStoreResult === true,
|
||||
width: cacheProbeResult?.width || 0,
|
||||
height: cacheProbeResult?.height || 0
|
||||
},
|
||||
grownBookState,
|
||||
clampedSliderValue,
|
||||
percentReserveState,
|
||||
overlayLayout,
|
||||
requestedFlip,
|
||||
activeFlipsAfterRequest,
|
||||
activeFlipsAfterAdvance,
|
||||
postAdvanceState,
|
||||
targetFlipFinished,
|
||||
targetFlipEventDetail,
|
||||
postTargetFlipState,
|
||||
textureInfo
|
||||
};
|
||||
});
|
||||
|
||||
await browser.close();
|
||||
|
||||
const failures = [];
|
||||
const relevantErrors = errors.filter((error) => !/^Failed to load resource: the server responded with a status of 400/.test(error));
|
||||
if (relevantErrors.length) failures.push(`browser errors: ${relevantErrors.join(' | ')}`);
|
||||
if (!result.navExists) failures.push('bottom navigation missing');
|
||||
if (result.initialSliderMax !== '300') failures.push(`expected initial slider max 300, got ${result.initialSliderMax}`);
|
||||
if (result.initialMinLabel !== '0') failures.push(`expected min label 0, got ${result.initialMinLabel}`);
|
||||
if (result.initialMaxLabel !== '300') failures.push(`expected initial max label 300, got ${result.initialMaxLabel}`);
|
||||
if (result.initialBookState?.pageCount !== 300) failures.push(`expected initial pageCount 300, got ${result.initialBookState?.pageCount}`);
|
||||
if (result.initialBookState?.pageReserve !== 50) failures.push(`expected initial pageReserve 50, got ${result.initialBookState?.pageReserve}`);
|
||||
if (result.initialBookState?.progress !== 0) failures.push(`expected initial progress 0, got ${result.initialBookState?.progress}`);
|
||||
if (JSON.stringify(result.pageSpreadMap) !== JSON.stringify([[0, 0], [1, 1], [2, 2], [3, 2], [4, 3], [5, 3]])) {
|
||||
failures.push(`unexpected page-to-spread map ${JSON.stringify(result.pageSpreadMap)}`);
|
||||
}
|
||||
if (JSON.stringify(result.spreadPageMap) !== JSON.stringify([[0, 0], [1, 1], [2, 2], [3, 4]])) {
|
||||
failures.push(`unexpected spread-to-page map ${JSON.stringify(result.spreadPageMap)}`);
|
||||
}
|
||||
if (!result.pageCacheReady) failures.push('WebGL page cache is not ready');
|
||||
if (!result.pageCacheProbe?.stored || result.pageCacheProbe?.width !== 8 || result.pageCacheProbe?.height !== 8) {
|
||||
failures.push(`WebGL page cache probe failed: ${JSON.stringify(result.pageCacheProbe)}`);
|
||||
}
|
||||
if (result.grownBookState?.pageCount !== 310) failures.push(`expected page count to grow to 310 at writable limit, got ${result.grownBookState?.pageCount}`);
|
||||
if (result.finalSliderMax !== '310') failures.push(`expected final slider max 310, got ${result.finalSliderMax}`);
|
||||
if (result.finalMaxLabel !== '310') failures.push(`expected final max label 310, got ${result.finalMaxLabel}`);
|
||||
if (result.clampedSliderValue !== '10') failures.push(`expected slider clamp to written page 10, got ${result.clampedSliderValue}`);
|
||||
if (result.percentReserveState?.pageReserve !== 62) failures.push(`expected 20% reserve of 310 pages to be 62, got ${result.percentReserveState?.pageReserve}`);
|
||||
if (!result.overlayLayout?.pageLeftNoHorizontalScrollbar) failures.push('WebGL overlay page_left has a horizontal scrollbar');
|
||||
if (!result.overlayLayout?.choicesNoHorizontalScrollbar) failures.push('WebGL choices panel has a horizontal scrollbar');
|
||||
if (result.overlayLayout?.pageLeftOverflowX !== 'hidden') failures.push(`expected page_left overflow-x hidden, got ${result.overlayLayout?.pageLeftOverflowX}`);
|
||||
if (result.overlayLayout?.choicesOverflowX !== 'hidden') failures.push(`expected choices overflow-x hidden, got ${result.overlayLayout?.choicesOverflowX}`);
|
||||
if (!['none', 'absent'].includes(result.overlayLayout?.titleDisplay)) failures.push(`expected title hidden in WebGL overlay, got ${result.overlayLayout?.titleDisplay}`);
|
||||
if (!['none', 'absent'].includes(result.overlayLayout?.startPromptDisplay)) failures.push(`expected start prompt hidden in WebGL overlay, got ${result.overlayLayout?.startPromptDisplay}`);
|
||||
if (/^rgb\(0,\s*0,\s*0\)$/.test(result.overlayLayout?.buttonColor || '')) failures.push('choice button text is still black in WebGL overlay');
|
||||
if (!result.requestedFlip) failures.push('targeted page flip request was rejected');
|
||||
if (!result.targetFlipFinished) failures.push(`targeted page flip did not finish: ${JSON.stringify({
|
||||
requestedFlip: result.requestedFlip,
|
||||
activeFlipsAfterRequest: result.activeFlipsAfterRequest,
|
||||
activeFlipsAfterAdvance: result.activeFlipsAfterAdvance,
|
||||
postAdvanceState: result.postAdvanceState,
|
||||
eventDetail: result.targetFlipEventDetail
|
||||
})}`);
|
||||
if (result.postTargetFlipState?.spreadIndex !== 2) failures.push(`targeted page flip should commit spread 2, got ${result.postTargetFlipState?.spreadIndex}`);
|
||||
if (!result.textureInfo?.debug?.left?.painted || !result.textureInfo?.debug?.right?.painted) failures.push('page texture publish did not paint both pages');
|
||||
|
||||
if (failures.length) {
|
||||
console.error('WebGL runtime regression checks failed:');
|
||||
failures.forEach(failure => console.error(`- ${failure}`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('WebGL runtime regression checks passed.');
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user