Files
maka/apps/desktop/e2e/code-scroll.spec.ts
T
agent-radar 7be535059e
Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 19:05:14 +08:00

126 lines
4.7 KiB
TypeScript

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { expect, test, COMPOSER_INPUT } from './fixtures';
test('a one-line Markdown code block exposes native and selection horizontal scrolling', async ({
window: page,
}) => {
await page.setViewportSize({ width: 900, height: 700 });
const longLine = Array.from(
{ length: 80 },
(_, index) => `word-${String(index).padStart(3, '0')}`,
).join(' ');
const composer = page.locator(COMPOSER_INPUT);
await composer.fill([
'show these keys',
'',
'```',
'short-key',
'```',
'',
'```',
longLine,
'```',
].join('\n'));
await composer.press('Enter');
const codeBlocks = page.locator('.maka-markdown-code[data-maka-code-layout="single-line"]');
const viewport = codeBlocks.last().locator('[role="group"]');
await expect(viewport).toBeVisible();
await expect(page.getByRole('button', { name: '重新生成' })).toHaveCount(1, {
timeout: 20_000,
});
const metrics = await viewport.evaluate((element) => {
const node = element as HTMLElement;
const rect = node.getBoundingClientRect();
const code = node.querySelector('code');
const line = code?.querySelector(':scope > [data-line]');
if (!code || !line) throw new Error('code viewport has no line content');
const codeRect = code.getBoundingClientRect();
const lineRect = line.getBoundingClientRect();
return {
rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
clientWidth: node.clientWidth,
scrollWidth: node.scrollWidth,
lineTopInset: lineRect.top - codeRect.top,
lineBottomInset: codeRect.bottom - lineRect.bottom,
};
});
expect(metrics.scrollWidth).toBeGreaterThan(metrics.clientWidth);
expect(Math.abs(metrics.lineTopInset - metrics.lineBottomInset)).toBeLessThanOrEqual(1);
const overflowX = await viewport.evaluate((element) => getComputedStyle(element).overflowX);
expect(overflowX).toBe('auto');
const viewportBox = await viewport.boundingBox();
if (!viewportBox) throw new Error('native scroll viewport has no visible bounds');
await page.mouse.move(
viewportBox.x + viewportBox.width / 2,
viewportBox.y + viewportBox.height / 2,
);
await page.mouse.wheel(240, 0);
await expect.poll(
() => viewport.evaluate((element) => (element as HTMLElement).scrollLeft),
).toBeGreaterThan(0);
const afterWheelScroll = await viewport.evaluate(
(element) => (element as HTMLElement).scrollLeft,
);
await viewport.evaluate((element) => {
(element as HTMLElement).scrollLeft = 0;
});
await viewport.focus();
await viewport.press('ArrowRight');
await expect.poll(
() => viewport.evaluate((element) => (element as HTMLElement).scrollLeft),
).toBeGreaterThan(0);
const afterKeyboardScroll = await viewport.evaluate(
(element) => (element as HTMLElement).scrollLeft,
);
await viewport.evaluate((element) => {
(element as HTMLElement).scrollLeft = 0;
window.getSelection()?.removeAllRanges();
});
const code = viewport.locator('code');
const codeBox = await code.boundingBox();
if (!codeBox) throw new Error('code line has no visible bounds');
const textY = codeBox.y + Math.min(codeBox.height / 2, 18);
await page.mouse.move(codeBox.x + 24, textY);
await page.mouse.down();
await page.mouse.move(metrics.rect.x + metrics.rect.width + 50, textY, { steps: 20 });
await expect.poll(
() => viewport.evaluate((element) => (element as HTMLElement).scrollLeft),
).toBeGreaterThan(0);
await expect.poll(
() => viewport.evaluate(() => window.getSelection()?.toString().length ?? 0),
).toBeGreaterThan(10);
const afterSelectionDrag = await viewport.evaluate((element) => ({
scrollLeft: (element as HTMLElement).scrollLeft,
selection: window.getSelection()?.toString() ?? '',
}));
await page.mouse.up();
expect(afterWheelScroll).toBeGreaterThan(0);
expect(afterKeyboardScroll).toBeGreaterThan(0);
expect(afterSelectionDrag.scrollLeft).toBeGreaterThan(0);
expect(afterSelectionDrag.selection.length).toBeGreaterThan(10);
});