源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
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
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
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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);
|
||||
});
|
||||
Reference in New Issue
Block a user