git.iko.soy/rowtype-yoga/purescript-playwright
on main help sign in
Release playwright 0.0.1: portable bindings and registry metadata
commit86c655b78b1cae7d1254613b297d1e22649fcdbe authorMark <mark.eibes@gmail.com> dateSun 13 Sep 2026, 00:22 (1 month ago)
8 files changed+209
.gitignore+7 LICENSE+21 package.json+11 spago.yaml+21 src/Playwright.js+49 src/Playwright.purs+88 src/Playwright/Test.js+3 src/Playwright/Test.purs+9
.gitignorenew file+7
@@ -0,0 +1,7 @@
1+/.jj/
2+/.spago/
3+/node_modules/
4+/output/
5+/output-es/
6+/spago.lock
7+.DS_Store
LICENSEnew file+21
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) 2026 Mark Eibes
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
package.jsonnew file+11
@@ -0,0 +1,11 @@
1+{
2+ "name": "purescript-playwright",
3+ "version": "0.0.1",
4+ "license": "MIT",
5+ "repository": "github:rowtype-yoga/purescript-playwright",
6+ "private": true,
7+ "type": "module",
8+ "peerDependencies": {
9+ "@playwright/test": "^1.60.0"
10+ }
11+}
spago.yamlnew file+21
@@ -0,0 +1,21 @@
1+package:
2+ name: playwright
3+ dependencies:
4+ - effect: ">=4.0.0 <5.0.0"
5+ - foreign: ">=7.0.0 <8.0.0"
6+ - js-promise: ">=1.0.0 <2.0.0"
7+ - nullable: ">=6.0.0 <7.0.0"
8+ - prelude: ">=6.0.1 <7.0.0"
9+ publish:
10+ version: 0.0.1
11+ license: MIT
12+ include:
13+ - package.json
14+ - LICENSE
15+ location:
16+ githubOwner: rowtype-yoga
17+ githubRepo: purescript-playwright
18+
19+workspace:
20+ packageSet:
21+ registry: 77.3.1
src/Playwright.jsnew file+49
@@ -0,0 +1,49 @@
1+import { chromium } from '@playwright/test';
2+
3+export const launchChromium = options => () => chromium.launch({
4+ executablePath: options.executablePath === null ? undefined : options.executablePath,
5+ args: options.args,
6+});
7+export const close = browser => () => browser.close();
8+export const newPage = browser => options => () => browser.newPage(options);
9+export const goto = page => url => () => page.goto(url).then(() => {});
10+export const locator = page => selector => page.locator(selector);
11+export const getByRole = page => role => options => page.getByRole(role, options);
12+export const click = target => () => target.click();
13+export const setChecked = target => checked => () => target.setChecked(checked);
14+export const selectOption = target => value => () => target.selectOption(value).then(() => {});
15+export const screenshot = page => options => () => page.screenshot(options).then(() => {});
16+export const waitForGlobalFunction = page => name => () => page.waitForFunction(
17+ name => typeof window[name] === 'function', name,
18+).then(() => {});
19+
20+export const invokeGlobal = page => name => applications => () => page.evaluate(
21+ ({ name, applications }) => {
22+ let receiver = window;
23+ let result = receiver[name];
24+ for (const args of applications) {
25+ result = result.apply(receiver, args);
26+ receiver = undefined;
27+ }
28+ return result;
29+ }, { name, applications },
30+).then(() => {});
31+
32+export const performImpl = target => operations => () => target.evaluate(
33+ (element, operations) => {
34+ for (const operation of operations) {
35+ switch (operation.operation) {
36+ case 'setNativeProperty': {
37+ const prototype = window[operation.prototypeName].prototype;
38+ Object.getOwnPropertyDescriptor(prototype, operation.propertyName).set.call(element, operation.value);
39+ break;
40+ }
41+ case 'dispatchEvent':
42+ element.dispatchEvent(new Event(operation.name, operation.options));
43+ break;
44+ default:
45+ throw new Error(`Unknown element operation: ${operation.operation}`);
46+ }
47+ }
48+ }, operations,
49+);
src/Playwright.pursnew file+88
@@ -0,0 +1,88 @@
1+module Playwright
2+ ( Browser
3+ , Page
4+ , Locator
5+ , LaunchOptions
6+ , PageOptions
7+ , RoleOptions
8+ , ScreenshotOptions
9+ , ElementOperation(..)
10+ , launchChromium
11+ , close
12+ , newPage
13+ , goto
14+ , locator
15+ , getByRole
16+ , click
17+ , setChecked
18+ , selectOption
19+ , screenshot
20+ , waitForGlobalFunction
21+ , invokeGlobal
22+ , perform
23+ ) where
24+
25+import Prelude
26+
27+import Data.Nullable (Nullable)
28+import Effect (Effect)
29+import Foreign (Foreign, unsafeToForeign)
30+import Promise (Promise)
31+
32+type LaunchOptions =
33+ { executablePath :: Nullable String
34+ , args :: Array String
35+ }
36+
37+type PageOptions =
38+ { viewport :: { width :: Int, height :: Int }
39+ }
40+
41+type RoleOptions =
42+ { name :: String
43+ , exact :: Boolean
44+ }
45+
46+type ScreenshotOptions =
47+ { path :: String
48+ , fullPage :: Boolean
49+ }
50+
51+-- | Operations run synchronously, in order, in one locator evaluation.
52+-- | Native setters are resolved from the named browser-global constructor's
53+-- | prototype, bypassing instance overrides installed by UI frameworks.
54+data ElementOperation
55+ = SetNativeProperty String String Foreign
56+ | DispatchEvent String { bubbles :: Boolean }
57+
58+perform :: Locator -> Array ElementOperation -> Effect (Promise Unit)
59+perform target operations = performImpl target (map encode operations)
60+ where
61+ encode = case _ of
62+ SetNativeProperty prototypeName propertyName value -> unsafeToForeign
63+ { operation: "setNativeProperty", prototypeName, propertyName, value }
64+ DispatchEvent name options -> unsafeToForeign
65+ { operation: "dispatchEvent", name, options }
66+
67+foreign import data Browser :: Type
68+foreign import data Page :: Type
69+foreign import data Locator :: Type
70+
71+foreign import launchChromium :: LaunchOptions -> Effect (Promise Browser)
72+foreign import close :: Browser -> Effect (Promise Unit)
73+foreign import newPage :: Browser -> PageOptions -> Effect (Promise Page)
74+foreign import goto :: Page -> String -> Effect (Promise Unit)
75+foreign import locator :: Page -> String -> Locator
76+foreign import getByRole :: Page -> String -> RoleOptions -> Locator
77+foreign import click :: Locator -> Effect (Promise Unit)
78+foreign import setChecked :: Locator -> Boolean -> Effect (Promise Unit)
79+foreign import selectOption :: Locator -> String -> Effect (Promise Unit)
80+foreign import screenshot :: Page -> ScreenshotOptions -> Effect (Promise Unit)
81+foreign import waitForGlobalFunction :: Page -> String -> Effect (Promise Unit)
82+
83+-- | Apply a named window function, then each returned function, to successive
84+-- | argument lists. An empty list of arguments invokes a zero-argument thunk.
85+-- | The final promise is awaited; remote exceptions reject the operation.
86+foreign import invokeGlobal :: Page -> String -> Array (Array Foreign) -> Effect (Promise Unit)
87+
88+foreign import performImpl :: Locator -> Array Foreign -> Effect (Promise Unit)
src/Playwright/Test.jsnew file+3
@@ -0,0 +1,3 @@
1+import { test as register } from '@playwright/test';
2+
3+export const test = name => run => () => register(name, ({ page }) => run(page)());
src/Playwright/Test.pursnew file+9
@@ -0,0 +1,9 @@
1+module Playwright.Test (test) where
2+
3+import Prelude
4+
5+import Effect (Effect)
6+import Playwright (Page)
7+import Promise (Promise)
8+
9+foreign import test :: String -> (Page -> Effect (Promise Unit)) -> Effect Unit