Merge all forks upstream + latest spago and fix tests (#3)

* Fix compiling errors building with purs/psc-set 0.14.2

* Remove bower and pulp

* Add pacakge-lock.json

* Update playwright minimum version

* evaluation needs to be one expression/statement

* Add connect/connectOverCDP functions

* Convert FFI to more modern JS

* Add bindings for focus, fill, connect among others

* Make ready for 0.15 but drop tests

* Use milliseconds for timeouts

* update spago and esm

* fix tests

* Revert "fix tests"

This reverts commit aec92cb08e.

* fix effectfulGetter

* add package-lock.json

* update .gitignore

* update CI, aff affCall to context

---------

Co-authored-by: kamoii <>
Co-authored-by: Mark Eibes <mark.eibes@gmail.com>
Co-authored-by: phtz <spamsucks@posteo.de>
This commit is contained in:
fetsorn 2024-07-11 18:59:58 +04:00 committed by GitHub
parent 3638634ae1
commit 6d0aba0f34
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 1711 additions and 261 deletions

View File

@ -16,5 +16,6 @@ jobs:
run: | run: |
PATH="$PATH:./node_modules/.bin/" PATH="$PATH:./node_modules/.bin/"
npm install npm install
npm install spago purescript npm install spago@next purescript
npx playwright install --with-deps chromium
spago test spago test

2
.gitignore vendored
View File

@ -7,3 +7,5 @@
/.psc* /.psc*
/.purs* /.purs*
/.psa* /.psa*
/.spago/
/.DS_Store

View File

@ -1,17 +0,0 @@
{
"name": "purescript-playwright",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"output"
],
"dependencies": {
"purescript-prelude": "^4.1.1",
"purescript-console": "^4.4.0",
"purescript-effect": "^2.0.1"
},
"devDependencies": {
"purescript-psci-support": "^4.0.0"
}
}

57
package-lock.json generated Normal file
View File

@ -0,0 +1,57 @@
{
"name": "purescript-playwright",
"version": "0.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "purescript-playwright",
"version": "0.0.1",
"license": "BSD-3-Clause",
"dependencies": {
"playwright": "^1.45.1"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/playwright": {
"version": "1.45.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.1.tgz",
"integrity": "sha512-Hjrgae4kpSQBr98nhCj3IScxVeVUixqj+5oyif8TdIn2opTCPEzqAqNMeK42i3cWDCVu9MI+ZsGWw+gVR4ISBg==",
"dependencies": {
"playwright-core": "1.45.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.45.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.1.tgz",
"integrity": "sha512-LF4CUUtrUu2TCpDw4mcrAIuYrEjVDfT1cHbJMfwnE2+1b8PZcFzPNgvZCvq2JfQ4aTjRCCHw5EJ2tmr2NSzdPg==",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
}
}
}

View File

@ -7,11 +7,8 @@
"test": "test" "test": "test"
}, },
"dependencies": { "dependencies": {
"bower": "^1.8.8", "playwright": "^1.45.1"
"playwright": "^1.3.0",
"pulp": "^15.0.0"
}, },
"devDependencies": {},
"scripts": { "scripts": {
"test": "pulp test" "test": "pulp test"
}, },

View File

@ -1,42 +0,0 @@
let upstream =
https://github.com/purescript/package-sets/releases/download/psc-0.14.2-20210629/packages.dhall sha256:534c490bb73cae75adb5a39871142fd8db5c2d74c90509797a80b8bb0d5c3f7b
let overrides =
{ untagged-union =
{ dependencies =
[ "assert"
, "console"
, "effect"
, "foreign"
, "foreign-object"
, "literals"
, "maybe"
, "newtype"
, "psci-support"
, "tuples"
, "unsafe-coerce"
]
, repo = "https://github.com/jvliwanag/purescript-untagged-union.git"
, version = "v0.3.0"
}
}
let additions =
{ literals =
{ dependencies =
[ "assert"
, "effect"
, "console"
, "integers"
, "numbers"
, "partial"
, "psci-support"
, "unsafe-coerce"
, "typelevel-prelude"
]
, repo = "https://github.com/jvliwanag/purescript-literals.git"
, version = "7b2ae20f77c67b7e419a92fdd0dc7a09b447b18e"
}
}
in upstream ⫽ overrides ⫽ additions

View File

