Skip to content

Commit 2410448

Browse files
authored
Merge pull request #14 from Jobflow-io/locator-100
Locator 100
2 parents 1fc53e6 + cc2f18a commit 2410448

File tree

4 files changed

+1214
-3
lines changed

4 files changed

+1214
-3
lines changed

scripts/coverage.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ const MAPPINGS = [
1313
},
1414
{ pw: "Page", ep: "PlaywrightPageService", type: "interface" as const },
1515
{ pw: "Frame", ep: "PlaywrightFrameService", type: "interface" as const },
16+
{
17+
pw: "FrameLocator",
18+
ep: "PlaywrightFrameLocatorService",
19+
type: "interface" as const,
20+
},
1621
{ pw: "Locator", ep: "PlaywrightLocatorService", type: "interface" as const },
1722
{ pw: "Request", ep: "PlaywrightRequest", type: "class" as const },
1823
{ pw: "Response", ep: "PlaywrightResponse", type: "class" as const },

src/frame-locator.ts

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
import { Context, Match, Predicate } from "effect";
2+
import type { FrameLocator, Locator } from "playwright-core";
3+
import { PlaywrightLocator, type PlaywrightLocatorService } from "./locator";
4+
5+
/**
6+
* Interface for a Playwright frame locator.
7+
* @category model
8+
*/
9+
export interface PlaywrightFrameLocatorService {
10+
/**
11+
* The underlying Playwright FrameLocator instance.
12+
* @internal
13+
*/
14+
readonly _raw: FrameLocator;
15+
16+
/**
17+
* Returns locator to the first matching frame.
18+
*
19+
* @see {@link FrameLocator.first}
20+
* @since 0.1.0
21+
*/
22+
readonly first: () => PlaywrightFrameLocatorService;
23+
24+
/**
25+
* When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements
26+
* in that iframe.
27+
*
28+
* @see {@link FrameLocator.frameLocator}
29+
* @since 0.1.0
30+
*/
31+
readonly frameLocator: (selector: string) => PlaywrightFrameLocatorService;
32+
33+
/**
34+
* Returns locator to the last matching frame.
35+
*
36+
* @see {@link FrameLocator.last}
37+
* @since 0.1.0
38+
*/
39+
readonly last: () => PlaywrightFrameLocatorService;
40+
41+
/**
42+
* Returns locator to the n-th matching frame.
43+
*
44+
* @see {@link FrameLocator.nth}
45+
* @since 0.1.0
46+
*/
47+
readonly nth: (index: number) => PlaywrightFrameLocatorService;
48+
49+
/**
50+
* Returns a `Locator` object pointing to the same `iframe` as this frame locator.
51+
*
52+
* @see {@link FrameLocator.owner}
53+
* @since 0.1.0
54+
*/
55+
readonly owner: () => PlaywrightLocatorService;
56+
57+
/**
58+
* Finds an element matching the specified selector in the locator's subtree.
59+
*
60+
* @see {@link FrameLocator.locator}
61+
* @since 0.1.0
62+
*/
63+
readonly locator: (
64+
selectorOrLocator: string | Locator | PlaywrightLocatorService,
65+
options?: Parameters<FrameLocator["locator"]>[1],
66+
) => PlaywrightLocatorService;
67+
68+
/**
69+
* Allows locating elements by their ARIA role.
70+
*
71+
* @see {@link FrameLocator.getByRole}
72+
* @since 0.1.0
73+
*/
74+
readonly getByRole: (
75+
role: Parameters<FrameLocator["getByRole"]>[0],
76+
options?: Parameters<FrameLocator["getByRole"]>[1],
77+
) => PlaywrightLocatorService;
78+
79+
/**
80+
* Allows locating elements that contain given text.
81+
*
82+
* @see {@link FrameLocator.getByText}
83+
* @since 0.1.0
84+
*/
85+
readonly getByText: (
86+
text: Parameters<FrameLocator["getByText"]>[0],
87+
options?: Parameters<FrameLocator["getByText"]>[1],
88+
) => PlaywrightLocatorService;
89+
90+
/**
91+
* Allows locating elements by their label text.
92+
*
93+
* @see {@link FrameLocator.getByLabel}
94+
* @since 0.1.0
95+
*/
96+
readonly getByLabel: (
97+
text: Parameters<FrameLocator["getByLabel"]>[0],
98+
options?: Parameters<FrameLocator["getByLabel"]>[1],
99+
) => PlaywrightLocatorService;
100+
101+
/**
102+
* Allows locating elements by their placeholder text.
103+
*
104+
* @see {@link FrameLocator.getByPlaceholder}
105+
* @since 0.1.0
106+
*/
107+
readonly getByPlaceholder: (
108+
text: Parameters<FrameLocator["getByPlaceholder"]>[0],
109+
options?: Parameters<FrameLocator["getByPlaceholder"]>[1],
110+
) => PlaywrightLocatorService;
111+
112+
/**
113+
* Allows locating elements by their alt text.
114+
*
115+
* @see {@link FrameLocator.getByAltText}
116+
* @since 0.1.0
117+
*/
118+
readonly getByAltText: (
119+
text: Parameters<FrameLocator["getByAltText"]>[0],
120+
options?: Parameters<FrameLocator["getByAltText"]>[1],
121+
) => PlaywrightLocatorService;
122+
123+
/**
124+
* Allows locating elements by their title attribute.
125+
*
126+
* @see {@link FrameLocator.getByTitle}
127+
* @since 0.1.0
128+
*/
129+
readonly getByTitle: (
130+
text: Parameters<FrameLocator["getByTitle"]>[0],
131+
options?: Parameters<FrameLocator["getByTitle"]>[1],
132+
) => PlaywrightLocatorService;
133+
134+
/**
135+
* Allows locating elements by their test id.
136+
*
137+
* @see {@link FrameLocator.getByTestId}
138+
* @since 0.1.0
139+
*/
140+
readonly getByTestId: (
141+
testId: Parameters<FrameLocator["getByTestId"]>[0],
142+
) => PlaywrightLocatorService;
143+
}
144+
145+
/**
146+
* A service that provides a `PlaywrightFrameLocator` instance.
147+
*
148+
* @since 0.1.0
149+
* @category tag
150+
*/
151+
export class PlaywrightFrameLocator extends Context.Tag(
152+
"effect-playwright/PlaywrightFrameLocator",
153+
)<PlaywrightFrameLocator, PlaywrightFrameLocatorService>() {
154+
/**
155+
* Creates a `PlaywrightFrameLocator` from a Playwright `FrameLocator` instance.
156+
*
157+
* @param frameLocator - The Playwright `FrameLocator` instance to wrap.
158+
* @since 0.1.0
159+
* @category constructor
160+
*/
161+
static make(
162+
frameLocator: FrameLocator,
163+
): typeof PlaywrightFrameLocator.Service {
164+
const unwrap = Match.type<
165+
string | Locator | PlaywrightLocatorService
166+
>().pipe(
167+
Match.when(Predicate.hasProperty("_raw"), (l) => l._raw),
168+
Match.orElse((l) => l),
169+
);
170+
171+
return PlaywrightFrameLocator.of({
172+
_raw: frameLocator,
173+
first: () => PlaywrightFrameLocator.make(frameLocator.first()),
174+
frameLocator: (selector: string) =>
175+
PlaywrightFrameLocator.make(frameLocator.frameLocator(selector)),
176+
last: () => PlaywrightFrameLocator.make(frameLocator.last()),
177+
nth: (index: number) =>
178+
PlaywrightFrameLocator.make(frameLocator.nth(index)),
179+
owner: () => PlaywrightLocator.make(frameLocator.owner()),
180+
locator: (selectorOrLocator, options) =>
181+
PlaywrightLocator.make(
182+
frameLocator.locator(unwrap(selectorOrLocator), options),
183+
),
184+
getByRole: (role, options) =>
185+
PlaywrightLocator.make(frameLocator.getByRole(role, options)),
186+
getByText: (text, options) =>
187+
PlaywrightLocator.make(frameLocator.getByText(text, options)),
188+
getByLabel: (text, options) =>
189+
PlaywrightLocator.make(frameLocator.getByLabel(text, options)),
190+
getByPlaceholder: (text, options) =>
191+
PlaywrightLocator.make(frameLocator.getByPlaceholder(text, options)),
192+
getByAltText: (text, options) =>
193+
PlaywrightLocator.make(frameLocator.getByAltText(text, options)),
194+
getByTitle: (text, options) =>
195+
PlaywrightLocator.make(frameLocator.getByTitle(text, options)),
196+
getByTestId: (testId) =>
197+
PlaywrightLocator.make(frameLocator.getByTestId(testId)),
198+
});
199+
}
200+
}

0 commit comments

Comments
 (0)