dana-medical-site/web/scripts/test-page-loading.mjs
2026-10-23 07:23:20 +00:00

68 lines
4.3 KiB
JavaScript

// Public fixtures only. No network or production mutations.
import assert from 'node:assert/strict';
import { parseHTML } from 'linkedom';
import { createServer } from 'vite';
const { window } = parseHTML('<html><body><div id="root"></div></body></html>');
Object.assign(globalThis, { window, document: window.document, HTMLElement: window.HTMLElement, IS_REACT_ACT_ENVIRONMENT: true });
window.location = new URL('https://test.invalid/');
const { default: React, act } = await import('react');
const { createRoot } = await import('react-dom/client');
const { renderToString } = await import('react-dom/server');
const { MemoryRouter } = await import('react-router-dom');
const vite = await createServer({ server: { middlewareMode: true, hmr: false }, appType: 'custom' });
const { useApi } = await vite.ssrLoadModule('/src/lib/useApi.js');
const { ApiDataProvider } = await vite.ssrLoadModule('/src/context/ApiData.jsx');
const { SiteDataProvider } = await vite.ssrLoadModule('/src/context/SiteData.jsx');
const { default: HeroSlider } = await vite.ssrLoadModule('/src/components/HeroSlider.jsx');
const { api } = await vite.ssrLoadModule('/src/api.js');
let calls = 0;
let siteCalls = 0;
api.branches = api.settings = async () => { siteCalls++; return { data: [] }; };
const pending = [];
const load = (signal) => { calls++; return new Promise((resolve) => pending.push({ signal, resolve })); };
function Probe({ query }) {
const result = useApi(load, [query], query);
return React.createElement('output', {}, result.data || 'loading');
}
const container = document.getElementById('root');
const root = createRoot(container);
const snapshot = { first: { data: 'SSR first' }, second: { data: 'SSR second' }, site: { phone: 'test' }, __receivedAt: Date.now() };
async function mount(query, initial = snapshot, key = 'same') {
await act(async () => root.render(React.createElement(React.StrictMode, {},
React.createElement(ApiDataProvider, { value: initial }, React.createElement(SiteDataProvider, {},
React.createElement(Probe, { key, query }))))));
}
const banners = Array.from({ length: 6 }, (_, id) => ({ id, image: `/test-${id}.webp`, title: `Banner ${id}` }));
const images = () => [...container.querySelectorAll('.hero-slide img[src]')].map((node) => node.getAttribute('src'));
try {
await mount('first');
assert.equal(calls, 0, 'Hydration including StrictMode must not repeat catalog request');
assert.equal(siteCalls, 0, 'No redundant branch/settings requests');
assert.equal(container.querySelector('output').textContent, 'SSR first');
await mount('second');
assert.equal(calls, 0);
assert.equal(container.querySelector('output').textContent, 'SSR second');
await mount('uncached');
assert.equal(calls, 1, 'Client-only route still loads');
await mount('new-query');
await act(async () => pending.at(-2).resolve({ data: 'STALE' }));
assert.doesNotMatch(container.textContent, /STALE/);
await act(async () => pending.at(-1).resolve({ data: 'Fresh query' }));
assert.match(container.textContent, /Fresh query/);
await mount('first', { ...snapshot, __receivedAt: Date.now() - 31_000 }, 'expired');
assert.ok(calls >= 3, 'Old SSR snapshot must revalidate');
for (const request of pending) await act(async () => request.resolve({ data: 'Refreshed' }));
const ssr = renderToString(React.createElement(MemoryRouter, {}, React.createElement(HeroSlider, { banners })));
assert.equal(new Set([...ssr.matchAll(/src="(\/test-[^"]+)"/g)].map((m) => m[1])).size, 3, 'SSR exposes only current/neighbor images');
await act(async () => root.render(React.createElement(MemoryRouter, {}, React.createElement(HeroSlider, { banners }))));
assert.deepEqual([...new Set(images())].sort(), ['/test-0.webp', '/test-1.webp', '/test-5.webp']);
assert.equal(container.querySelector('.hero__item--active picture img').getAttribute('fetchpriority'), 'high');
const next = container.querySelector('[aria-label="Следующий слайд"]');
for (let i = 0; i < 6; i++) await act(async () => next.dispatchEvent(new window.Event('click', { bubbles: true })));
assert.equal(new Set(images()).size, 6, 'All banners load on demand as the visitor browses');
console.log('PASS: SSR catalog/site requests 0, uncached/expired queries load, stale replies ignored; initial banner images 6 → 3, all slides reachable');
} finally {
await act(async () => root.unmount());
await vite.close();
}