@ -1,29 +0,0 @@
{ name = "playwright"
, dependencies =
[ "argonaut-core"
, "console"
, "effect"
, "prelude"
, "psci-support"
, "aff-promise"
, "test-unit"
, "untagged-union"
, "node-buffer"
, "node-fs-aff"
, "undefined"
, "aff"
, "either"
, "exceptions"
, "foreign"
, "foreign-object"
, "literals"
, "maybe"
, "node-streams"
, "ordered-collections"
, "refs"
, "strings"
, "transformers"
]
, packages = ./packages.dhall
, sources = [ "src/**/*.purs", "test/**/*.purs" ]
}

1386
spago.lock Normal file

File diff suppressed because it is too large Load Diff

30
spago.yaml Normal file
View File

@ -0,0 +1,30 @@
package:
dependencies:
- aff
- aff-promise
- argonaut-core
- console
- datetime
- effect
- either
- exceptions
- foreign
- foreign-object
- literals
- maybe
- node-buffer
- node-fs
- node-streams
- ordered-collections
- prelude
- psci-support
- refs
- strings
- test-unit
- transformers
- untagged-union
- literals
name: playwright
workspace:
packageSet:
registry: 53.2.0

View File

@ -1,15 +1,21 @@
/* global exports */ /* global exports */
exports.exposeBinding_ = function (x) { export const exposeBinding_ = x => name => cb => opts => () => {
return function (name) { return x.exposeBinding(
return function (cb) { name,
return function (opts) { function (info, arg) {
return function () { return cb(info)(arg)()
return x.exposeBinding(name, function (info, arg) { },
return cb(info)(arg)(); opts
}, opts); )
}; }
};
export const onResponse = function (page) {
return function (cb) {
return function () {
page.on('response', function (response) {
cb(response)();
});
}; };
}; };
}; };

View File

@ -1,7 +1,10 @@
module Playwright module Playwright
( launch ( launch
, connect
, connectOverCDP
, close , close
, contexts , contexts
, context
, isConnected , isConnected
, version , version
, newPage , newPage
@ -9,6 +12,7 @@ module Playwright
, goBack , goBack
, goto , goto
, addCookies , addCookies
, cookies
, hover , hover
, innerHTML , innerHTML
, innerText , innerText
@ -40,25 +44,45 @@ module Playwright
, setViewportSize , setViewportSize
, title , title
, exposeBinding , exposeBinding
, fill
, focus
, onResponse
, connect
, module Playwright.Data , module Playwright.Data
, module Playwright.Options , module Playwright.Options
) )
where where
import Playwright.Options
import Control.Promise (Promise, fromAff, toAffE) import Control.Promise (Promise, fromAff, toAffE)
import Data.String.Regex (Regex) import Data.String.Regex (Regex)
import Data.Unit (unit)
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff) import Effect.Aff (Aff)
import Foreign (Foreign, unsafeToForeign) import Foreign (Foreign, unsafeToForeign)
import Literals.Null (Null) import Literals.Null (Null)
import Node.Buffer (Buffer) import Node.Buffer (Buffer)
import Playwright.Data import Playwright.Data (Browser, BrowserContext, BrowserType, ConsoleMessage, Dialog, Download, ElementHandle, ElementState, FileChooser, Frame, JSHandle, Keyboard, Modifier, Mouse, MouseButton, Page, Raf, Request, Response, Route, ScreenshotType, Selector(..), Selectors, URL(..), WaitUntil, Worker, alt, attached, chromium, control, detached, domcontentloaded, firefox, hidden, jpg, left, load, meta, middle, networkidle, png, raf, right, shift, visible, webkit)
import Playwright.Internal (effCall, effProp, affCall) import Playwright.Internal (effCall, effProp, affCall)
import Playwright.Options
import Prelude (Unit, ($)) import Prelude (Unit, ($))
import Undefined (undefined)
import Untagged.Castable (class Castable) import Untagged.Castable (class Castable)
import Untagged.Union (type (|+|), UndefinedOr) import Untagged.Union (type (|+|), UndefinedOr)
import Playwright.Types (Cookie)
foreign import onResponse :: Page -> (Response -> Effect Unit) -> Effect Unit
fill
:: forall o
. Castable o FillOptions
=> Page -> Selector -> String -> o -> Aff Unit
fill = affCall "fill" \_ -> fill
focus
:: forall o
. Castable o FocusOptions
=> Page -> Selector -> o -> Aff Unit
focus = affCall "focus" \_ -> focus
launch launch
:: forall o :: forall o
@ -67,6 +91,35 @@ launch
launch = launch =
affCall "launch" \_ -> launch affCall "launch" \_ -> launch
type WebSocketEndpoint = String
connect
:: forall o
. Castable o ConnectOptions
=> BrowserType
-> WebSocketEndpoint
-> o
-> Aff Browser
connect = affCall "connect" \_ -> connect
type ConnectOptions =
{ timeout :: UndefinedOr Number
}
connectOverCDP
:: forall o
. Castable o ConnectOverCDPOptions
=> BrowserType
-> String
-> o
-> Aff Browser
connectOverCDP =
affCall "connectOverCDP" \_ -> connectOverCDP
type ConnectOverCDPOptions =
{ timeout :: UndefinedOr Number
}
close close
:: forall x :: forall x
. Castable x (Browser |+| BrowserContext |+| Page) . Castable x (Browser |+| BrowserContext |+| Page)
@ -116,12 +169,15 @@ goto
goto = goto =
affCall "goto" \_ -> goto affCall "goto" \_ -> goto
type Cookie = context :: Page -> BrowserContext
{ name :: String context = affCall "context" \_ -> context
, value :: String
, url :: UndefinedOr String
}
cookies
:: BrowserContext
-> Aff (Array Cookie)
cookies =
affCall "cookies" \_ -> cookies
addCookies addCookies
:: BrowserContext :: BrowserContext
-> Array Cookie -> Array Cookie
@ -320,7 +376,7 @@ waitForFunction
-- ^ Function to be evaluated in browser context -- ^ Function to be evaluated in browser context
-> o -> o
-> Aff JSHandle -> Aff JSHandle
waitForFunction x s o = waitForFunction' x s (unsafeToForeign undefined) o waitForFunction x s o = waitForFunction' x s unit o
where where
waitForFunction' = affCall "waitForFunction" \_ -> waitForFunction' waitForFunction' = affCall "waitForFunction" \_ -> waitForFunction'

