Reduce export surface

This commit is contained in:
HarelM
2026-07-09 15:22:41 +03:00
parent 60ed80651c
commit 6c31add041
2 changed files with 29 additions and 26 deletions
+6 -13
View File
@@ -1,11 +1,4 @@
import {
Assertable,
PlaywrightHelper,
Query,
assertDeepNestedInclude,
readFixture,
retry,
} from "./playwright-helper";
import { Assertable, PlaywrightHelper } from "./playwright-helper";
import { ModalDriver } from "./modal-driver";
const baseUrl = "http://localhost:8888/";
@@ -22,9 +15,9 @@ export class MaputnikAssertable<T> extends Assertable<T> {
*/
shouldEqualToStoredStyle = async () => {
const expected = await (this.target as any);
await retry(async () => {
await this.retry(async () => {
const stored = await this.getStoredStyle();
assertDeepNestedInclude(expected, stored);
this.assertDeepNestedInclude(expected, stored);
});
};
}
@@ -202,14 +195,14 @@ export class MaputnikDriver {
skipTargetLayerEditor: () => this.helper.get.elementByTestId("skip-target-layer-editor"),
styleFromLocalStorage: () => new Query<any>(() => this.readStoredStyle()),
styleFromLocalStorage: () => this.helper.query(() => this.readStoredStyle()),
fixture: (name: string) => Promise.resolve(readFixture(name)),
fixture: (name: string) => Promise.resolve(this.helper.readFixture(name)),
responseBody: (alias: string) => {
// Our mocked style responses always return the matching fixture.
const name = alias.endsWith(".json") ? alias : `${alias}.json`;
return Promise.resolve(readFixture(name));
return Promise.resolve(this.helper.readFixture(name));
},
exampleFileUrl: () => baseUrl + "example-style.json",
+23 -13
View File
@@ -8,17 +8,12 @@ const DATA_ATTRIBUTE = "data-wd-key";
const FIXTURES_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), "fixtures");
const isMac = process.platform === "darwin";
export function testIdSelector(testId: string): string {
function testIdSelector(testId: string): string {
return `[${DATA_ATTRIBUTE}="${testId}"]`;
}
export function readFixture(name: string): any {
const contents = fs.readFileSync(path.join(FIXTURES_DIR, name), "utf-8");
return JSON.parse(contents);
}
/** Retries `assertion` until it stops throwing */
export async function retry(
async function retry(
assertion: () => Promise<void> | void,
timeout = 10000,
interval = 100
@@ -41,7 +36,7 @@ export async function retry(
* A lazily-evaluated value (e.g. the style in localStorage). Assertions on a
* Query re-read the value until they pass.
*/
export class Query<T> {
class Query<T> {
readonly __query = true as const;
constructor(private readonly getter: () => Promise<T>) {}
@@ -68,7 +63,7 @@ function isLocator(target: unknown): target is Locator {
}
/** Asserts that every top-level key in `expected` deep-equals its counterpart in `actual`. */
export function assertDeepNestedInclude(actual: any, expected: Record<string, unknown>): void {
function assertDeepNestedInclude(actual: any, expected: Record<string, unknown>): void {
for (const key of Object.keys(expected)) {
expect(actual?.[key], `property "${key}"`).toEqual(expected[key]);
}
@@ -82,6 +77,11 @@ export function assertDeepNestedInclude(actual: any, expected: Record<string, un
export class Assertable<T> {
constructor(protected readonly target: T) {}
// Exposed to subclasses (e.g. MaputnikAssertable) so custom assertions can be
// built without re-importing these module-private helpers.
protected readonly retry = retry;
protected readonly assertDeepNestedInclude = assertDeepNestedInclude;
private locator(): Locator {
if (!isLocator(this.target)) throw new Error("Expected a Locator target for this assertion");
return this.target;
@@ -139,7 +139,7 @@ export class Assertable<T> {
this.assertValue((actual) => assertDeepNestedInclude(actual, value));
}
export async function typeSequence(page: Page, text: string): Promise<void> {
async function typeSequence(page: Page, text: string): Promise<void> {
const tokens = text.match(/\{[^}]+\}|[^{]+/g) ?? [];
const modifierMap: Record<string, string> = { meta: "Meta", ctrl: "Control", shift: "Shift", alt: "Alt" };
const namedKeys: Record<string, string> = {
@@ -197,6 +197,16 @@ export class PlaywrightHelper {
return this.page.locator(testIdSelector(testId));
}
/** Reads and parses a JSON fixture from the fixtures directory. */
public readFixture(name: string): any {
return JSON.parse(fs.readFileSync(path.join(FIXTURES_DIR, name), "utf-8"));
}
/** Wraps a lazily-evaluated value so assertions on it auto-retry. */
public query<T>(getter: () => Promise<T>): Query<T> {
return new Query<T>(getter);
}
public given = {
intercept: async (pattern: RegExp, alias: string, _method = "GET") => {
this.recordedRequests.set(alias, []);
@@ -218,7 +228,7 @@ export class PlaywrightHelper {
if (alias) this.recordedRequests.get(alias)!.push(route.request());
const body =
response && typeof response === "object" && "fixture" in (response as any)
? readFixture((response as { fixture: string }).fixture)
? this.readFixture((response as { fixture: string }).fixture)
: response;
route.fulfill({ json: body });
});
@@ -326,7 +336,7 @@ export class PlaywrightHelper {
},
openFileByFixture: async (fixture: string, buttonTestId: string, inputTestId: string) => {
const content = JSON.stringify(readFixture(fixture));
const content = JSON.stringify(this.readFixture(fixture));
const hasPicker = await this.page.evaluate(() => "showOpenFilePicker" in window);
if (hasPicker) {
await this.page.evaluate((fileContent) => {
@@ -345,7 +355,7 @@ export class PlaywrightHelper {
},
dropFileByFixture: async (fixture: string, dropzoneTestId: string) => {
const content = JSON.stringify(readFixture(fixture));
const content = JSON.stringify(this.readFixture(fixture));
const dataTransfer = await this.page.evaluateHandle((fileContent) => {
const dt = new DataTransfer();
dt.items.add(new File([fileContent], "example-style.json", { type: "application/json" }));