Files
my-protfolio/tests/scroll-and-loading.test.mjs
Amirhossein Khalili b3cfcfe057
Some checks failed
Test and deploy / Verify production build (push) Has been cancelled
Test and deploy / Deploy production (push) Has been cancelled
fix: stabilize portfolio scrolling and loading
Remove skeleton swaps and deferred layout estimates, restore reveal state, and use native proximity scrolling for project cards. Also make the Docker build npm registry configurable.
2026-09-09 17:53:48 +03:30

88 lines
3.7 KiB
JavaScript

import assert from 'node:assert/strict';
import { access, readFile } from 'node:fs/promises';
import test from 'node:test';
const removedSkeletonFiles = [
'src/app/(en)/loading.tsx',
'src/app/(fa)/fa/loading.tsx',
'src/components/ImageWithSkeleton.tsx',
'src/components/ImageWithSkeleton.module.css',
'src/components/skeletons/PortfolioSkeleton.tsx',
'src/components/skeletons/PortfolioSkeleton.module.css',
];
test('portfolio rendering no longer uses skeleton loading layers', async () => {
await Promise.all(
removedSkeletonFiles.map(async (file) => {
await assert.rejects(access(file), `${file} should be removed`);
}),
);
const [page, scene, sceneStyles, sourceFiles] = await Promise.all([
readFile('src/app/PortfolioPage.tsx', 'utf8'),
readFile('src/components/scenes/DeferredAboutRiverScene.tsx', 'utf8'),
readFile('src/components/scenes/Scenes.module.css', 'utf8'),
Promise.all([
'src/components/HeroSection.tsx',
'src/components/ProjectThumbnail.tsx',
'src/components/ExperienceSection.tsx',
'src/components/SkillsSection.tsx',
'src/components/EducationSection.tsx',
'src/components/MilestonesSection.tsx',
].map((file) => readFile(file, 'utf8'))).then((files) => files.join('\n')),
]);
assert.doesNotMatch(page, /Suspense|Skeleton/);
assert.doesNotMatch(sourceFiles, /ImageWithSkeleton|skeletonShape/);
assert.doesNotMatch(scene, /isReady|onReady|sceneSkeleton|aria-busy/);
assert.doesNotMatch(sceneStyles, /sceneSkeleton|sceneSkeletonShimmer/);
});
test('deferred section sizing cannot replace real layout while scrolling', async () => {
const [globals, sections] = await Promise.all([
readFile('src/styles/globals.css', 'utf8'),
Promise.all([
'src/components/ProjectsSection.tsx',
'src/components/ExperienceSection.tsx',
'src/components/SkillsSection.tsx',
'src/components/EducationSection.tsx',
'src/components/MilestonesSection.tsx',
'src/components/FooterSection.tsx',
].map((file) => readFile(file, 'utf8'))).then((files) => files.join('\n')),
]);
assert.doesNotMatch(globals, /content-visibility|contain-intrinsic-size/);
assert.doesNotMatch(sections, /data-deferred-render/);
});
test('project carousel uses native momentum with soft proximity snapping', async () => {
const [component, styles] = await Promise.all([
readFile('src/components/ProjectsSection.tsx', 'utf8'),
readFile('src/components/ProjectsSection.module.css', 'utf8'),
]);
assert.match(styles, /scroll-snap-type:\s*x proximity/);
assert.match(styles, /touch-action:\s*pan-x pan-y/);
assert.doesNotMatch(styles, /scroll-snap-type:\s*x mandatory/);
assert.doesNotMatch(styles, /scroll-snap-stop:\s*always/);
assert.match(component, /event\.pointerType !== 'mouse'/);
assert.match(
component,
/cancelAnimationFrame\(scrollFrameRef\.current\);\s*scrollFrameRef\.current = 0;/,
);
assert.doesNotMatch(component, /targetIndex|velocity fling|Math\.(?:ceil|floor|round)\(currentScroll \/ step\)/);
});
test('motion state is initialized for dependent section animations', async () => {
const [controller, footer] = await Promise.all([
readFile('src/components/MotionController.tsx', 'utf8'),
readFile('src/components/IsometricFooter/IsometricFooter.tsx', 'utf8'),
]);
assert.match(controller, /document\.documentElement\.dataset\.motionReady = 'true'/);
assert.match(controller, /element\.dataset\.visible = 'true'/);
assert.match(footer, /className=\{styles\.headerTitle\} data-reveal="soft"/);
assert.match(footer, /className=\{styles\.headerSubtitle\} data-reveal="soft"/);
assert.match(footer, /className=\{styles\.emailWrapper\} data-reveal="scale"/);
});