Skip to content

Commit 4e86ecd

Browse files
committed
chore: add browser tests for scroll indicators and reading progress bar functionalities
1 parent 7f37c7a commit 4e86ecd

1 file changed

Lines changed: 112 additions & 0 deletions

File tree

tests/Browser/PostTest.php

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,115 @@
3535

3636
$page->assertDontSee('目錄');
3737
});
38+
39+
test('right scroll indicator appears when a code block overflows horizontally', function () {
40+
// a monospace line >150 chars is wider than the 768px center column
41+
$longLine = str_repeat('echo("aaaaaaaaaaaaaaaa"); ', 12);
42+
43+
$body = <<<HTML
44+
<pre><code class="language-php">{$longLine}</code></pre>
45+
HTML;
46+
47+
$post = Post::factory()->create(['body' => $body]);
48+
49+
$page = $this->visit($post->link_with_slug);
50+
$page->assertSee($post->title);
51+
52+
// Shiki highlight + fonts.ready + ResizeObserver all need to settle
53+
// before the indicator opacity flips, so poll instead of guessing a wait().
54+
$opacity = $page->script(<<<'JS'
55+
new Promise((resolve) => {
56+
const deadline = Date.now() + 5000;
57+
const check = () => {
58+
const el = document.querySelector('.scroll-indicator-right');
59+
if (el && el.style.opacity === '1') {
60+
return resolve('1');
61+
}
62+
if (Date.now() > deadline) {
63+
return resolve(el ? el.style.opacity : 'missing');
64+
}
65+
requestAnimationFrame(check);
66+
};
67+
check();
68+
})
69+
JS
70+
);
71+
72+
expect($opacity)->toBe('1');
73+
});
74+
75+
test('scroll indicators flip as the user scrolls a code block', function () {
76+
$longLine = str_repeat('echo("aaaaaaaaaaaaaaaa"); ', 12);
77+
78+
$body = <<<HTML
79+
<pre><code class="language-php">{$longLine}</code></pre>
80+
HTML;
81+
82+
$post = Post::factory()->create(['body' => $body]);
83+
84+
$page = $this->visit($post->link_with_slug);
85+
$page->assertSee($post->title);
86+
87+
// wait for fonts AND for the right indicator to flip to '1'.
88+
// Returns the indicator opacity so we can fail fast if it never settled.
89+
$ready = $page->script(<<<'JS'
90+
new Promise(async (resolve) => {
91+
await document.fonts.ready;
92+
const deadline = Date.now() + 5000;
93+
const check = () => {
94+
const right = document.querySelector('.scroll-indicator-right');
95+
if (right && right.style.opacity === '1') return resolve('1');
96+
if (Date.now() > deadline) return resolve(right ? right.style.opacity : 'missing');
97+
requestAnimationFrame(check);
98+
};
99+
check();
100+
})
101+
JS
102+
);
103+
expect($ready)->toBe('1');
104+
105+
// scroll to the far right (999999 → browser clamps to true max scroll)
106+
$state = $page->script(<<<'JS'
107+
new Promise((resolve) => {
108+
const pre = document.querySelector('pre.shiki');
109+
pre.addEventListener('scroll', () => {
110+
requestAnimationFrame(() => {
111+
resolve({
112+
left: document.querySelector('.scroll-indicator-left').style.opacity,
113+
right: document.querySelector('.scroll-indicator-right').style.opacity,
114+
});
115+
});
116+
}, { once: true });
117+
pre.scrollLeft = 999999;
118+
})
119+
JS
120+
);
121+
122+
// at the far right: left indicator visible, right indicator hidden
123+
expect($state)->toMatchArray(['left' => '1', 'right' => '0']);
124+
});
125+
126+
test('reading progress bar starts at 0 and advances as user scrolls', function () {
127+
// long body so the post is taller than the viewport
128+
$body = str_repeat('<p>'.fake()->paragraph(20).'</p>', 30);
129+
130+
$post = Post::factory()->create(['body' => $body]);
131+
132+
$page = $this->visit($post->link_with_slug);
133+
134+
// assertSee on the title naturally waits for the isReady gate to flip
135+
// (title sits inside x-show="isReady"), so by the time this passes,
136+
// setupProgressBar has run on a laid-out section.
137+
$page->assertSee($post->title)
138+
->assertAttribute('[role=progressbar]', 'aria-valuenow', '0');
139+
140+
// scroll halfway down and let the scroll handler fire
141+
$page->script('window.scrollTo(0, document.documentElement.scrollHeight / 2)');
142+
$page->wait(1);
143+
144+
$progress = (int) $page->script(
145+
'document.querySelector("[role=progressbar]").getAttribute("aria-valuenow")'
146+
);
147+
148+
expect($progress)->toBeGreaterThan(0);
149+
});

0 commit comments

Comments
 (0)