View File

@ -1,31 +1,36 @@
/* global require exports */ /* global require exports */
var P = require('playwright'); import { chromium as pwChromium, firefox as pwFirefox, webkit as pwWebkit } from 'playwright';
exports.png = "png"; export const png = "png";
exports.jpg = "jpg"; export const jpg = "jpg";
exports.chromium = P.chromium; export const chromium = pwChromium;
exports.firefox = P.firefox; export const firefox = pwFirefox;
exports.webkit = P.webkit; export const webkit = pwWebkit;
exports.domcontentloaded = "domcontentloaded"; export const domcontentloaded = "domcontentloaded";
exports.load = "load"; export const load = "load";
exports.networkidle = "networkidle"; export const networkidle = "networkidle";
exports.alt = "Alt"; export const alt = "Alt";
exports.control = "Control"; export const control = "Control";
exports.meta = "Meta"; export const meta = "Meta";
exports.shift = "Shift"; export const shift = "Shift";
exports.null = null; const _null = null;
export { _null as null };
exports.left = "left"; export const left = "left";
exports.right = "right"; export const right = "right";
exports.middle = "middle"; export const middle = "middle";
exports.attached = "attached"; export const attached = "attached";
exports.detached = "detached"; export const detached = "detached";
exports.visible = "visible"; export const visible = "visible";
exports.hidden = "hidden"; export const hidden = "hidden";
exports.raf = "raf"; export const raf = "raf";
export const strict = "Strict";
export const lax = "Lax";
export const none = "None";

View File

@ -65,5 +65,10 @@ foreign import detached :: ElementState
foreign import visible :: ElementState foreign import visible :: ElementState
foreign import hidden :: ElementState foreign import hidden :: ElementState
foreign import data SameSite :: Type
foreign import strict :: SameSite
foreign import lax :: SameSite
foreign import none :: SameSite
foreign import data Raf :: Type foreign import data Raf :: Type
foreign import raf :: Raf foreign import raf :: Raf

View File

@ -1,17 +1,10 @@
/* global exports */ /* global exports */
exports.createReadStream_ = function (Nothing) { export const createReadStream_ = Nothing => Just => Download => () =>
return function (Just) { Download.createReadStream().then(result => {
return function (Download) { if (result === null) {
return function () { return Nothing
return Download.createReadStream().then(function (result) { } else {
if (result === null) { return Just(result)
return Nothing; }
} else { })
return Just(result);
}
});
};
};
};
};

