From e87030a92a4e71565b19df1061f498f048aded9a Mon Sep 17 00:00:00 2001 From: Wolfgang Date: Sun, 16 Jun 2024 21:43:12 -0700 Subject: [PATCH] it's been a while --- .gitattributes | 1 + .github/CODEOWNERS | 1 + .github/workflows/ci.yml | 22 ++-- .prettierrc | 5 - README.md | 16 +-- eslint.config.js | 34 ++++++ package.json | 56 +++------- release.js | 34 +++--- src/fx/Console.js | 4 +- src/fx/Cookie.js | 64 +++++------ src/fx/Debounce.js | 20 ++-- src/fx/Dispatch.js | 4 +- src/fx/Geo.js | 8 +- src/fx/History.js | 16 +-- src/fx/Http.js | 16 +-- src/fx/Merge.js | 8 +- src/fx/Random.js | 26 ++--- src/fx/Storage.js | 26 ++--- src/fx/Throttle.js | 22 ++-- src/fx/Time.js | 10 +- src/fx/WebSocket.js | 14 +-- src/fx/index.js | 26 ++--- src/index.js | 4 +- src/subs/Animation.js | 14 +-- src/subs/Geo.js | 14 +-- src/subs/History.js | 6 +- src/subs/Keyboard.js | 22 ++-- src/subs/Time.js | 12 +-- src/subs/WebSocket.js | 46 ++++---- src/subs/index.js | 12 +-- src/utils.js | 38 +++---- test/.eslintrc | 12 --- test/fx/Console.test.js | 24 ++--- test/fx/Cookie.attributes.test.js | 60 +++++------ test/fx/Cookie.test.js | 136 +++++++++++------------ test/fx/Debounce.test.js | 50 ++++----- test/fx/Dispatch.test.js | 18 ++-- test/fx/Geo.test.js | 38 +++---- test/fx/HistoryPush.test.js | 48 ++++----- test/fx/HistoryReplace.test.js | 48 ++++----- test/fx/Http.test.js | 172 +++++++++++++++--------------- test/fx/Merge.test.js | 20 ++-- test/fx/Random.test.js | 88 +++++++-------- test/fx/Storage.test.js | 110 +++++++++---------- test/fx/Throttle.test.js | 44 ++++---- test/fx/Time.test.js | 106 +++++++++--------- test/fx/WebSocketClient.test.js | 78 +++++++------- test/subs/Animation.test.js | 44 ++++---- test/subs/Geo.test.js | 50 ++++----- test/subs/History.test.js | 30 +++--- test/subs/Keyboard.test.js | 74 ++++++------- test/subs/Time.test.js | 94 ++++++++-------- test/subs/WebSocketClient.test.js | 140 ++++++++++++------------ test/utils.js | 10 +- 54 files changed, 1046 insertions(+), 1049 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/CODEOWNERS delete mode 100644 .prettierrc create mode 100644 eslint.config.js delete mode 100644 test/.eslintrc diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..9857501 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @okwolf \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 273e10d..8356895 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,18 +2,18 @@ name: CI on: [push, pull_request] jobs: test: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: matrix: - node-version: [12.x, 14.x, 16.x] + os: [ubuntu-latest, macos-latest, windows-latest] + node: [18, 20, 22] steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node }} + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} - - run: | - npm i -g codecov - npm i - npm run check - codecov + node-version: ${{ matrix.node }} + - run: npm run release:dry + - uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index aa67954..0000000 --- a/.prettierrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "semi": false, - "trailingComma": "none", - "arrowParens": "avoid" -} diff --git a/README.md b/README.md index a0a2d6d..020d02f 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ A handy set of effects for use with [Hyperapp](https://github.com/jorgebucaran/h Here's a taste of how to use a common effect for making HTTP requests. The app displays inspiring quotes about design, fetching a new quote each time the user clicks on the current one. Go ahead and [try it online here](https://codepen.io/okwolf/pen/vPbMaa?editors=0010). ```js -import { app, h, text } from "hyperapp" -import { Http } from "hyperapp-fx" +import { app, h, text } from "hyperapp"; +import { Http } from "hyperapp-fx"; const GetQuote = () => [ "...", @@ -20,13 +20,13 @@ const GetQuote = () => [ url: "https://api.quotable.io/random", action: (_, { content }) => content }) -] +]; app({ init: "Click here for quotes", view: quote => h("h1", { onclick: GetQuote }, text(quote)), node: document.getElementById("app") -}) +}); ``` More [examples](https://github.com/okwolf/hyperapp-playground) are available to show other effects in action. @@ -40,13 +40,13 @@ npm i hyperapp-fx Then with a module bundler like [Rollup](https://rollupjs.org) or [Webpack](https://webpack.js.org), use as you would anything else. ```js -import { Http } from "hyperapp-fx" +import { Http } from "hyperapp-fx"; ``` -If you don't want to set up a build environment, you can download Hyperapp FX from a CDN like [unpkg.com](https://unpkg.com/hyperapp-fx) and it will be globally available through the window.hyperappFx object. We support all ES5-compliant browsers, including Internet Explorer 10 and above. Use of the [`Http`](api.md#module_fx.exports.Http) effect requires a polyfill. +If you don't want to set up a build environment, you can import Hyperapp FX from a CDN like [esm.sh](https://esm.sh/hyperapp-fx). We support all modern browsers used by at least 1% of the world. -```html - +```js +import { Http } from "https://esm.sh/hyperapp-fx"; ``` ## [API documentation](api.md) diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..062ce40 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,34 @@ +import js from "@eslint/js"; +import globals from "globals"; +import compat from "eslint-plugin-compat"; + +export default [ + js.configs.recommended, + { + plugins: { compat }, + rules: { + "no-use-before-define": "error", + "no-var": "error", + "prefer-const": "error", + "compat/compat": "error" + } + }, + { + files: ["src/**/*.js", "test/**/*.js"], + languageOptions: { + globals: globals.browser + } + }, + { + files: ["test/**/*.js", "*.js"], + languageOptions: { + globals: globals.node + } + }, + { + files: ["test/**/*.js"], + languageOptions: { + globals: globals.jest + } + } +]; diff --git a/package.json b/package.json index e8d8e92..d5cde15 100644 --- a/package.json +++ b/package.json @@ -2,60 +2,38 @@ "name": "hyperapp-fx", "version": "2.0.0-beta.2", "description": "Effects for use with Hyperapp", - "main": "dist/hyperappFx.js", "type": "module", "module": "src/index.js", "peerDependencies": { "hyperapp": "^2.0.0" }, "devDependencies": { - "eslint": "=8.8.0", - "eslint-plugin-compat": "=4.0.2", - "jest": "=27.5.0", - "jsdoc-to-markdown": "=7.1.1", - "jsdom": "=19.0.0", - "prettier": "=2.5.1", - "rollup": "=2.67.0", - "uglify-js": "=3.15.0" + "eslint": "=8.57.0", + "eslint-plugin-compat": "=5.0.0", + "jest": "=29.7.0", + "jest-environment-jsdom": "=29.7.0", + "jsdoc-to-markdown": "=8.0.1", + "prettier": "=3.3.2", + "rollup": "=4.18.0", + "uglify-js": "=3.18.0" }, "scripts": { - "clean": "npx --ignore-existing --quiet rimraf coverage dist node_modules", - "format": "prettier --write \"{src,test}/**/*.js\"", - "format:check": "prettier --list-different \"{src,test}/**/*.js\"", - "lint": "eslint {src,test}/**/*.js", + "clean": "npx --quiet rimraf coverage dist node_modules", + "format": "prettier --write \"**/*.js\"", + "format:check": "prettier --list-different \"**/*.js\"", + "lint": "eslint . --ignore-pattern dist", "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage --no-cache", "doc": "jsdoc2md src/fx/*.js src/subs/*.js > api.md", - "bundle": "rollup -i src/index.js -o dist/hyperappFx.js -m -f umd -n hyperappFx", - "minify": "uglifyjs dist/hyperappFx.js -o dist/hyperappFx.js -mc pure_funcs=['Object.defineProperty'] --source-map includeSources,url=hyperappFx.js.map", "check": "npm run format:check && npm run lint && npm t", - "build": "npm run check && npm run bundle && npm run minify", - "release:dry": "npm run clean && npm i && npm run check && npm run build && npm run doc", + "release:dry": "npm run clean && npm i && npm run check && npm run doc", "release": "node --experimental-json-modules release" }, - "eslintConfig": { - "extends": "eslint:recommended", - "plugins": [ - "compat" - ], - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "env": { - "browser": true - }, - "rules": { - "no-use-before-define": "error", - "compat/compat": "error" - }, - "settings": { - "polyfills": [ - "fetch" - ] - } + "prettier": { + "trailingComma": "none", + "arrowParens": "avoid" }, "browserslist": [ - "IE 10" + "> 1%" ], "jest": { "testEnvironment": "jsdom", diff --git a/release.js b/release.js index 82cdaa1..3dbd0dc 100644 --- a/release.js +++ b/release.js @@ -1,30 +1,30 @@ -import { execSync } from "child_process" -import packageJson from "./package.json" -import { fileURLToPath } from "url" -import { dirname } from "path" +import { execSync } from "child_process"; +import packageJson from "./package.json"; +import { fileURLToPath } from "url"; +import { dirname } from "path"; -const __filename = fileURLToPath(import.meta.url) -const __dirname = dirname(__filename) -const exec = command => execSync(command, { encoding: "utf8" }).trim() +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const exec = command => execSync(command, { encoding: "utf8" }).trim(); const exitWithError = error => { - process.stderr.write(`\x1b[1;31m${error}\x1b[0m\n\n`) - process.exit(1) -} + process.stderr.write(`\x1b[1;31m${error}\x1b[0m\n\n`); + process.exit(1); +}; -const gitBranchName = exec("git rev-parse --abbrev-ref HEAD") +const gitBranchName = exec("git rev-parse --abbrev-ref HEAD"); if (gitBranchName !== "master") { - exitWithError("please checkout the master branch to make a release!") + exitWithError("please checkout the master branch to make a release!"); } -const workingCopyChanges = exec("git status --porcelain") +const workingCopyChanges = exec("git status --porcelain"); if (workingCopyChanges) { - exitWithError("please commit your changes before making a release!") + exitWithError("please commit your changes before making a release!"); } -const tagExists = exec(`git tag -l "${packageJson.version}"`) +const tagExists = exec(`git tag -l "${packageJson.version}"`); if (tagExists) { - exitWithError(`${packageJson.version} has already been released!`) + exitWithError(`${packageJson.version} has already been released!`); } execSync( @@ -34,4 +34,4 @@ execSync( stdio: "inherit", cwd: __dirname } -) +); diff --git a/src/fx/Console.js b/src/fx/Console.js index 020a03a..1f96a8f 100644 --- a/src/fx/Console.js +++ b/src/fx/Console.js @@ -1,6 +1,6 @@ function consoleEffect(_, args) { // eslint-disable-next-line no-console - console.log.apply(null, args) + console.log.apply(null, args); } /** @@ -22,5 +22,5 @@ function consoleEffect(_, args) { * ] */ export function Console() { - return [consoleEffect, arguments] + return [consoleEffect, arguments]; } diff --git a/src/fx/Cookie.js b/src/fx/Cookie.js index f21ed0f..40a464c 100644 --- a/src/fx/Cookie.js +++ b/src/fx/Cookie.js @@ -1,17 +1,17 @@ -import { assign } from "../utils.js" +import { assign } from "../utils.js"; -var validCookieNameChars = - "abdefghijklmnqrstuvxyzABDEFGHIJKLMNQRSTUVXYZ0123456789!#$%&'*+-.^_`|~" -var validCookieValueChars = validCookieNameChars + "()/:<>?@[]{}" +const validCookieNameChars = + "abdefghijklmnqrstuvxyzABDEFGHIJKLMNQRSTUVXYZ0123456789!#$%&'*+-.^_`|~"; +const validCookieValueChars = validCookieNameChars + "()/:<>?@[]{}"; function nameEncoder(value) { return value .toString() .split("") .map(function (c) { - return validCookieNameChars.indexOf(c) > -1 ? c : encodeURIComponent(c) + return validCookieNameChars.indexOf(c) > -1 ? c : encodeURIComponent(c); }) - .join("") + .join(""); } function valueEncoder(value) { @@ -19,45 +19,45 @@ function valueEncoder(value) { .toString() .split("") .map(function (c) { - return validCookieValueChars.indexOf(c) > -1 ? c : encodeURIComponent(c) + return validCookieValueChars.indexOf(c) > -1 ? c : encodeURIComponent(c); }) - .join("") + .join(""); } function writeCookie(name, value, attributes) { - var attrs = Object.keys(attributes) + const attrs = Object.keys(attributes) .map(function (k) { - return k + "=" + attributes[k] + return k + "=" + attributes[k]; }) - .join(";") - document.cookie = name + "=" + value + (attrs ? ";" + attrs : "") + .join(";"); + document.cookie = name + "=" + value + (attrs ? ";" + attrs : ""); } function readCookieEffect(dispatch, props) { - var cookies = document.cookie.split("; ") - var cookie = cookies.find(function (c) { - return c.substr(0, c.indexOf("=")) === props.nameEncoder(props.name) - }) + const cookies = document.cookie.split("; "); + const cookie = cookies.find(function (c) { + return c.substr(0, c.indexOf("=")) === props.nameEncoder(props.name); + }); if (cookie) { - var dispatchProps = assign({}, props.props || {}) + const dispatchProps = assign({}, props.props || {}); dispatchProps[props.prop || "value"] = props.converter( props.decoder(cookie.substr(props.nameEncoder(props.name).length + 1)) - ) - dispatch(props.action, dispatchProps) + ); + dispatch(props.action, dispatchProps); } } function writeCookieEffect(dispatch, props) { - var name = (props.nameEncoder || nameEncoder)(props.name) - var value = (props.encoder || valueEncoder)(props.converter(props.value)) - var attributes = {} + const name = (props.nameEncoder || nameEncoder)(props.name); + const value = (props.encoder || valueEncoder)(props.converter(props.value)); + const attributes = {}; if (props.ttl) - props.expires = new Date(new Date().getTime() + props.ttl * 1000) - if (props.path) attributes.path = props.path - if (props.domain) attributes.domain = props.domain - if (props.expires) attributes.expires = props.expires.toUTCString() + props.expires = new Date(new Date().getTime() + props.ttl * 1000); + if (props.path) attributes.path = props.path; + if (props.domain) attributes.domain = props.domain; + if (props.expires) attributes.expires = props.expires.toUTCString(); - writeCookie(name, value, attributes) + writeCookie(name, value, attributes); } /** @@ -97,13 +97,13 @@ export function ReadCookie(props) { props.converter || props.json ? JSON.parse : function (v) { - return v + return v; }, decoder: props.decoder || decodeURIComponent }, props ) - ] + ]; } /** @@ -143,12 +143,12 @@ export function WriteCookie(props) { props.converter || props.json ? JSON.stringify : function (v) { - return v + return v; } }, props ) - ] + ]; } /** @@ -170,5 +170,5 @@ export function WriteCookie(props) { */ export function DeleteCookie(props) { - return WriteCookie(assign(props, { ttl: -1, value: "" })) + return WriteCookie(assign(props, { ttl: -1, value: "" })); } diff --git a/src/fx/Debounce.js b/src/fx/Debounce.js index 763e9c2..9174f13 100644 --- a/src/fx/Debounce.js +++ b/src/fx/Debounce.js @@ -1,18 +1,18 @@ -var debounceTimeouts = [] +const debounceTimeouts = []; function debounceEffect(dispatch, props) { - var timeout = debounceTimeouts.find(function (nextTimeout) { - return nextTimeout[0] === props.action - }) + let timeout = debounceTimeouts.find(function (nextTimeout) { + return nextTimeout[0] === props.action; + }); if (!timeout) { - timeout = [props.action] - debounceTimeouts.push(timeout) + timeout = [props.action]; + debounceTimeouts.push(timeout); } else { - clearTimeout(timeout[1]) + clearTimeout(timeout[1]); } timeout[1] = setTimeout(function () { - dispatch(props.action) - }, props.wait) + dispatch(props.action); + }, props.wait); } /** @@ -38,5 +38,5 @@ function debounceEffect(dispatch, props) { * ] */ export function Debounce(props) { - return [debounceEffect, props] + return [debounceEffect, props]; } diff --git a/src/fx/Dispatch.js b/src/fx/Dispatch.js index f64cad5..9ed30ff 100644 --- a/src/fx/Dispatch.js +++ b/src/fx/Dispatch.js @@ -1,5 +1,5 @@ function dispatchEffect(dispatch, props) { - dispatch(props.action) + dispatch(props.action); } /** @@ -17,5 +17,5 @@ function dispatchEffect(dispatch, props) { * ] */ export function Dispatch(action) { - return [dispatchEffect, { action: action }] + return [dispatchEffect, { action: action }]; } diff --git a/src/fx/Geo.js b/src/fx/Geo.js index ec5c19a..5147036 100644 --- a/src/fx/Geo.js +++ b/src/fx/Geo.js @@ -1,13 +1,13 @@ function getCurrentPositionEffect(dispatch, props) { navigator.geolocation.getCurrentPosition( function (result) { - return dispatch(props.action, result) + return dispatch(props.action, result); }, function (error) { - return dispatch(props.error, error) + return dispatch(props.error, error); }, props.options - ) + ); } /** @@ -34,5 +34,5 @@ function getCurrentPositionEffect(dispatch, props) { * ] */ export function GetCurrentPosition(props) { - return [getCurrentPositionEffect, props] + return [getCurrentPositionEffect, props]; } diff --git a/src/fx/History.js b/src/fx/History.js index be82083..a54e98a 100644 --- a/src/fx/History.js +++ b/src/fx/History.js @@ -1,15 +1,15 @@ function historyPushEffect(dispatch, props) { - var title = props.title || document.title - var url = props.url || location.href + const title = props.title || document.title; + const url = props.url || location.href; - history.pushState(props.state, title, url) + history.pushState(props.state, title, url); } function historyReplaceEffect(dispatch, props) { - var title = props.title || document.title - var url = props.url || location.href + const title = props.title || document.title; + const url = props.url || location.href; - history.replaceState(props.state, title, url) + history.replaceState(props.state, title, url); } /** @@ -34,7 +34,7 @@ function historyReplaceEffect(dispatch, props) { */ export function HistoryPush(props) { - return [historyPushEffect, props] + return [historyPushEffect, props]; } /** @@ -59,5 +59,5 @@ export function HistoryPush(props) { */ export function HistoryReplace(props) { - return [historyReplaceEffect, props] + return [historyReplaceEffect, props]; } diff --git a/src/fx/Http.js b/src/fx/Http.js index 9fe4815..61a1fdd 100644 --- a/src/fx/Http.js +++ b/src/fx/Http.js @@ -1,22 +1,22 @@ -import { assign } from "../utils.js" +import { assign } from "../utils.js"; function httpEffect(dispatch, props) { fetch(props.url, props.options) .then(function (response) { if (!response.ok) { - throw response + throw response; } - return response + return response; }) .then(function (response) { - return response[props.response]() + return response[props.response](); }) .then(function (result) { - dispatch(props.action, result) + dispatch(props.action, result); }) .catch(function (error) { - dispatch(props.error, error) - }) + dispatch(props.error, error); + }); } /** @@ -60,5 +60,5 @@ export function Http(props) { }, props ) - ] + ]; } diff --git a/src/fx/Merge.js b/src/fx/Merge.js index ce9c545..c45785f 100644 --- a/src/fx/Merge.js +++ b/src/fx/Merge.js @@ -1,9 +1,9 @@ -import { assign } from "../utils.js" +import { assign } from "../utils.js"; function mergeEffect(dispatch, props) { dispatch(function (state) { - return assign(state, props.action(state)) - }) + return assign(state, props.action(state)); + }); } /** @@ -20,5 +20,5 @@ function mergeEffect(dispatch, props) { * ] */ export function Merge(action) { - return [mergeEffect, { action: action }] + return [mergeEffect, { action: action }]; } diff --git a/src/fx/Random.js b/src/fx/Random.js index f89dbd4..5b18788 100644 --- a/src/fx/Random.js +++ b/src/fx/Random.js @@ -1,27 +1,27 @@ function generateRandom(props) { if (props.values) { - return props.values.map(generateRandom) + return props.values.map(generateRandom); } - var min = props.min || 0 - var max = props.max || 1 - if (props.int) max++ + let min = props.min || 0; + let max = props.max || 1; + if (props.int) max++; if (props.bool) { - min = 0 - max = 2 + min = 0; + max = 2; } - var randomValue = Math.random() * (max - min) + min + let randomValue = Math.random() * (max - min) + min; if (props.int || props.bool) { - randomValue = Math.floor(randomValue) + randomValue = Math.floor(randomValue); } if (props.bool) { - randomValue = !!randomValue + randomValue = !!randomValue; } - return randomValue + return randomValue; } function randomEffect(dispatch, props) { - var randomValue = generateRandom(props) - dispatch(props.action, randomValue) + const randomValue = generateRandom(props); + dispatch(props.action, randomValue); } /** @@ -54,5 +54,5 @@ function randomEffect(dispatch, props) { * ] */ export function Random(props) { - return [randomEffect, props] + return [randomEffect, props]; } diff --git a/src/fx/Storage.js b/src/fx/Storage.js index 6e1fbee..5f62766 100644 --- a/src/fx/Storage.js +++ b/src/fx/Storage.js @@ -1,27 +1,27 @@ -import { assign } from "../utils.js" +import { assign } from "../utils.js"; function storageArea(area) { - return window[area + "Storage"] || localStorage + return window[area + "Storage"] || localStorage; } function writeToStorageEffect(_, props) { - var value = props.converter(props.value) - storageArea(props.area).setItem(props.key, value) + const value = props.converter(props.value); + storageArea(props.area).setItem(props.key, value); } function readFromStorageEffect(dispatch, props) { try { - var value = props.converter(storageArea(props.area).getItem(props.key)) - var dispatchProps = assign({}, props.props || {}) - dispatchProps[props.prop || "value"] = value - dispatch(props.action, dispatchProps) + const value = props.converter(storageArea(props.area).getItem(props.key)); + const dispatchProps = assign({}, props.props || {}); + dispatchProps[props.prop || "value"] = value; + dispatch(props.action, dispatchProps); } catch (error) { - dispatch(props.error) + dispatch(props.error); } } function removeFromStorageEffect(_, props) { - storageArea(props.area).removeItem(props.key) + storageArea(props.area).removeItem(props.key); } /** @@ -56,7 +56,7 @@ export function WriteToStorage(props) { }, props ) - ] + ]; } /** @@ -94,7 +94,7 @@ export function ReadFromStorage(props) { }, props ) - ] + ]; } /** @@ -118,5 +118,5 @@ export function ReadFromStorage(props) { */ export function RemoveFromStorage(props) { - return [removeFromStorageEffect, props] + return [removeFromStorageEffect, props]; } diff --git a/src/fx/Throttle.js b/src/fx/Throttle.js index 5da3045..74a8c4a 100644 --- a/src/fx/Throttle.js +++ b/src/fx/Throttle.js @@ -1,19 +1,19 @@ -var throttleLocks = [] +const throttleLocks = []; function throttleEffect(dispatch, props) { - var lock = throttleLocks.find(function (nextLock) { - return nextLock[0] === props.action - }) + let lock = throttleLocks.find(function (nextLock) { + return nextLock[0] === props.action; + }); if (!lock) { - lock = [props.action] - throttleLocks.push(lock) + lock = [props.action]; + throttleLocks.push(lock); } if (!lock[1]) { - dispatch(props.action) - lock[1] = true + dispatch(props.action); + lock[1] = true; setTimeout(function () { - lock[1] = false - }, props.rate) + lock[1] = false; + }, props.rate); } } @@ -40,5 +40,5 @@ function throttleEffect(dispatch, props) { * ] */ export function Throttle(props) { - return [throttleEffect, props] + return [throttleEffect, props]; } diff --git a/src/fx/Time.js b/src/fx/Time.js index fda958e..7347278 100644 --- a/src/fx/Time.js +++ b/src/fx/Time.js @@ -1,11 +1,11 @@ -import { makeDispatchTime } from "../utils.js" +import { makeDispatchTime } from "../utils.js"; function nowEffect(dispatch, props) { - makeDispatchTime(dispatch, props)() + makeDispatchTime(dispatch, props)(); } function delayEffect(dispatch, props) { - setTimeout(makeDispatchTime(dispatch, props), props.wait) + setTimeout(makeDispatchTime(dispatch, props), props.wait); } /** @@ -28,7 +28,7 @@ function delayEffect(dispatch, props) { * ] */ export function Now(props) { - return [nowEffect, props] + return [nowEffect, props]; } /** @@ -53,5 +53,5 @@ export function Now(props) { * ] */ export function Delay(props) { - return [delayEffect, props] + return [delayEffect, props]; } diff --git a/src/fx/WebSocket.js b/src/fx/WebSocket.js index b3a2a81..3e05949 100644 --- a/src/fx/WebSocket.js +++ b/src/fx/WebSocket.js @@ -1,15 +1,15 @@ -import { getOpenWebSocket } from "../utils.js" +import { getOpenWebSocket } from "../utils.js"; function webSocketSendEffect(dispatch, props) { - var connection = getOpenWebSocket(props) + const connection = getOpenWebSocket(props); function sendMessage() { - connection.socket.send(props.data) - connection.socket.removeEventListener("open", sendMessage) + connection.socket.send(props.data); + connection.socket.removeEventListener("open", sendMessage); } if (connection.socket.readyState === WebSocket.CONNECTING) { - connection.socket.addEventListener("open", sendMessage) + connection.socket.addEventListener("open", sendMessage); } else { - sendMessage() + sendMessage(); } } @@ -35,5 +35,5 @@ function webSocketSendEffect(dispatch, props) { * ] */ export function WebSocketSend(props) { - return [webSocketSendEffect, props] + return [webSocketSendEffect, props]; } diff --git a/src/fx/index.js b/src/fx/index.js index f5e5b78..979b264 100644 --- a/src/fx/index.js +++ b/src/fx/index.js @@ -2,16 +2,16 @@ * @module fx */ -export * from "./Dispatch.js" -export * from "./Console.js" -export * from "./Random.js" -export * from "./Http.js" -export * from "./Merge.js" -export * from "./Debounce.js" -export * from "./Throttle.js" -export * from "./Storage.js" -export * from "./History.js" -export * from "./Cookie.js" -export * from "./Time.js" -export * from "./WebSocket.js" -export * from "./Geo.js" +export * from "./Dispatch.js"; +export * from "./Console.js"; +export * from "./Random.js"; +export * from "./Http.js"; +export * from "./Merge.js"; +export * from "./Debounce.js"; +export * from "./Throttle.js"; +export * from "./Storage.js"; +export * from "./History.js"; +export * from "./Cookie.js"; +export * from "./Time.js"; +export * from "./WebSocket.js"; +export * from "./Geo.js"; diff --git a/src/index.js b/src/index.js index 2e04a65..54fe670 100644 --- a/src/index.js +++ b/src/index.js @@ -1,2 +1,2 @@ -export * from "./fx/index.js" -export * from "./subs/index.js" +export * from "./fx/index.js"; +export * from "./subs/index.js"; diff --git a/src/subs/Animation.js b/src/subs/Animation.js index 2b33c71..b5775fd 100644 --- a/src/subs/Animation.js +++ b/src/subs/Animation.js @@ -1,15 +1,15 @@ function animationEffect(dispatch, action) { - var cancelId + let cancelId; function frame(timestamp) { - dispatch(action, timestamp) - cancelId = requestAnimationFrame(frame) + dispatch(action, timestamp); + cancelId = requestAnimationFrame(frame); } - cancelId = requestAnimationFrame(frame) + cancelId = requestAnimationFrame(frame); return function () { - cancelAnimationFrame(cancelId) - } + cancelAnimationFrame(cancelId); + }; } /** @@ -46,5 +46,5 @@ function animationEffect(dispatch, action) { * }) */ export function Animation(action) { - return [animationEffect, action] + return [animationEffect, action]; } diff --git a/src/subs/Geo.js b/src/subs/Geo.js index 037c759..ce01e92 100644 --- a/src/subs/Geo.js +++ b/src/subs/Geo.js @@ -1,17 +1,17 @@ function watchPositionEffect(dispatch, props) { - var cancelId = navigator.geolocation.watchPosition( + const cancelId = navigator.geolocation.watchPosition( function (result) { - return dispatch(props.action, result) + return dispatch(props.action, result); }, function (error) { - return dispatch(props.error, error) + return dispatch(props.error, error); }, props.options - ) + ); return function () { - navigator.geolocation.clearWatch(cancelId) - } + navigator.geolocation.clearWatch(cancelId); + }; } /** @@ -32,5 +32,5 @@ function watchPositionEffect(dispatch, props) { * }) */ export function WatchPosition(props) { - return [watchPositionEffect, props] + return [watchPositionEffect, props]; } diff --git a/src/subs/History.js b/src/subs/History.js index b64548f..e497937 100644 --- a/src/subs/History.js +++ b/src/subs/History.js @@ -1,7 +1,7 @@ -import { makeRemoveListener } from "../utils.js" +import { makeRemoveListener } from "../utils.js"; function historyPopEffect(dispatch, props) { - return makeRemoveListener(window, dispatch, props.action, "popstate") + return makeRemoveListener(window, dispatch, props.action, "popstate"); } /** @@ -24,5 +24,5 @@ function historyPopEffect(dispatch, props) { */ export function HistoryPop(props) { - return [historyPopEffect, props] + return [historyPopEffect, props]; } diff --git a/src/subs/Keyboard.js b/src/subs/Keyboard.js index 030c75a..95fdf7c 100644 --- a/src/subs/Keyboard.js +++ b/src/subs/Keyboard.js @@ -1,20 +1,20 @@ -import { makeRemoveListener } from "../utils.js" +import { makeRemoveListener } from "../utils.js"; function keyboardEffect(dispatch, props) { - var removeListenerForEvent = makeRemoveListener.bind( + const removeListenerForEvent = makeRemoveListener.bind( null, document, dispatch, props.action - ) - var removeDown = props.downs ? removeListenerForEvent("keydown") : null - var removeUp = props.ups ? removeListenerForEvent("keyup") : null - var removePress = props.presses ? removeListenerForEvent("keypress") : null + ); + const removeDown = props.downs ? removeListenerForEvent("keydown") : null; + const removeUp = props.ups ? removeListenerForEvent("keyup") : null; + const removePress = props.presses ? removeListenerForEvent("keypress") : null; return function () { - removeDown && removeDown() - removeUp && removeUp() - removePress && removePress() - } + removeDown && removeDown(); + removeUp && removeUp(); + removePress && removePress(); + }; } /** @@ -39,5 +39,5 @@ function keyboardEffect(dispatch, props) { * }) */ export function Keyboard(props) { - return [keyboardEffect, props] + return [keyboardEffect, props]; } diff --git a/src/subs/Time.js b/src/subs/Time.js index 66d8cb9..c4ebcf0 100644 --- a/src/subs/Time.js +++ b/src/subs/Time.js @@ -1,11 +1,11 @@ -import { makeDispatchTime } from "../utils.js" +import { makeDispatchTime } from "../utils.js"; function intervalEffect(dispatch, props) { - var dispatchTime = makeDispatchTime(dispatch, props) - var everyInterval = setInterval(dispatchTime, props.every) + const dispatchTime = makeDispatchTime(dispatch, props); + const everyInterval = setInterval(dispatchTime, props.every); return function () { - everyInterval && clearInterval(everyInterval) - } + everyInterval && clearInterval(everyInterval); + }; } /** @@ -46,5 +46,5 @@ function intervalEffect(dispatch, props) { * }) */ export function Interval(props) { - return [intervalEffect, props] + return [intervalEffect, props]; } diff --git a/src/subs/WebSocket.js b/src/subs/WebSocket.js index 8d9b31d..a7fa05e 100644 --- a/src/subs/WebSocket.js +++ b/src/subs/WebSocket.js @@ -2,65 +2,65 @@ import { getOpenWebSocket, makeRemoveListener, closeWebSocket -} from "../utils.js" +} from "../utils.js"; function webSocketListenEffect(dispatch, props) { - var connection = getOpenWebSocket(props) - var removeListen = makeRemoveListener( + const connection = getOpenWebSocket(props); + const removeListen = makeRemoveListener( connection.socket, dispatch, props.action, "message" - ) - connection.listeners.push(removeListen) - var removeError + ); + connection.listeners.push(removeListen); + let removeError; if (props.error) { removeError = makeRemoveListener( connection.socket, dispatch, props.error, "error" - ) - connection.listeners.push(removeError) + ); + connection.listeners.push(removeError); } - var removeOpen + let removeOpen; if (props.open) { removeOpen = makeRemoveListener( connection.socket, dispatch, props.open, "open" - ) - connection.listeners.push(removeOpen) + ); + connection.listeners.push(removeOpen); } - var removeClose + let removeClose; if (props.close) { removeClose = makeRemoveListener( connection.socket, dispatch, props.close, "close" - ) - connection.listeners.push(removeClose) + ); + connection.listeners.push(removeClose); } return function () { - removeListen && removeListen() - removeError && removeError() - removeOpen && removeOpen() - removeClose && removeClose() + removeListen && removeListen(); + removeError && removeError(); + removeOpen && removeOpen(); + removeClose && removeClose(); connection.listeners = connection.listeners.filter(function (listener) { return ( listener !== removeListen && listener !== removeError && listener !== removeOpen && listener !== removeClose - ) - }) + ); + }); if (connection.listeners.length === 0) { - closeWebSocket(props) + closeWebSocket(props); } - } + }; } /** @@ -83,5 +83,5 @@ function webSocketListenEffect(dispatch, props) { * }) */ export function WebSocketListen(props) { - return [webSocketListenEffect, props] + return [webSocketListenEffect, props]; } diff --git a/src/subs/index.js b/src/subs/index.js index 4b95da8..0312c29 100644 --- a/src/subs/index.js +++ b/src/subs/index.js @@ -2,9 +2,9 @@ * @module subs */ -export * from "./Time.js" -export * from "./Animation.js" -export * from "./Keyboard.js" -export * from "./WebSocket.js" -export * from "./History.js" -export * from "./Geo.js" +export * from "./Time.js"; +export * from "./Animation.js"; +export * from "./Keyboard.js"; +export * from "./WebSocket.js"; +export * from "./History.js"; +export * from "./Geo.js"; diff --git a/src/utils.js b/src/utils.js index 265485b..a9c5e9d 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,42 +1,42 @@ export function assign(source, assignments) { - var result = {}, - i - for (i in source) result[i] = source[i] - for (i in assignments) result[i] = assignments[i] - return result + const result = {}; + let i; + for (i in source) result[i] = source[i]; + for (i in assignments) result[i] = assignments[i]; + return result; } export function makeRemoveListener(attachTo, dispatch, action, eventName) { - var handler = dispatch.bind(null, action) - attachTo.addEventListener(eventName, handler) + const handler = dispatch.bind(null, action); + attachTo.addEventListener(eventName, handler); return function () { - attachTo.removeEventListener(eventName, handler) - } + attachTo.removeEventListener(eventName, handler); + }; } export function makeDispatchTime(dispatch, props) { return function () { - dispatch(props.action, props.asDate ? new Date() : performance.now()) - } + dispatch(props.action, props.asDate ? new Date() : performance.now()); + }; } -var webSocketConnections = {} +const webSocketConnections = {}; export function getOpenWebSocket(props) { - var connection = webSocketConnections[props.url] + let connection = webSocketConnections[props.url]; if (!connection) { connection = { socket: new WebSocket(props.url, props.protocols), listeners: [] - } - webSocketConnections[props.url] = connection + }; + webSocketConnections[props.url] = connection; } - return connection + return connection; } export function closeWebSocket(props) { - var connection = getOpenWebSocket(props) + const connection = getOpenWebSocket(props); // FIXME: handle close on opening - connection.socket.close() - delete webSocketConnections[props.url] + connection.socket.close(); + delete webSocketConnections[props.url]; } diff --git a/test/.eslintrc b/test/.eslintrc deleted file mode 100644 index 6e407e7..0000000 --- a/test/.eslintrc +++ /dev/null @@ -1,12 +0,0 @@ -{ - "env": { - "node": true, - "jest": true - }, - "globals": { - "Promise": true - }, - "rules": { - "compat/compat": "off" - } -} diff --git a/test/fx/Console.test.js b/test/fx/Console.test.js index 3ab0614..45fd093 100644 --- a/test/fx/Console.test.js +++ b/test/fx/Console.test.js @@ -1,20 +1,20 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { Console } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { Console } from "../../src"; /* eslint-disable no-console */ describe("Console effect", () => { it("should log to console", () => { - const defaultLog = console.log + const defaultLog = console.log; try { - const testArgs = ["bar", { some: "data" }, ["list", "of", "data"]] - console.log = jest.fn() - const consoleFx = Console(...testArgs) - runFx(consoleFx) - expect(console.log).toBeCalledWith(...testArgs) + const testArgs = ["bar", { some: "data" }, ["list", "of", "data"]]; + console.log = jest.fn(); + const consoleFx = Console(...testArgs); + runFx(consoleFx); + expect(console.log).toBeCalledWith(...testArgs); } finally { - console.log = defaultLog + console.log = defaultLog; } - }) -}) + }); +}); /* eslint-enable no-console */ diff --git a/test/fx/Cookie.attributes.test.js b/test/fx/Cookie.attributes.test.js index 21deb41..c457338 100644 --- a/test/fx/Cookie.attributes.test.js +++ b/test/fx/Cookie.attributes.test.js @@ -1,64 +1,64 @@ -import { runFx } from "../utils" -import { WriteCookie } from "../../src" +import { runFx } from "../utils"; +import { WriteCookie } from "../../src"; describe("Cookie effect for attributes", () => { beforeEach(() => { Object.defineProperty(document, "cookie", { value: "", writable: true - }) - }) + }); + }); it("should set path", () => { const writeCookie = WriteCookie({ name: "name", value: "value", path: "/path" - }) - runFx(writeCookie) + }); + runFx(writeCookie); - expect(document.cookie).toEqual("name=value;path=/path") - }) + expect(document.cookie).toEqual("name=value;path=/path"); + }); it("should set domain", () => { const writeCookie = WriteCookie({ name: "name", value: "value", domain: ".domain.com" - }) - runFx(writeCookie) + }); + runFx(writeCookie); - expect(document.cookie).toEqual("name=value;domain=.domain.com") - }) + expect(document.cookie).toEqual("name=value;domain=.domain.com"); + }); it("should set ttl to expiry date", () => { - const ttl = 1000 - const now = new Date() - const expires = new Date(now.getTime() + ttl * 1000) + const ttl = 1000; + const now = new Date(); + const expires = new Date(now.getTime() + ttl * 1000); - const writeCookie = WriteCookie({ name: "name", value: "value", ttl }) - runFx(writeCookie) + const writeCookie = WriteCookie({ name: "name", value: "value", ttl }); + runFx(writeCookie); expect(document.cookie).toEqual( "name=value;expires=" + expires.toUTCString() - ) - }) + ); + }); it("should set expiry date", () => { - const now = new Date() - const expires = new Date(now.getTime() + 1000 * 1000) + const now = new Date(); + const expires = new Date(now.getTime() + 1000 * 1000); const writeCookie = WriteCookie({ name: "name", value: "value", expires: expires - }) - runFx(writeCookie) + }); + runFx(writeCookie); expect(document.cookie).toEqual( "name=value;expires=" + expires.toUTCString() - ) - }) + ); + }); it("should set multiple attributes", () => { const writeCookie = WriteCookie({ @@ -66,9 +66,9 @@ describe("Cookie effect for attributes", () => { value: "value", domain: "domain.com", path: "/home" - }) - runFx(writeCookie) + }); + runFx(writeCookie); - expect(document.cookie).toEqual("name=value;path=/home;domain=domain.com") - }) -}) + expect(document.cookie).toEqual("name=value;path=/home;domain=domain.com"); + }); +}); diff --git a/test/fx/Cookie.test.js b/test/fx/Cookie.test.js index 4dde64c..c4ee3d1 100644 --- a/test/fx/Cookie.test.js +++ b/test/fx/Cookie.test.js @@ -1,121 +1,121 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { ReadCookie, WriteCookie, DeleteCookie } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { ReadCookie, WriteCookie, DeleteCookie } from "../../src"; describe("Cookie effect", () => { beforeEach(() => { document.cookie.split("; ").forEach(c => { - let cookie = c.substr(0, c.indexOf("=")) + const cookie = c.substr(0, c.indexOf("=")); document.cookie = `${cookie}=; expires=${new Date( 1970, 0, 1 - ).toUTCString()}` - }) - }) + ).toUTCString()}`; + }); + }); describe("for reading cookies", () => { it("should read a cookie", () => { - document.cookie = "a=cookie" + document.cookie = "a=cookie"; - const action = jest.fn() - const readCookie = ReadCookie({ action, name: "a" }) - const { dispatch } = runFx(readCookie) + const action = jest.fn(); + const readCookie = ReadCookie({ action, name: "a" }); + const { dispatch } = runFx(readCookie); - expect(dispatch).toBeCalledWith(action, { value: "cookie" }) - }) + expect(dispatch).toBeCalledWith(action, { value: "cookie" }); + }); it("should handle missing cookies", () => { - const action = jest.fn() - const readCookie = ReadCookie({ action, name: "missing" }) - const { dispatch } = runFx(readCookie) + const action = jest.fn(); + const readCookie = ReadCookie({ action, name: "missing" }); + const { dispatch } = runFx(readCookie); - expect(dispatch).not.toHaveBeenCalled() - }) + expect(dispatch).not.toHaveBeenCalled(); + }); it("should handle names containing reserved characters", () => { - document.cookie = "a%3Db=cookie" + document.cookie = "a%3Db=cookie"; - const action = jest.fn() - const readCookie = ReadCookie({ action, name: "a=b" }) - const { dispatch } = runFx(readCookie) + const action = jest.fn(); + const readCookie = ReadCookie({ action, name: "a=b" }); + const { dispatch } = runFx(readCookie); - expect(dispatch).toBeCalledWith(action, { value: "cookie" }) - }) + expect(dispatch).toBeCalledWith(action, { value: "cookie" }); + }); it("should handle values contains reserved characters", () => { - document.cookie = "a=b%3Bc" + document.cookie = "a=b%3Bc"; - const action = jest.fn() - const readCookie = ReadCookie({ action, name: "a" }) - const { dispatch } = runFx(readCookie) + const action = jest.fn(); + const readCookie = ReadCookie({ action, name: "a" }); + const { dispatch } = runFx(readCookie); - expect(dispatch).toBeCalledWith(action, { value: "b;c" }) - }) + expect(dispatch).toBeCalledWith(action, { value: "b;c" }); + }); it("should handle multiple cookies", () => { - document.cookie = "a=cookie" - document.cookie = "b=another%20cookie" + document.cookie = "a=cookie"; + document.cookie = "b=another%20cookie"; - const action = jest.fn() - const readCookie = ReadCookie({ action, name: "b" }) - const { dispatch } = runFx(readCookie) + const action = jest.fn(); + const readCookie = ReadCookie({ action, name: "b" }); + const { dispatch } = runFx(readCookie); - expect(dispatch).toBeCalledWith(action, { value: "another cookie" }) - }) - }) + expect(dispatch).toBeCalledWith(action, { value: "another cookie" }); + }); + }); describe("for writing cookies", () => { it("should be able to write a cookie", () => { - const writeCookie = WriteCookie({ name: "a", value: "new cookie" }) - runFx(writeCookie) + const writeCookie = WriteCookie({ name: "a", value: "new cookie" }); + runFx(writeCookie); - expect(document.cookie).toEqual("a=new%20cookie") - }) + expect(document.cookie).toEqual("a=new%20cookie"); + }); it("should handle names contains reserved characters", () => { - const writeCookie = WriteCookie({ name: "a=b", value: "new cookie" }) - runFx(writeCookie) + const writeCookie = WriteCookie({ name: "a=b", value: "new cookie" }); + runFx(writeCookie); - expect(document.cookie).toEqual("a%3Db=new%20cookie") - }) + expect(document.cookie).toEqual("a%3Db=new%20cookie"); + }); it("should handle values containing reserved characters", () => { - const writeCookie = WriteCookie({ name: "a", value: "new; cookie" }) - runFx(writeCookie) + const writeCookie = WriteCookie({ name: "a", value: "new; cookie" }); + runFx(writeCookie); - expect(document.cookie).toEqual("a=new%3B%20cookie") - }) - }) + expect(document.cookie).toEqual("a=new%3B%20cookie"); + }); + }); it("should be able to write a json object to a cookie", () => { const writeCookie = WriteCookie({ name: "a", value: { an: "object" }, json: true - }) - runFx(writeCookie) + }); + runFx(writeCookie); - expect(document.cookie).toEqual("a={%22an%22:%22object%22}") - }) + expect(document.cookie).toEqual("a={%22an%22:%22object%22}"); + }); it("should be able to read a json object from a cookie", () => { - document.cookie = "a={%22an%22:%22object%22}" + document.cookie = "a={%22an%22:%22object%22}"; - const action = jest.fn() - const readCookie = ReadCookie({ action, name: "a", json: true }) - const { dispatch } = runFx(readCookie) + const action = jest.fn(); + const readCookie = ReadCookie({ action, name: "a", json: true }); + const { dispatch } = runFx(readCookie); - expect(dispatch).toBeCalledWith(action, { value: { an: "object" } }) - }) + expect(dispatch).toBeCalledWith(action, { value: { an: "object" } }); + }); it("should be able to delete a cookie", () => { - document.cookie = "a=b" - expect(document.cookie).toEqual("a=b") + document.cookie = "a=b"; + expect(document.cookie).toEqual("a=b"); - const deleteCookie = DeleteCookie({ name: "a" }) - runFx(deleteCookie) + const deleteCookie = DeleteCookie({ name: "a" }); + runFx(deleteCookie); - expect(document.cookie).toEqual("") - }) -}) + expect(document.cookie).toEqual(""); + }); +}); diff --git a/test/fx/Debounce.test.js b/test/fx/Debounce.test.js index c8b1eab..1fa9877 100644 --- a/test/fx/Debounce.test.js +++ b/test/fx/Debounce.test.js @@ -1,37 +1,37 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { Debounce } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { Debounce } from "../../src"; describe("Debounce effect", () => { it("should fire an action after a delay", () => { - jest.useFakeTimers() + jest.useFakeTimers(); try { - const action = jest.fn() - const debounceFx = Debounce({ wait: 1000, action }) - const { dispatch } = runFx(debounceFx) - expect(dispatch).not.toBeCalled() + const action = jest.fn(); + const debounceFx = Debounce({ wait: 1000, action }); + const { dispatch } = runFx(debounceFx); + expect(dispatch).not.toBeCalled(); - jest.runAllTimers() - expect(dispatch).toBeCalledWith(action) + jest.runAllTimers(); + expect(dispatch).toBeCalledWith(action); } finally { - jest.useRealTimers() + jest.useRealTimers(); } - }) + }); it("should only fire the last action fired while waiting", () => { - jest.useFakeTimers() + jest.useFakeTimers(); try { - const action = jest.fn() - const debounceFx = Debounce({ wait: 1000, action }) - const { dispatch: dispatch1 } = runFx(debounceFx) - const { dispatch: dispatch2 } = runFx(debounceFx) - expect(dispatch1).not.toBeCalled() - expect(dispatch2).not.toBeCalled() + const action = jest.fn(); + const debounceFx = Debounce({ wait: 1000, action }); + const { dispatch: dispatch1 } = runFx(debounceFx); + const { dispatch: dispatch2 } = runFx(debounceFx); + expect(dispatch1).not.toBeCalled(); + expect(dispatch2).not.toBeCalled(); - jest.runAllTimers() - expect(dispatch1).not.toBeCalled() - expect(dispatch2).toBeCalledWith(action) + jest.runAllTimers(); + expect(dispatch1).not.toBeCalled(); + expect(dispatch2).toBeCalledWith(action); } finally { - jest.useRealTimers() + jest.useRealTimers(); } - }) -}) + }); +}); diff --git a/test/fx/Dispatch.test.js b/test/fx/Dispatch.test.js index e151c43..27a93e0 100644 --- a/test/fx/Dispatch.test.js +++ b/test/fx/Dispatch.test.js @@ -1,12 +1,12 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { Dispatch } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { Dispatch } from "../../src"; describe("Dispatch effect", () => { it("should dispatch the action", () => { - const action = jest.fn() - const dispatchFx = Dispatch(action) - const { dispatch } = runFx(dispatchFx) - expect(dispatch).toBeCalledWith(action) - }) -}) + const action = jest.fn(); + const dispatchFx = Dispatch(action); + const { dispatch } = runFx(dispatchFx); + expect(dispatch).toBeCalledWith(action); + }); +}); diff --git a/test/fx/Geo.test.js b/test/fx/Geo.test.js index 3fb2a5c..90c36cc 100644 --- a/test/fx/Geo.test.js +++ b/test/fx/Geo.test.js @@ -1,31 +1,31 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { GetCurrentPosition } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { GetCurrentPosition } from "../../src"; describe("GetCurrentPosition effect", () => { it("should return coords on success", () => { navigator.geolocation = { getCurrentPosition: function (successCallback) { - successCallback({ coords: { longitude: 42.0, latitude: 42.1 } }) + successCallback({ coords: { longitude: 42.0, latitude: 42.1 } }); } - } - const action = jest.fn() - const getCurrentPositionFx = GetCurrentPosition({ action }) - const { dispatch } = runFx(getCurrentPositionFx) + }; + const action = jest.fn(); + const getCurrentPositionFx = GetCurrentPosition({ action }); + const { dispatch } = runFx(getCurrentPositionFx); expect(dispatch).toBeCalledWith(action, { coords: { longitude: 42.0, latitude: 42.1 } - }) - }) + }); + }); it("should call error handler on error", () => { navigator.geolocation = { getCurrentPosition: function (successCallback, errorCallback) { - errorCallback("things went wrong") + errorCallback("things went wrong"); } - } - const action = jest.fn() - const error = jest.fn() - const getCurrentPositionFx = GetCurrentPosition({ action, error }) - const { dispatch } = runFx(getCurrentPositionFx) - expect(dispatch).toBeCalledWith(error, "things went wrong") - }) -}) + }; + const action = jest.fn(); + const error = jest.fn(); + const getCurrentPositionFx = GetCurrentPosition({ action, error }); + const { dispatch } = runFx(getCurrentPositionFx); + expect(dispatch).toBeCalledWith(error, "things went wrong"); + }); +}); diff --git a/test/fx/HistoryPush.test.js b/test/fx/HistoryPush.test.js index 9960a9d..17c0e3f 100644 --- a/test/fx/HistoryPush.test.js +++ b/test/fx/HistoryPush.test.js @@ -1,6 +1,6 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { HistoryPush } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { HistoryPush } from "../../src"; describe("HistoryPush effect", () => { it("should update location and state, and increment history length", () => { @@ -8,38 +8,38 @@ describe("HistoryPush effect", () => { state: {}, title: "new title", url: "#foo" - }) - expect(history.length).toEqual(1) - expect(history.state).toEqual(null) - runFx(historyPush) - expect(history.length).toEqual(2) - expect(location.hash).toEqual("#foo") - expect(history.state).toEqual({}) - }) + }); + expect(history.length).toEqual(1); + expect(history.state).toEqual(null); + runFx(historyPush); + expect(history.length).toEqual(2); + expect(location.hash).toEqual("#foo"); + expect(history.state).toEqual({}); + }); it("should call history.pushState with correct params", () => { - history.pushState = jest.fn() + history.pushState = jest.fn(); const historyPush = HistoryPush({ state: {}, title: "new title", url: "#foo" - }) - runFx(historyPush) - expect(history.pushState).toHaveBeenCalledWith({}, "new title", "#foo") - }) + }); + runFx(historyPush); + expect(history.pushState).toHaveBeenCalledWith({}, "new title", "#foo"); + }); it("should call history.pushState with default title and url", () => { - history.pushState = jest.fn() - document.title = "another title" - location.href = "http://localhost/#bar" + history.pushState = jest.fn(); + document.title = "another title"; + location.href = "http://localhost/#bar"; const historyPush = HistoryPush({ state: {} - }) - runFx(historyPush) + }); + runFx(historyPush); expect(history.pushState).toHaveBeenCalledWith( {}, "another title", "http://localhost/#bar" - ) - }) -}) + ); + }); +}); diff --git a/test/fx/HistoryReplace.test.js b/test/fx/HistoryReplace.test.js index 6642270..c82d0b7 100644 --- a/test/fx/HistoryReplace.test.js +++ b/test/fx/HistoryReplace.test.js @@ -1,6 +1,6 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { HistoryReplace } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { HistoryReplace } from "../../src"; describe("HistoryReplace effect", () => { it("should update location and state, and not increment history length", () => { @@ -8,39 +8,39 @@ describe("HistoryReplace effect", () => { state: {}, title: "new title", url: "#bar" - }) + }); - expect(history.length).toEqual(1) - expect(history.state).toEqual(null) - runFx(historyReplace) - expect(history.length).toEqual(1) - expect(location.hash).toEqual("#bar") - expect(history.state).toEqual({}) - }) + expect(history.length).toEqual(1); + expect(history.state).toEqual(null); + runFx(historyReplace); + expect(history.length).toEqual(1); + expect(location.hash).toEqual("#bar"); + expect(history.state).toEqual({}); + }); it("should call history.replaceState with correct params", () => { - history.replaceState = jest.fn() + history.replaceState = jest.fn(); const historyReplace = HistoryReplace({ state: {}, title: "new title", url: "#foo" - }) - runFx(historyReplace) - expect(history.replaceState).toHaveBeenCalledWith({}, "new title", "#foo") - }) + }); + runFx(historyReplace); + expect(history.replaceState).toHaveBeenCalledWith({}, "new title", "#foo"); + }); it("should call history.replaceState with default title and url", () => { - history.replaceState = jest.fn() - document.title = "another title" - location.href = "http://localhost/#bar" + history.replaceState = jest.fn(); + document.title = "another title"; + location.href = "http://localhost/#bar"; const historyReplace = HistoryReplace({ state: {} - }) - runFx(historyReplace) + }); + runFx(historyReplace); expect(history.replaceState).toHaveBeenCalledWith( {}, "another title", "http://localhost/#bar" - ) - }) -}) + ); + }); +}); diff --git a/test/fx/Http.test.js b/test/fx/Http.test.js index e1cc8df..9d8a3ae 100644 --- a/test/fx/Http.test.js +++ b/test/fx/Http.test.js @@ -1,66 +1,66 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { Http } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { Http } from "../../src"; describe("Http effect", () => { it("should get json", done => { - const testUrl = "https://example.com" + const testUrl = "https://example.com"; global.fetch = (url, options) => { - expect(url).toBe(testUrl) - expect(options).toEqual({}) + expect(url).toBe(testUrl); + expect(options).toEqual({}); return Promise.resolve({ ok: true, json: () => Promise.resolve({ response: "data" }) - }) - } + }); + }; - const action = jest.fn() - const httpFx = Http({ url: testUrl, action }) - const { dispatch } = runFx(httpFx) + const action = jest.fn(); + const httpFx = Http({ url: testUrl, action }); + const { dispatch } = runFx(httpFx); process.nextTick(() => { - expect(dispatch).toBeCalledWith(action, { response: "data" }) - delete global.fetch - done() - }) - }) + expect(dispatch).toBeCalledWith(action, { response: "data" }); + delete global.fetch; + done(); + }); + }); it("should get text", done => { - const testUrl = "https://example.com/hello" + const testUrl = "https://example.com/hello"; global.fetch = (url, options) => { - expect(url).toBe(testUrl) - expect(options).toEqual({}) + expect(url).toBe(testUrl); + expect(options).toEqual({}); return Promise.resolve({ ok: true, text: () => Promise.resolve("hello world") - }) - } - const action = jest.fn() - const httpFx = Http({ url: testUrl, response: "text", action }) - const { dispatch } = runFx(httpFx) + }); + }; + const action = jest.fn(); + const httpFx = Http({ url: testUrl, response: "text", action }); + const { dispatch } = runFx(httpFx); process.nextTick(() => { - expect(dispatch).toBeCalledWith(action, "hello world") - delete global.fetch - done() - }) - }) + expect(dispatch).toBeCalledWith(action, "hello world"); + delete global.fetch; + done(); + }); + }); it("should post json", done => { - const testUrl = "/login" + const testUrl = "/login"; global.fetch = (url, options) => { - expect(url).toBe(testUrl) + expect(url).toBe(testUrl); expect(options).toEqual({ method: "POST", body: { user: "username", pass: "password" } - }) + }); return Promise.resolve({ ok: true, json: () => Promise.resolve({ result: "authenticated" }) - }) - } - const action = jest.fn() + }); + }; + const action = jest.fn(); const httpFx = Http({ url: testUrl, options: { @@ -68,76 +68,76 @@ describe("Http effect", () => { body: { user: "username", pass: "password" } }, action - }) - const { dispatch } = runFx(httpFx) + }); + const { dispatch } = runFx(httpFx); process.nextTick(() => { - expect(dispatch).toBeCalledWith(action, { result: "authenticated" }) - delete global.fetch - done() - }) - }) + expect(dispatch).toBeCalledWith(action, { result: "authenticated" }); + delete global.fetch; + done(); + }); + }); it("should call the error handler on error", done => { - const testUrl = "https://example.com/hello" - const error = new Error("Failed") + const testUrl = "https://example.com/hello"; + const error = new Error("Failed"); global.fetch = (url, options) => { - expect(url).toBe(testUrl) - expect(options).toEqual({}) - return Promise.reject(error) - } + expect(url).toBe(testUrl); + expect(options).toEqual({}); + return Promise.reject(error); + }; - const successAction = jest.fn() - const errorAction = jest.fn() + const successAction = jest.fn(); + const errorAction = jest.fn(); const httpFx = Http({ url: testUrl, response: "text", action: successAction, error: errorAction - }) - const { dispatch } = runFx(httpFx) + }); + const { dispatch } = runFx(httpFx); process.nextTick(() => { - expect(dispatch).toBeCalledWith(errorAction, error) - delete global.fetch - done() - }) - }) + expect(dispatch).toBeCalledWith(errorAction, error); + delete global.fetch; + done(); + }); + }); it("should call default action on error", done => { - const testUrl = "https://example.com/hello" - const error = new Error("Failed") + const testUrl = "https://example.com/hello"; + const error = new Error("Failed"); global.fetch = (url, options) => { - expect(url).toBe(testUrl) - expect(options).toEqual({}) - return Promise.reject(error) - } - const action = jest.fn() - const httpFx = Http({ url: testUrl, response: "text", action }) - const { dispatch } = runFx(httpFx) + expect(url).toBe(testUrl); + expect(options).toEqual({}); + return Promise.reject(error); + }; + const action = jest.fn(); + const httpFx = Http({ url: testUrl, response: "text", action }); + const { dispatch } = runFx(httpFx); process.nextTick(() => { - expect(dispatch).toBeCalledWith(action, error) - delete global.fetch - done() - }) - }) + expect(dispatch).toBeCalledWith(action, error); + delete global.fetch; + done(); + }); + }); it("should call default action on error when response is not OK", done => { - const testUrl = "https://example.com/hello" + const testUrl = "https://example.com/hello"; const response = { ok: false - } + }; global.fetch = (url, options) => { - expect(url).toBe(testUrl) - expect(options).toEqual({}) - return Promise.resolve(response) - } - const action = jest.fn() - const httpFx = Http({ url: testUrl, response: "text", action }) - const { dispatch } = runFx(httpFx) + expect(url).toBe(testUrl); + expect(options).toEqual({}); + return Promise.resolve(response); + }; + const action = jest.fn(); + const httpFx = Http({ url: testUrl, response: "text", action }); + const { dispatch } = runFx(httpFx); process.nextTick(() => { - expect(dispatch).toBeCalledWith(action, response) - delete global.fetch - done() - }) - }) -}) + expect(dispatch).toBeCalledWith(action, response); + delete global.fetch; + done(); + }); + }); +}); diff --git a/test/fx/Merge.test.js b/test/fx/Merge.test.js index 1840ba0..f75da22 100644 --- a/test/fx/Merge.test.js +++ b/test/fx/Merge.test.js @@ -1,16 +1,16 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { Merge } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { Merge } from "../../src"; describe("Merge effect", () => { it("should shallow merge existing state with new state returned by action", () => { - const inc = jest.fn(({ count }) => ({ count: count + 1 })) - const batchFx = Merge(inc) - const { dispatch } = runFx(batchFx) - expect(dispatch).toBeCalledWith(expect.any(Function)) + const inc = jest.fn(({ count }) => ({ count: count + 1 })); + const batchFx = Merge(inc); + const { dispatch } = runFx(batchFx); + expect(dispatch).toBeCalledWith(expect.any(Function)); expect(dispatch.mock.calls[0][0]({ count: 0, other: "state" })).toEqual({ count: 1, other: "state" - }) - }) -}) + }); + }); +}); diff --git a/test/fx/Random.test.js b/test/fx/Random.test.js index 41c6c9d..522562f 100644 --- a/test/fx/Random.test.js +++ b/test/fx/Random.test.js @@ -1,62 +1,62 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { Random } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { Random } from "../../src"; describe("Random effect", () => { - const defaultRandom = Math.random + const defaultRandom = Math.random; afterEach(() => { - Math.random = defaultRandom - }) + Math.random = defaultRandom; + }); it("should call Math.random with default range", () => { - const randomValue = 0.5 - Math.random = () => randomValue + const randomValue = 0.5; + Math.random = () => randomValue; - const action = jest.fn() - const randomFx = Random({ action }) - const { dispatch } = runFx(randomFx) - expect(dispatch).toBeCalledWith(action, randomValue) - }) + const action = jest.fn(); + const randomFx = Random({ action }); + const { dispatch } = runFx(randomFx); + expect(dispatch).toBeCalledWith(action, randomValue); + }); it("should call Math.random with custom range", () => { - Math.random = () => 0.5 + Math.random = () => 0.5; - const action = jest.fn() - const randomFx = Random({ min: 2, max: 5, action }) - const { dispatch } = runFx(randomFx) - expect(dispatch).toBeCalledWith(action, 3.5) - }) + const action = jest.fn(); + const randomFx = Random({ min: 2, max: 5, action }); + const { dispatch } = runFx(randomFx); + expect(dispatch).toBeCalledWith(action, 3.5); + }); it("should generate integers", () => { - Math.random = () => 0.5 + Math.random = () => 0.5; - const action = jest.fn() - const randomFx = Random({ int: true, min: 1, max: 3, action }) - const { dispatch } = runFx(randomFx) - expect(dispatch).toBeCalledWith(action, 2) - }) + const action = jest.fn(); + const randomFx = Random({ int: true, min: 1, max: 3, action }); + const { dispatch } = runFx(randomFx); + expect(dispatch).toBeCalledWith(action, 2); + }); it("should generate false booleans", () => { - Math.random = () => 0.4 + Math.random = () => 0.4; - const action = jest.fn() - const randomFx = Random({ bool: true, action }) - const { dispatch } = runFx(randomFx) - expect(dispatch).toBeCalledWith(action, false) - }) + const action = jest.fn(); + const randomFx = Random({ bool: true, action }); + const { dispatch } = runFx(randomFx); + expect(dispatch).toBeCalledWith(action, false); + }); it("should generate true booleans", () => { - Math.random = () => 0.5 + Math.random = () => 0.5; - const action = jest.fn() - const randomFx = Random({ bool: true, action }) - const { dispatch } = runFx(randomFx) - expect(dispatch).toBeCalledWith(action, true) - }) + const action = jest.fn(); + const randomFx = Random({ bool: true, action }); + const { dispatch } = runFx(randomFx); + expect(dispatch).toBeCalledWith(action, true); + }); it("should generate multiple values", () => { - Math.random = () => 0.5 + Math.random = () => 0.5; - const action = jest.fn() + const action = jest.fn(); const randomFx = Random({ values: [ {}, @@ -65,8 +65,8 @@ describe("Random effect", () => { { bool: true } ], action - }) - const { dispatch } = runFx(randomFx) - expect(dispatch).toBeCalledWith(action, [0.5, 3.5, 2, true]) - }) -}) + }); + const { dispatch } = runFx(randomFx); + expect(dispatch).toBeCalledWith(action, [0.5, 3.5, 2, true]); + }); +}); diff --git a/test/fx/Storage.test.js b/test/fx/Storage.test.js index da2d26f..03b1445 100644 --- a/test/fx/Storage.test.js +++ b/test/fx/Storage.test.js @@ -1,93 +1,93 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { WriteToStorage, ReadFromStorage } from "../../src" -import { RemoveFromStorage } from "../../src/fx/Storage" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { WriteToStorage, ReadFromStorage } from "../../src"; +import { RemoveFromStorage } from "../../src/fx/Storage"; const mockStorage = (store = {}) => { - jest.spyOn(Storage.prototype, "setItem").mockImplementation(jest.fn()) + jest.spyOn(Storage.prototype, "setItem").mockImplementation(jest.fn()); jest .spyOn(Storage.prototype, "getItem") - .mockImplementation(key => store[key] || null) - jest.spyOn(Storage.prototype, "removeItem").mockImplementation(jest.fn()) -} + .mockImplementation(key => store[key] || null); + jest.spyOn(Storage.prototype, "removeItem").mockImplementation(jest.fn()); +}; -const reverser = s => s.split("").reverse().join("") +const reverser = s => s.split("").reverse().join(""); describe("WriteToStorage effect", () => { beforeEach(() => { - mockStorage() - }) + mockStorage(); + }); it("should write to storage", () => { - const writeToStorageFx = WriteToStorage({ key: "bar", value: "123" }) - runFx(writeToStorageFx) - expect(sessionStorage.setItem).toBeCalledWith("bar", '"123"') - }) + const writeToStorageFx = WriteToStorage({ key: "bar", value: "123" }); + runFx(writeToStorageFx); + expect(sessionStorage.setItem).toBeCalledWith("bar", '"123"'); + }); it("should support a custom converter", () => { const writeToStorageFx = WriteToStorage({ key: "bar", value: "foo", converter: reverser - }) - runFx(writeToStorageFx) - expect(sessionStorage.setItem).toBeCalledWith("bar", "oof") - }) -}) + }); + runFx(writeToStorageFx); + expect(sessionStorage.setItem).toBeCalledWith("bar", "oof"); + }); +}); describe("ReadFromStorage effect", () => { beforeEach(() => { - mockStorage({ foo: '"bar"', soo: '"cat"' }) - }) + mockStorage({ foo: '"bar"', soo: '"cat"' }); + }); it("should read from storage", () => { - const action = jest.fn() - const readFromStorageFx = ReadFromStorage({ key: "soo", action }) - const { dispatch } = runFx(readFromStorageFx) - expect(dispatch).toBeCalledWith(action, { value: "cat" }) - expect(sessionStorage.getItem).toBeCalledWith("soo") - }) + const action = jest.fn(); + const readFromStorageFx = ReadFromStorage({ key: "soo", action }); + const { dispatch } = runFx(readFromStorageFx); + expect(dispatch).toBeCalledWith(action, { value: "cat" }); + expect(sessionStorage.getItem).toBeCalledWith("soo"); + }); it("should call error function on parse error", () => { - mockStorage({ foo: "rab" }) - const action = jest.fn() - const readFromStorageFx = ReadFromStorage({ key: "foo", error: action }) - const { dispatch } = runFx(readFromStorageFx) - expect(dispatch).toBeCalledWith(action) - expect(sessionStorage.getItem).toBeCalledWith("soo") - }) + mockStorage({ foo: "rab" }); + const action = jest.fn(); + const readFromStorageFx = ReadFromStorage({ key: "foo", error: action }); + const { dispatch } = runFx(readFromStorageFx); + expect(dispatch).toBeCalledWith(action); + expect(sessionStorage.getItem).toBeCalledWith("soo"); + }); it("should support a custom converter to read", () => { - mockStorage({ foo: "rab" }) - const action = jest.fn() + mockStorage({ foo: "rab" }); + const action = jest.fn(); const readFromStorageFx = ReadFromStorage({ key: "foo", action, converter: reverser - }) - const { dispatch } = runFx(readFromStorageFx) - expect(dispatch).toBeCalledWith(action, { value: "bar" }) - expect(sessionStorage.getItem).toBeCalledWith("foo") - }) + }); + const { dispatch } = runFx(readFromStorageFx); + expect(dispatch).toBeCalledWith(action, { value: "bar" }); + expect(sessionStorage.getItem).toBeCalledWith("foo"); + }); it("should support action prop", () => { - const action = jest.fn() + const action = jest.fn(); const readFromStorageFx = ReadFromStorage({ key: "foo", action, prop: "text" - }) - const { dispatch } = runFx(readFromStorageFx) - expect(dispatch).toBeCalledWith(action, { text: "bar" }) - expect(sessionStorage.getItem).toBeCalledWith("foo") - }) -}) + }); + const { dispatch } = runFx(readFromStorageFx); + expect(dispatch).toBeCalledWith(action, { text: "bar" }); + expect(sessionStorage.getItem).toBeCalledWith("foo"); + }); +}); describe("DeleteFromStorage effect", () => { it("should remove from storage", () => { - mockStorage({ foo: "bar" }) - const removeFromStorageFx = RemoveFromStorage({ key: "foo" }) - runFx(removeFromStorageFx) - expect(sessionStorage.removeItem).toBeCalledWith("foo") - }) -}) + mockStorage({ foo: "bar" }); + const removeFromStorageFx = RemoveFromStorage({ key: "foo" }); + runFx(removeFromStorageFx); + expect(sessionStorage.removeItem).toBeCalledWith("foo"); + }); +}); diff --git a/test/fx/Throttle.test.js b/test/fx/Throttle.test.js index 89718d7..97ca265 100644 --- a/test/fx/Throttle.test.js +++ b/test/fx/Throttle.test.js @@ -1,30 +1,30 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { Throttle } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { Throttle } from "../../src"; describe("Throttle effect", () => { it("should fire a single action immediately", () => { - const action = jest.fn() - const debounceFx = Throttle({ rate: 1000, action }) - const { dispatch } = runFx(debounceFx) - expect(dispatch).toBeCalledWith(action) - }) + const action = jest.fn(); + const debounceFx = Throttle({ rate: 1000, action }); + const { dispatch } = runFx(debounceFx); + expect(dispatch).toBeCalledWith(action); + }); it("should only fire an action once within a rate limit", () => { - jest.useFakeTimers() + jest.useFakeTimers(); try { - const action = jest.fn() - const debounceFx = Throttle({ wait: 1000, action }) - const { dispatch: dispatch1 } = runFx(debounceFx) - const { dispatch: dispatch2 } = runFx(debounceFx) - expect(dispatch1).toBeCalledWith(action) - expect(dispatch2).not.toBeCalled() + const action = jest.fn(); + const debounceFx = Throttle({ wait: 1000, action }); + const { dispatch: dispatch1 } = runFx(debounceFx); + const { dispatch: dispatch2 } = runFx(debounceFx); + expect(dispatch1).toBeCalledWith(action); + expect(dispatch2).not.toBeCalled(); - jest.runAllTimers() - expect(dispatch2).not.toBeCalled() - const { dispatch: dispatch3 } = runFx(debounceFx) - expect(dispatch3).toBeCalledWith(action) + jest.runAllTimers(); + expect(dispatch2).not.toBeCalled(); + const { dispatch: dispatch3 } = runFx(debounceFx); + expect(dispatch3).toBeCalledWith(action); } finally { - jest.useRealTimers() + jest.useRealTimers(); } - }) -}) + }); +}); diff --git a/test/fx/Time.test.js b/test/fx/Time.test.js index 7b1430e..8c178ef 100644 --- a/test/fx/Time.test.js +++ b/test/fx/Time.test.js @@ -1,75 +1,75 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { Now, Delay } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { Now, Delay } from "../../src"; describe("Now effect", () => { it("should get the current time", () => { - jest.useFakeTimers() - const timestamp = 9001 - const defaultPerformance = global.performance - global.performance = { now: () => timestamp } + jest.useFakeTimers(); + const timestamp = 9001; + const defaultPerformance = global.performance; + global.performance = { now: () => timestamp }; try { - const action = jest.fn() - const timeFx = Now({ action }) - const { dispatch } = runFx(timeFx) - expect(dispatch).toBeCalledWith(action, timestamp) + const action = jest.fn(); + const timeFx = Now({ action }); + const { dispatch } = runFx(timeFx); + expect(dispatch).toBeCalledWith(action, timestamp); } finally { - global.performance = defaultPerformance - jest.useRealTimers() + global.performance = defaultPerformance; + jest.useRealTimers(); } - }) + }); it("should get the current date", () => { - const timestamp = 9001 - const defaultDate = global.Date + const timestamp = 9001; + const defaultDate = global.Date; global.Date = function () { - return { timestamp } - } + return { timestamp }; + }; try { - const action = jest.fn() - const timeFx = Now({ asDate: true, action }) - const { dispatch } = runFx(timeFx) - expect(dispatch).toBeCalledWith(action, { timestamp }) + const action = jest.fn(); + const timeFx = Now({ asDate: true, action }); + const { dispatch } = runFx(timeFx); + expect(dispatch).toBeCalledWith(action, { timestamp }); } finally { - global.Date = defaultDate + global.Date = defaultDate; } - }) -}) + }); +}); describe("Delay effect", () => { it("should get the current time after a delay", () => { - jest.useFakeTimers() - const timestamp = 666 - const defaultPerformance = global.performance - global.performance = { now: () => timestamp } + jest.useFakeTimers(); + const timestamp = 666; + const defaultPerformance = global.performance; + global.performance = { now: () => timestamp }; try { - const action = jest.fn() - const timeFx = Delay({ wait: timestamp, action }) - const { dispatch } = runFx(timeFx) - expect(dispatch).not.toBeCalled() - jest.runAllTimers() - expect(dispatch).toBeCalledWith(action, timestamp) + const action = jest.fn(); + const timeFx = Delay({ wait: timestamp, action }); + const { dispatch } = runFx(timeFx); + expect(dispatch).not.toBeCalled(); + jest.runAllTimers(); + expect(dispatch).toBeCalledWith(action, timestamp); } finally { - global.performance = defaultPerformance - jest.useRealTimers() + global.performance = defaultPerformance; + jest.useRealTimers(); } - }) + }); it("should get the current date after a delay", () => { - jest.useFakeTimers() - const timestamp = 666 - const defaultDate = global.Date + jest.useFakeTimers(); + const timestamp = 666; + const defaultDate = global.Date; global.Date = function () { - return { timestamp } - } + return { timestamp }; + }; try { - const action = jest.fn() - const timeFx = Delay({ wait: timestamp, asDate: true, action }) - const { dispatch } = runFx(timeFx) - expect(dispatch).not.toBeCalled() - jest.runAllTimers() - expect(dispatch).toBeCalledWith(action, { timestamp }) + const action = jest.fn(); + const timeFx = Delay({ wait: timestamp, asDate: true, action }); + const { dispatch } = runFx(timeFx); + expect(dispatch).not.toBeCalled(); + jest.runAllTimers(); + expect(dispatch).toBeCalledWith(action, { timestamp }); } finally { - global.Date = defaultDate - jest.useRealTimers() + global.Date = defaultDate; + jest.useRealTimers(); } - }) -}) + }); +}); diff --git a/test/fx/WebSocketClient.test.js b/test/fx/WebSocketClient.test.js index 32c13df..87e2b53 100644 --- a/test/fx/WebSocketClient.test.js +++ b/test/fx/WebSocketClient.test.js @@ -1,56 +1,56 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { WebSocketSend } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { WebSocketSend } from "../../src"; describe("WebSocketSend effect", () => { - const url = "wss://localhost" - const mockWebSocket = {} - const defaultWebSocket = global.WebSocket + const url = "wss://localhost"; + const mockWebSocket = {}; + const defaultWebSocket = global.WebSocket; beforeEach(() => { - mockWebSocket.readyState = 1 - mockWebSocket.send = jest.fn() - mockWebSocket.addEventListener = jest.fn() - mockWebSocket.removeEventListener = jest.fn() - mockWebSocket.close = jest.fn() - global.WebSocket = jest.fn(() => mockWebSocket) - global.WebSocket.CONNECTING = 0 - }) + mockWebSocket.readyState = 1; + mockWebSocket.send = jest.fn(); + mockWebSocket.addEventListener = jest.fn(); + mockWebSocket.removeEventListener = jest.fn(); + mockWebSocket.close = jest.fn(); + global.WebSocket = jest.fn(() => mockWebSocket); + global.WebSocket.CONNECTING = 0; + }); afterEach(() => { - global.WebSocket = defaultWebSocket - }) + global.WebSocket = defaultWebSocket; + }); it("should create a new WebSocket without protocols if not connected", () => { - const webSocketFx = WebSocketSend({ url }) - runFx(webSocketFx) + const webSocketFx = WebSocketSend({ url }); + runFx(webSocketFx); - expect(WebSocket).toBeCalledWith(url, undefined) - expect(mockWebSocket.close).not.toBeCalled() - }) + expect(WebSocket).toBeCalledWith(url, undefined); + expect(mockWebSocket.close).not.toBeCalled(); + }); it("should send a message if connected", () => { - const message = { some: "message value" } - const webSocketFx = WebSocketSend({ url, data: message }) - runFx(webSocketFx) + const message = { some: "message value" }; + const webSocketFx = WebSocketSend({ url, data: message }); + runFx(webSocketFx); - expect(mockWebSocket.send).toBeCalledWith(message) - }) + expect(mockWebSocket.send).toBeCalledWith(message); + }); it("should queue sending a message if not connected", () => { - mockWebSocket.readyState = 0 - const message = { some: "message value" } - const webSocketFx = WebSocketSend({ url, data: message }) - runFx(webSocketFx) + mockWebSocket.readyState = 0; + const message = { some: "message value" }; + const webSocketFx = WebSocketSend({ url, data: message }); + runFx(webSocketFx); expect(mockWebSocket.addEventListener).toBeCalledWith( "open", expect.any(Function) - ) - expect(mockWebSocket.send).not.toBeCalled() - expect(mockWebSocket.removeEventListener).not.toBeCalled() + ); + expect(mockWebSocket.send).not.toBeCalled(); + expect(mockWebSocket.removeEventListener).not.toBeCalled(); - const openListener = mockWebSocket.addEventListener.mock.calls[0][1] - openListener() - expect(mockWebSocket.send).toBeCalledWith(message) + const openListener = mockWebSocket.addEventListener.mock.calls[0][1]; + openListener(); + expect(mockWebSocket.send).toBeCalledWith(message); expect(mockWebSocket.removeEventListener).toBeCalledWith( "open", expect.any(Function) - ) - }) -}) + ); + }); +}); diff --git a/test/subs/Animation.test.js b/test/subs/Animation.test.js index 8057857..e238753 100644 --- a/test/subs/Animation.test.js +++ b/test/subs/Animation.test.js @@ -1,27 +1,27 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { Animation } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { Animation } from "../../src"; describe("Animation subscription", () => { it("should fire an action each frame until unsubscribed", () => { - const cancelId = 9001 - const timestamp = 1001 - const defaultRequestAnimationFrame = requestAnimationFrame - const defaultCancelAnimationFrame = cancelAnimationFrame + const cancelId = 9001; + const timestamp = 1001; + const defaultRequestAnimationFrame = requestAnimationFrame; + const defaultCancelAnimationFrame = cancelAnimationFrame; global.requestAnimationFrame = jest.fn().mockImplementationOnce(cb => { - cb(timestamp) - return cancelId - }) - global.cancelAnimationFrame = jest.fn() - const action = jest.fn() - const frameFx = Animation(action) - const { dispatch, unsubscribe } = runFx(frameFx) - expect(dispatch).toBeCalledWith(action, timestamp) - expect(requestAnimationFrame).toBeCalledWith(expect.any(Function)) + cb(timestamp); + return cancelId; + }); + global.cancelAnimationFrame = jest.fn(); + const action = jest.fn(); + const frameFx = Animation(action); + const { dispatch, unsubscribe } = runFx(frameFx); + expect(dispatch).toBeCalledWith(action, timestamp); + expect(requestAnimationFrame).toBeCalledWith(expect.any(Function)); - unsubscribe() - expect(global.cancelAnimationFrame).toBeCalledWith(cancelId) - global.requestAnimationFrame = defaultRequestAnimationFrame - global.cancelAnimationFrame = defaultCancelAnimationFrame - }) -}) + unsubscribe(); + expect(global.cancelAnimationFrame).toBeCalledWith(cancelId); + global.requestAnimationFrame = defaultRequestAnimationFrame; + global.cancelAnimationFrame = defaultCancelAnimationFrame; + }); +}); diff --git a/test/subs/Geo.test.js b/test/subs/Geo.test.js index 9ad87fa..60a21c3 100644 --- a/test/subs/Geo.test.js +++ b/test/subs/Geo.test.js @@ -1,41 +1,41 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { WatchPosition } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { WatchPosition } from "../../src"; describe("WatchPosition effect", () => { it("should return coords on success", () => { navigator.geolocation = { watchPosition: function (successCallback) { - successCallback({ coords: { longitude: 42.0, latitude: 42.1 } }) - return 12 + successCallback({ coords: { longitude: 42.0, latitude: 42.1 } }); + return 12; }, clearWatch: function (watchId) { - expect(watchId).toBe(12) + expect(watchId).toBe(12); } - } - const action = jest.fn() - const watchPositionFx = WatchPosition({ action }) - const { dispatch, unsubscribe } = runFx(watchPositionFx) + }; + const action = jest.fn(); + const watchPositionFx = WatchPosition({ action }); + const { dispatch, unsubscribe } = runFx(watchPositionFx); expect(dispatch).toBeCalledWith(action, { coords: { longitude: 42.0, latitude: 42.1 } - }) - unsubscribe() - }) + }); + unsubscribe(); + }); it("should call error handler on error", () => { navigator.geolocation = { watchPosition: function (successCallback, errorCallback) { - errorCallback("things went wrong") - return 34 + errorCallback("things went wrong"); + return 34; }, clearWatch: function (watchId) { - expect(watchId).toBe(34) + expect(watchId).toBe(34); } - } - const action = jest.fn() - const error = jest.fn() - const watchPositionFx = WatchPosition({ action, error }) - const { dispatch, unsubscribe } = runFx(watchPositionFx) - expect(dispatch).toBeCalledWith(error, "things went wrong") - unsubscribe() - }) -}) + }; + const action = jest.fn(); + const error = jest.fn(); + const watchPositionFx = WatchPosition({ action, error }); + const { dispatch, unsubscribe } = runFx(watchPositionFx); + expect(dispatch).toBeCalledWith(error, "things went wrong"); + unsubscribe(); + }); +}); diff --git a/test/subs/History.test.js b/test/subs/History.test.js index 6ee3d94..79383d0 100644 --- a/test/subs/History.test.js +++ b/test/subs/History.test.js @@ -1,21 +1,21 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { HistoryPop } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { HistoryPop } from "../../src"; describe("History subscription", () => { it("should add and remove popstate listener", () => { - const action = jest.fn() - const historyPop = HistoryPop({ action }) - const { dispatch, unsubscribe } = runFx(historyPop) + const action = jest.fn(); + const historyPop = HistoryPop({ action }); + const { dispatch, unsubscribe } = runFx(historyPop); const popStateEvent = new PopStateEvent("popstate", { state: { foo: "bar" } - }) - window.dispatchEvent(popStateEvent) - expect(dispatch).toBeCalledWith(action, popStateEvent) - unsubscribe() + }); + window.dispatchEvent(popStateEvent); + expect(dispatch).toBeCalledWith(action, popStateEvent); + unsubscribe(); - dispatch.mockReset() - document.dispatchEvent(popStateEvent) - expect(dispatch).not.toBeCalled() - }) -}) + dispatch.mockReset(); + document.dispatchEvent(popStateEvent); + expect(dispatch).not.toBeCalled(); + }); +}); diff --git a/test/subs/Keyboard.test.js b/test/subs/Keyboard.test.js index 99f15db..a359029 100644 --- a/test/subs/Keyboard.test.js +++ b/test/subs/Keyboard.test.js @@ -1,45 +1,45 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { Keyboard } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { Keyboard } from "../../src"; describe("Keyboard subscription", () => { it("should add and remove keydown listener", () => { - const keyEvent = new KeyboardEvent("keydown", { key: "a", code: "KeyA" }) - const action = jest.fn() - const keyboardFx = Keyboard({ downs: true, action }) - const { dispatch, unsubscribe } = runFx(keyboardFx) - document.dispatchEvent(keyEvent) - expect(dispatch).toBeCalledWith(action, keyEvent) + const keyEvent = new KeyboardEvent("keydown", { key: "a", code: "KeyA" }); + const action = jest.fn(); + const keyboardFx = Keyboard({ downs: true, action }); + const { dispatch, unsubscribe } = runFx(keyboardFx); + document.dispatchEvent(keyEvent); + expect(dispatch).toBeCalledWith(action, keyEvent); - dispatch.mockReset() - unsubscribe() - document.dispatchEvent(keyEvent) - expect(dispatch).not.toBeCalled() - }) + dispatch.mockReset(); + unsubscribe(); + document.dispatchEvent(keyEvent); + expect(dispatch).not.toBeCalled(); + }); it("should add and remove keyup listener", () => { - const keyEvent = new KeyboardEvent("keyup", { key: "a", code: "KeyA" }) - const action = jest.fn() - const keyboardFx = Keyboard({ ups: true, action }) - const { dispatch, unsubscribe } = runFx(keyboardFx) - document.dispatchEvent(keyEvent) - expect(dispatch).toBeCalledWith(action, keyEvent) + const keyEvent = new KeyboardEvent("keyup", { key: "a", code: "KeyA" }); + const action = jest.fn(); + const keyboardFx = Keyboard({ ups: true, action }); + const { dispatch, unsubscribe } = runFx(keyboardFx); + document.dispatchEvent(keyEvent); + expect(dispatch).toBeCalledWith(action, keyEvent); - dispatch.mockReset() - unsubscribe() - document.dispatchEvent(keyEvent) - expect(dispatch).not.toBeCalled() - }) + dispatch.mockReset(); + unsubscribe(); + document.dispatchEvent(keyEvent); + expect(dispatch).not.toBeCalled(); + }); it("should add and remove keypress listener", () => { - const keyEvent = new KeyboardEvent("keypress", { key: "a", code: "KeyA" }) - const action = jest.fn() - const keyboardFx = Keyboard({ presses: true, action }) - const { dispatch, unsubscribe } = runFx(keyboardFx) - document.dispatchEvent(keyEvent) - expect(dispatch).toBeCalledWith(action, keyEvent) + const keyEvent = new KeyboardEvent("keypress", { key: "a", code: "KeyA" }); + const action = jest.fn(); + const keyboardFx = Keyboard({ presses: true, action }); + const { dispatch, unsubscribe } = runFx(keyboardFx); + document.dispatchEvent(keyEvent); + expect(dispatch).toBeCalledWith(action, keyEvent); - dispatch.mockReset() - unsubscribe() - document.dispatchEvent(keyEvent) - expect(dispatch).not.toBeCalled() - }) -}) + dispatch.mockReset(); + unsubscribe(); + document.dispatchEvent(keyEvent); + expect(dispatch).not.toBeCalled(); + }); +}); diff --git a/test/subs/Time.test.js b/test/subs/Time.test.js index fe357d0..825d5aa 100644 --- a/test/subs/Time.test.js +++ b/test/subs/Time.test.js @@ -1,62 +1,62 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { Interval } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { Interval } from "../../src"; describe("Interval subscription", () => { it("should get the current time at an interval until unsubscribed", () => { - jest.useFakeTimers() - const every = 1000 - let now = 0 - const defaultPerformance = global.performance - global.performance = { now: () => (now += every) } + jest.useFakeTimers(); + const every = 1000; + let now = 0; + const defaultPerformance = global.performance; + global.performance = { now: () => (now += every) }; try { - const action = jest.fn() - const intervalSub = Interval({ every, action }) - const { dispatch, unsubscribe } = runFx(intervalSub) - expect(dispatch).not.toBeCalled() - jest.runOnlyPendingTimers() - expect(dispatch).toBeCalledWith(action, every) + const action = jest.fn(); + const intervalSub = Interval({ every, action }); + const { dispatch, unsubscribe } = runFx(intervalSub); + expect(dispatch).not.toBeCalled(); + jest.runOnlyPendingTimers(); + expect(dispatch).toBeCalledWith(action, every); - dispatch.mockReset() - jest.runOnlyPendingTimers() - expect(dispatch).toBeCalledWith(action, 2 * every) + dispatch.mockReset(); + jest.runOnlyPendingTimers(); + expect(dispatch).toBeCalledWith(action, 2 * every); - dispatch.mockReset() - unsubscribe() - jest.runOnlyPendingTimers() - expect(dispatch).not.toBeCalled() + dispatch.mockReset(); + unsubscribe(); + jest.runOnlyPendingTimers(); + expect(dispatch).not.toBeCalled(); } finally { - global.performance = defaultPerformance - jest.useRealTimers() + global.performance = defaultPerformance; + jest.useRealTimers(); } - }) + }); it("should get the current date at an interval until unsubscribed", () => { - jest.useFakeTimers() - const every = 1000 - let now = 0 - const defaultDate = global.Date + jest.useFakeTimers(); + const every = 1000; + let now = 0; + const defaultDate = global.Date; global.Date = function () { - return { now: (now += every) } - } + return { now: (now += every) }; + }; try { - const action = jest.fn() - const intervalSub = Interval({ every, asDate: true, action }) - const { dispatch, unsubscribe } = runFx(intervalSub) - expect(dispatch).not.toBeCalled() - jest.runOnlyPendingTimers() - expect(dispatch).toBeCalledWith(action, { now: every }) + const action = jest.fn(); + const intervalSub = Interval({ every, asDate: true, action }); + const { dispatch, unsubscribe } = runFx(intervalSub); + expect(dispatch).not.toBeCalled(); + jest.runOnlyPendingTimers(); + expect(dispatch).toBeCalledWith(action, { now: every }); - dispatch.mockReset() - jest.runOnlyPendingTimers() - expect(dispatch).toBeCalledWith(action, { now: 2 * every }) + dispatch.mockReset(); + jest.runOnlyPendingTimers(); + expect(dispatch).toBeCalledWith(action, { now: 2 * every }); - dispatch.mockReset() - unsubscribe() - jest.runOnlyPendingTimers() - expect(dispatch).not.toBeCalled() + dispatch.mockReset(); + unsubscribe(); + jest.runOnlyPendingTimers(); + expect(dispatch).not.toBeCalled(); } finally { - global.Date = defaultDate - jest.useRealTimers() + global.Date = defaultDate; + jest.useRealTimers(); } - }) -}) + }); +}); diff --git a/test/subs/WebSocketClient.test.js b/test/subs/WebSocketClient.test.js index ebb0058..bf1716b 100644 --- a/test/subs/WebSocketClient.test.js +++ b/test/subs/WebSocketClient.test.js @@ -1,114 +1,114 @@ -import { jest } from "@jest/globals" -import { runFx } from "../utils" -import { WebSocketListen } from "../../src" +import { jest } from "@jest/globals"; +import { runFx } from "../utils"; +import { WebSocketListen } from "../../src"; describe("WebSocketListen subscription", () => { - const url = "wss://localhost" - const mockWebSocket = {} - const defaultWebSocket = global.WebSocket + const url = "wss://localhost"; + const mockWebSocket = {}; + const defaultWebSocket = global.WebSocket; beforeEach(() => { - mockWebSocket.readyState = 1 - mockWebSocket.send = jest.fn() - mockWebSocket.addEventListener = jest.fn() - mockWebSocket.removeEventListener = jest.fn() - mockWebSocket.close = jest.fn() - global.WebSocket = jest.fn(() => mockWebSocket) - }) + mockWebSocket.readyState = 1; + mockWebSocket.send = jest.fn(); + mockWebSocket.addEventListener = jest.fn(); + mockWebSocket.removeEventListener = jest.fn(); + mockWebSocket.close = jest.fn(); + global.WebSocket = jest.fn(() => mockWebSocket); + }); afterEach(() => { - global.WebSocket = defaultWebSocket - }) + global.WebSocket = defaultWebSocket; + }); it("should create a new WebSocket without protocols if not connected and close on unsubscribe", () => { - const webSocketFx = WebSocketListen({ url }) - const { unsubscribe } = runFx(webSocketFx) + const webSocketFx = WebSocketListen({ url }); + const { unsubscribe } = runFx(webSocketFx); - expect(WebSocket).toBeCalledWith(url, undefined) - expect(mockWebSocket.close).not.toBeCalled() + expect(WebSocket).toBeCalledWith(url, undefined); + expect(mockWebSocket.close).not.toBeCalled(); - unsubscribe() - expect(mockWebSocket.close).toBeCalled() - }) + unsubscribe(); + expect(mockWebSocket.close).toBeCalled(); + }); it("should create a new WebSocket with protocols if not connected and close on unsubscribe", () => { - const protocols = ["soap", "wamp"] - const webSocketFx = WebSocketListen({ url, protocols }) - const { unsubscribe } = runFx(webSocketFx) + const protocols = ["soap", "wamp"]; + const webSocketFx = WebSocketListen({ url, protocols }); + const { unsubscribe } = runFx(webSocketFx); - expect(WebSocket).toBeCalledWith(url, protocols) - expect(mockWebSocket.close).not.toBeCalled() + expect(WebSocket).toBeCalledWith(url, protocols); + expect(mockWebSocket.close).not.toBeCalled(); - unsubscribe() - expect(mockWebSocket.close).toBeCalled() - }) + unsubscribe(); + expect(mockWebSocket.close).toBeCalled(); + }); it("should listen for messages and remove the listener on unsubscribe", () => { - const message = JSON.stringify({ some: "message value" }) - const action = jest.fn() - const webSocketFx = WebSocketListen({ url, action }) - const { dispatch, unsubscribe } = runFx(webSocketFx) + const message = JSON.stringify({ some: "message value" }); + const action = jest.fn(); + const webSocketFx = WebSocketListen({ url, action }); + const { dispatch, unsubscribe } = runFx(webSocketFx); expect(mockWebSocket.addEventListener).toBeCalledWith( "message", expect.any(Function) - ) - expect(mockWebSocket.removeEventListener).not.toBeCalled() + ); + expect(mockWebSocket.removeEventListener).not.toBeCalled(); - const messageListener = mockWebSocket.addEventListener.mock.calls[0][1] - messageListener(message) - expect(dispatch).toBeCalledWith(action, message) + const messageListener = mockWebSocket.addEventListener.mock.calls[0][1]; + messageListener(message); + expect(dispatch).toBeCalledWith(action, message); - unsubscribe() + unsubscribe(); expect(mockWebSocket.removeEventListener).toBeCalledWith( "message", expect.any(Function) - ) - }) + ); + }); it("should handle errors and remove the listener on unsubscribe", () => { - const errorMessage = new Error("uh oh!") - const error = jest.fn() - const webSocketFx = WebSocketListen({ url, error }) - const { dispatch, unsubscribe } = runFx(webSocketFx) + const errorMessage = new Error("uh oh!"); + const error = jest.fn(); + const webSocketFx = WebSocketListen({ url, error }); + const { dispatch, unsubscribe } = runFx(webSocketFx); expect(mockWebSocket.addEventListener).toBeCalledWith( "error", expect.any(Function) - ) - expect(mockWebSocket.removeEventListener).not.toBeCalled() + ); + expect(mockWebSocket.removeEventListener).not.toBeCalled(); - const errorListener = mockWebSocket.addEventListener.mock.calls[1][1] - errorListener(errorMessage) - expect(dispatch).toBeCalledWith(error, errorMessage) + const errorListener = mockWebSocket.addEventListener.mock.calls[1][1]; + errorListener(errorMessage); + expect(dispatch).toBeCalledWith(error, errorMessage); - unsubscribe() + unsubscribe(); expect(mockWebSocket.removeEventListener).toBeCalledWith( "error", expect.any(Function) - ) - }) + ); + }); it("should reuse an existing WebSocket and not close the socket if another socket is already listening", () => { - const onopen = jest.fn() - const onclose = jest.fn() + const onopen = jest.fn(); + const onclose = jest.fn(); - const listen1 = jest.fn() + const listen1 = jest.fn(); const webSocketFx1 = WebSocketListen({ url, listen: listen1, open: onopen, close: onclose - }) - const { unsubscribe: unsubscribe1 } = runFx(webSocketFx1) + }); + const { unsubscribe: unsubscribe1 } = runFx(webSocketFx1); - WebSocket.mockReset() - const listen2 = jest.fn() + WebSocket.mockReset(); + const listen2 = jest.fn(); const webSocketFx2 = WebSocketListen({ url, listen: listen2, open: onopen, close: onclose - }) - const { unsubscribe: unsubscribe2 } = runFx(webSocketFx2) - expect(WebSocket).not.toBeCalled() + }); + const { unsubscribe: unsubscribe2 } = runFx(webSocketFx2); + expect(WebSocket).not.toBeCalled(); - unsubscribe1() - expect(mockWebSocket.close).not.toBeCalled() - unsubscribe2() - expect(mockWebSocket.close).toBeCalled() - }) -}) + unsubscribe1(); + expect(mockWebSocket.close).not.toBeCalled(); + unsubscribe2(); + expect(mockWebSocket.close).toBeCalled(); + }); +}); diff --git a/test/utils.js b/test/utils.js index 93d1389..4ba35d3 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,7 +1,7 @@ -import { jest } from "@jest/globals" +import { jest } from "@jest/globals"; export const runFx = fx => { - const dispatch = jest.fn() - const unsubscribe = fx[0](dispatch, fx[1]) - return { dispatch, unsubscribe } -} + const dispatch = jest.fn(); + const unsubscribe = fx[0](dispatch, fx[1]); + return { dispatch, unsubscribe }; +};