harper/packages/chrome-plugin/tests/testUtils.ts
Elijah Potter b033b9bf6d
Some checks are pending
Binaries / harper-cli - macOS-aarch64 (push) Waiting to run
Binaries / harper-cli - Linux-aarch64-GNU (push) Waiting to run
Binaries / harper-cli - Linux-aarch64-musl (push) Waiting to run
Binaries / harper-cli - macOS-x86_64 (push) Waiting to run
Binaries / harper-cli - Linux-x86_64-GNU (push) Waiting to run
Binaries / harper-cli - Linux-x86_64-musl (push) Waiting to run
Binaries / harper-cli - Windows-x86_64 (push) Waiting to run
Binaries / harper-ls - macOS-aarch64 (push) Waiting to run
Binaries / harper-ls - Linux-aarch64-GNU (push) Waiting to run
Binaries / harper-ls - Linux-aarch64-musl (push) Waiting to run
Binaries / harper-ls - macOS-x86_64 (push) Waiting to run
Binaries / harper-ls - Linux-x86_64-GNU (push) Waiting to run
Binaries / harper-ls - Linux-x86_64-musl (push) Waiting to run
Binaries / harper-ls - Windows-x86_64 (push) Waiting to run
Build Web / build-web (push) Waiting to run
Chrome Plugin / chrome-plugin (push) Waiting to run
Just Checks / just build-obsidian (push) Waiting to run
Just Checks / just test-harperjs (push) Waiting to run
Just Checks / just test-obsidian (push) Waiting to run
Just Checks / just test-rust (push) Waiting to run
Just Checks / just test-vscode (push) Waiting to run
VS Code Plugin / alpine-arm64 (push) Waiting to run
VS Code Plugin / darwin-arm64 (push) Waiting to run
VS Code Plugin / linux-armhf (push) Waiting to run
VS Code Plugin / linux-x64 (push) Waiting to run
WordPress Plugin / wp-plugin (push) Waiting to run
Just Checks / just check-js (push) Waiting to run
Just Checks / just check-rust (push) Waiting to run
Just Checks / just test-chrome-plugin (push) Waiting to run
Just Checks / just test-firefox-plugin (push) Waiting to run
VS Code Plugin / alpine-x64 (push) Waiting to run
VS Code Plugin / darwin-x64 (push) Waiting to run
VS Code Plugin / linux-arm64 (push) Waiting to run
VS Code Plugin / win32-arm64 (push) Waiting to run
VS Code Plugin / win32-x64 (push) Waiting to run
chore: reduce dependency load for new contributors (#2131)
2025-11-04 13:53:01 -07:00

144 lines
4.4 KiB
TypeScript

import type { Locator, Page } from '@playwright/test';
import type { Box } from 'lint-framework';
import { expect, test } from './fixtures';
export function randomString(length: number): string {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
/** Locate the [`Slate`](https://www.slatejs.org/examples/richtext) editor on the page. */
export function getSlateEditor(page: Page): Locator {
return page.locator('[data-slate-editor="true"]');
}
/** Locate the [`Lexical`](https://lexical.dev/) editor on the page. */
export function getLexicalEditor(page: Page): Locator {
return page.locator('[data-lexical-editor="true"]');
}
/** Locate the ProseMirror editor on the page. */
export function getProseMirrorEditor(page: Page): Locator {
return page.locator('.ProseMirror');
}
/** Replace the content of a text editor. */
export async function replaceEditorContent(editorEl: Locator, text: string) {
await editorEl.selectText();
await editorEl.press('Backspace');
await editorEl.pressSequentially(text);
}
/** Locate the Harper highlights on a page. */
export function getHarperHighlights(page: Page): Locator {
return page.locator('#harper-highlight');
}
/** Locates the first Harper highlight on the page and clicks it.
* It should result in the popup opening.
* Returns whether the highlight was found. */
export async function clickHarperHighlight(page: Page): Promise<boolean> {
const highlights = getHarperHighlights(page);
// Wait briefly for at least one highlight to appear.
// If none appear within a reasonable time, return false.
try {
await highlights.first().waitFor({ state: 'visible', timeout: 5000 });
} catch {
return false;
}
const box = await highlights.first().boundingBox();
if (box == null) return false;
// Locate the center of the element and click to open the popup.
const cx = box.x + box.width / 2;
const cy = box.y + box.height / 2;
await page.mouse.click(cx, cy);
return true;
}
/** Grab the first `<textarea />` on a page. */
export function getTextarea(page: Page): Locator {
return page.locator('textarea');
}
export async function testBasicSuggestionTextarea(testPageUrl: string) {
test('Can apply basic suggestion.', async ({ page }) => {
await page.goto(testPageUrl);
await page.waitForTimeout(2000);
await page.reload();
const editor = getTextarea(page);
await replaceEditorContent(editor, 'This is an test');
await page.waitForTimeout(6000);
await clickHarperHighlight(page);
await page.getByTitle('Replace with "a"').click();
await page.waitForTimeout(3000);
expect(editor).toHaveValue('This is a test');
});
}
export async function testCanIgnoreTextareaSuggestion(testPageUrl: string) {
test('Can ignore suggestion.', async ({ page }) => {
await page.goto(testPageUrl);
await page.waitForTimeout(2000);
await page.reload();
const editor = getTextarea(page);
const cacheSalt = randomString(5);
await replaceEditorContent(editor, cacheSalt);
await page.waitForTimeout(6000);
// Open the popup for the first highlight and click Ignore.
const opened = await clickHarperHighlight(page);
expect(opened).toBe(true);
await page.getByTitle('Ignore this lint').click();
// Wait for highlights to disappear after ignoring.
await expect(getHarperHighlights(page)).toHaveCount(0);
// Nothing should change.
expect(editor).toHaveValue(cacheSalt);
expect(await clickHarperHighlight(page)).toBe(false);
});
}
export async function assertHarperHighlightBoxes(page: Page, boxes: Box[]): Promise<void> {
const highlights = getHarperHighlights(page);
expect(await highlights.count()).toBe(boxes.length);
for (let i = 0; i < (await highlights.count()); i++) {
const box = await highlights.nth(i).boundingBox();
console.log(`Expected: ${JSON.stringify(boxes[i])}`);
console.log(`Got: ${JSON.stringify(box)}`);
assertBoxesClose(box, boxes[i]);
}
}
/** An assertion that checks to ensure that two boxes are _approximately_ equal.
* Leaves wiggle room for floating point error. */
export function assertBoxesClose(a: Box, b: Box) {
assertClose(a.x, b.x);
assertClose(a.y, b.y);
assertClose(a.width, b.width);
assertClose(a.height, b.height);
}
function assertClose(actual: number, expected: number) {
expect(Math.abs(actual - expected)).toBeLessThanOrEqual(15);
}