View File

@ -1,13 +1,7 @@
/* global exports */ /* global exports */
exports.onForeign = function (obj) { export const onForeign = (obj) => (eventName) => (effCallback) => () => {
return function (eventName) { obj.on(eventName, (argument) => {
return function (effCallback) { effCallback(argument)()
return function () { })
obj.on(eventName, function (argument) { }
effCallback(argument)();
});
};
};
};
};

View File

@ -2,7 +2,7 @@
/** /**
* @param {string} property - method to call on object * @param {string} property - method to call on object
* @param {number} n - number of (curried) arguments * @param {number} argsCount - number of (curried) arguments
* @param {effectRunnerWrapper} effectRunnerWrapper - a function to overrride * @param {effectRunnerWrapper} effectRunnerWrapper - a function to overrride
* effect runner with. `toAffE` for `Aff`, `identity` for `Effect`. * effect runner with. `toAffE` for `Aff`, `identity` for `Effect`.
* *
@ -19,54 +19,33 @@
* effectfulGetter('close', 0, identity); * effectfulGetter('close', 0, identity);
*/ */
function effectfulGetter (property, argsCount, effectRunnerWrapper) { function effectfulGetter (property, argsCount, effectRunnerWrapper) {
var args = []; function consume(arg, args, counter) {
return function (object) { const argsNew = [ ...args, arg ];
function effectRunner () {
return object[property].apply(object, args); if (counter === 0) {
const [ object, ...rest ] = argsNew;
return effectRunnerWrapper(() => object[property].apply(object, rest))
} else {
return (a) => consume(a, argsNew, counter - 1)
} }
}
var affectRunner = effectRunnerWrapper(effectRunner); return (object) => consume(object, [], argsCount)
function chooseNext () {
return argsCount > 0 ? argsConsumer : affectRunner;
}
function argsConsumer (arg) {
if (argsCount == 0) {
return affectRunner;
} else {
args.push(arg);
argsCount--;
return chooseNext();
}
}
return chooseNext();
};
} }
function identity (x) { function identity (x) {
return x; return x;
} }
exports.unsafeEffCall = function (method) { export function unsafeEffCall(method) {
return function (argsCount) { return argsCount => effectfulGetter(method, argsCount, identity);
return effectfulGetter(method, argsCount, identity);
};
};
exports.unsafeAffCall = function (toAffE) {
return function (method) {
return function (argsCount) {
return effectfulGetter(method, argsCount, toAffE);
};
};
};
exports.effProp = function (prop) {
return function (object) {
return function () {
return object[prop];
};
};
} }
export function unsafeAffCall(toAffE) {
return method => argsCount => effectfulGetter(method, argsCount, toAffE);
}
export function effProp(prop) {
return object => () => object[prop];
}

View File

@ -1,17 +1,11 @@
/* global exports */ /* global exports */
exports.getProperties_ = function (insert) { export function getProperties_(insert) {
return function (emptyMap) { return emptyMap => jsHandle => () => jsHandle.getProperties().then(props => {
return function (jsHandle) { let acc = emptyMap;
return function () { props.entries().forEach(pair => {
return jsHandle.getProperties().then(function (props) { acc = insert(pair[0])(pair[1])(acc);
var acc = emptyMap; });
props.entries().forEach(function (pair) { return acc;
acc = insert(pair[0])(pair[1])(acc); });
}); }
return acc;
});
};
};
};
};

View File

@ -3,13 +3,27 @@ module Playwright.Options where
import Playwright.Data import Playwright.Data
import Data.String.Regex (Regex) import Data.String.Regex (Regex)
import Data.Time.Duration (Milliseconds(..))
import Foreign (Foreign) import Foreign (Foreign)
import Foreign.Object (Object) import Foreign.Object (Object)
import Literals.Null (Null) import Literals.Null (Null)
import Untagged.Union (UndefinedOr, type (|+|)) import Untagged.Union (UndefinedOr, type (|+|))
import Playwright.Types (Cookie)
type Opt a = UndefinedOr a type Opt a = UndefinedOr a
type FocusOptions =
{ strict :: Opt Boolean
, timeout :: Opt Number
}
type FillOptions =
{ force :: Opt Boolean
, noWaitAfter :: Opt Boolean
, strict :: Opt Boolean
, timeout :: Opt Number
}
type LaunchOptions = type LaunchOptions =
{ headless :: Opt Boolean { headless :: Opt Boolean
, executablePath :: Opt String , executablePath :: Opt String
@ -22,10 +36,17 @@ type LaunchOptions =
, handleSIGINT :: Opt Boolean , handleSIGINT :: Opt Boolean
, handleSIGTERM :: Opt Boolean , handleSIGTERM :: Opt Boolean
, handleSIGHUP :: Opt Boolean , handleSIGHUP :: Opt Boolean
, timeout :: Opt Number , timeout :: Opt Milliseconds
, env :: Opt (Object String) , env :: Opt (Object String)
, devtools :: Opt Boolean , devtools :: Opt Boolean
, slowMo :: Opt Number , slowMo :: Opt Number
, storageState :: Opt { cookies :: Array Cookie }
}
type ConnectOptions =
{ headers :: Opt (Object String)
, slowMo :: Opt Number
, timeout :: Opt Number
} }
type ProxyOptions = type ProxyOptions =
@ -40,11 +61,11 @@ type ScreenshotOptions =
, "type" :: Opt ScreenshotType , "type" :: Opt ScreenshotType
, quality :: Opt Number , quality :: Opt Number
, omitBackground :: Opt Boolean , omitBackground :: Opt Boolean
, timeout :: Opt Number , timeout :: Opt Milliseconds
} }
type GotoOptions = type GotoOptions =
{ timeout :: Opt Int { timeout :: Opt Milliseconds
, waitUntil :: Opt WaitUntil , waitUntil :: Opt WaitUntil
, referer :: Opt String , referer :: Opt String
} }
@ -57,7 +78,7 @@ type NewpageOptions =
} }
type GoOptions = type GoOptions =
{ timeout :: Opt Int { timeout :: Opt Milliseconds
, waitUntil :: Opt WaitUntil , waitUntil :: Opt WaitUntil
} }
@ -68,11 +89,11 @@ type HoverOptions =
} }
type InnerHTMLOptions = type InnerHTMLOptions =
{ timeout :: Opt Number { timeout :: Opt Milliseconds
} }
type InnerTextOptions = type InnerTextOptions =
{ timeout :: Opt Number { timeout :: Opt Milliseconds
} }
type KeyboardPressOptions = type KeyboardPressOptions =
@ -92,7 +113,7 @@ type ClickOptions =
, modifiers :: Opt (Array Modifier) , modifiers :: Opt (Array Modifier)
, force :: Opt Boolean , force :: Opt Boolean
, noWaitAfter :: Opt Boolean , noWaitAfter :: Opt Boolean
, timeout :: Opt Int , timeout :: Opt Milliseconds
} }
type MouseClickOptions = type MouseClickOptions =
@ -116,31 +137,31 @@ type MouseMoveOptions =
} }
type WaitForNavigationOptions = type WaitForNavigationOptions =
{ timeout :: Opt Int { timeout :: Opt Milliseconds
, url :: Opt (String |+| Regex |+| URL -> Boolean) , url :: Opt (String |+| Regex |+| URL -> Boolean)
, waitUntil :: Opt WaitUntil , waitUntil :: Opt WaitUntil
} }
type WaitForRequestOptions = type WaitForRequestOptions =
{ timeout :: Opt Int { timeout :: Opt Milliseconds
} }
type WaitForResponseOptions = type WaitForResponseOptions =
{ timeout :: Opt Int { timeout :: Opt Milliseconds
} }
type WaitForSelectorOptions = type WaitForSelectorOptions =
{ state :: Opt ElementState { state :: Opt ElementState
, timeout :: Opt Int , timeout :: Opt Milliseconds
} }
type WaitForFunctionOptions = type WaitForFunctionOptions =
{ polling :: Opt (Int |+| Raf) { polling :: Opt (Int |+| Raf)
, timeout :: Opt Int , timeout :: Opt Milliseconds
} }
type WaitForLoadStateOptions = type WaitForLoadStateOptions =
{ timeout :: Opt Int { timeout :: Opt Milliseconds
} }
type Margin = type Margin =
@ -168,5 +189,5 @@ type PdfOptions =
type SetFilesOptions = type SetFilesOptions =
{ noWaitAfter :: Opt Boolean { noWaitAfter :: Opt Boolean
, timeout :: Opt Int , timeout :: Opt Milliseconds
} }

View File

@ -1,17 +1,10 @@
/* global exports */ /* global exports */
exports.finished_ = function (Nothing) { export const finished_ = Nothing => Just => Response => () =>
return function (Just) { Response.finished().then((result) => {
return function (Response) { if (result === null) {
return function () { return Nothing
return Response.finished().then(function (result) { } else {
if (result === null) { return Just(result)
return Nothing; }
} else { })
return Just(result);
}
});
};
};
};
};

14
src/Playwright/Types.purs Normal file
View File

@ -0,0 +1,14 @@
module Playwright.Types where
import Playwright.Data (SameSite)
type Cookie =
{ name :: String
, value :: String
, domain :: String
, path :: String
, expires :: Number
, httpOnly :: Boolean
, secure :: Boolean
, sameSite :: SameSite
}

View File

@ -3,6 +3,7 @@ module Test.Main where
import Control.Monad.Except (runExcept) import Control.Monad.Except (runExcept)
import Data.Either (isLeft) import Data.Either (isLeft)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.Time.Duration (Milliseconds(..))
import Effect (Effect) import Effect (Effect)
import Effect.Aff (launchAff_, try) import Effect.Aff (launchAff_, try)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
@ -13,6 +14,7 @@ import Node.Encoding as Encoding
import Node.FS.Aff as FS import Node.FS.Aff as FS
import Node.Stream as Stream import Node.Stream as Stream
import Playwright import Playwright
import Node.EventEmitter (on_)
import Playwright.ConsoleMessage as ConsoleMessage import Playwright.ConsoleMessage as ConsoleMessage
import Playwright.Dialog as Dialog import Playwright.Dialog as Dialog
import Playwright.Download as Download import Playwright.Download as Download
@ -78,7 +80,7 @@ main = runTest do
withBrowserPage hello withBrowserPage hello
\page -> do \page -> do
void $ waitForSelector page (Selector "body") {} void $ waitForSelector page (Selector "body") {}
res <- try $ waitForSelector page (Selector "nonexistent") { timeout: 100 } res <- try $ waitForSelector page (Selector "nonexistent") { timeout: Milliseconds 100.0 }
Assert.assert "waitForSelector fails when no element" $ Assert.assert "waitForSelector fails when no element" $
isLeft res isLeft res
test "waitForFunction" do test "waitForFunction" do
@ -89,7 +91,7 @@ main = runTest do
void $ waitForFunction void $ waitForFunction
page page
"document.body.textContent.includes('uniqstring')" "document.body.textContent.includes('uniqstring')"
{ timeout: 5000, polling: 100 } { timeout: Milliseconds 5000.0, polling: 100 }
suite "FFI" do suite "FFI" do
test "exposeBinding" do test "exposeBinding" do
withBrowserPage hello \page -> do withBrowserPage hello \page -> do
@ -151,19 +153,22 @@ main = runTest do
case mbStream of case mbStream of
Nothing -> Assert.assert "Unable to get stream" false Nothing -> Assert.assert "Unable to get stream" false
Just stream -> do Just stream -> do
liftEffect $ Stream.onDataString stream Encoding.UTF8 $ \string -> do liftEffect $ Stream.setEncoding stream Encoding.UTF8
liftEffect $ stream # on_ Stream.dataHStr \string -> do
Ref.write (Just string) downloadRef Ref.write (Just string) downloadRef
void $ evaluate page void $ evaluate page
""" """
function download(filename, text) { {
var element = document.createElement('a'); function download(filename, text) {
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); var element = document.createElement('a');
element.setAttribute('download', filename); element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
document.body.appendChild(element); element.setAttribute('download', filename);
element.click(); document.body.appendChild(element);
document.body.removeChild(element); element.click();
document.body.removeChild(element);
}
download("hello.txt","hiiii");
} }
download("hello.txt","hiiii");
""" """
waitForTimeout page 100 waitForTimeout page 100
downloadContent <- liftEffect $ Ref.read downloadRef downloadContent <- liftEffect $ Ref.read downloadRef

View File

@ -1,5 +1,5 @@
/* global exports __dirname */ /* global exports __dirname */
exports.cwd = process.cwd(); export const cwd = process.cwd();
exports.isNull = function (sth) { return sth === null; }; export const isNull = sth => sth === null;