Initial commit

This commit is contained in:
2025-12-27 20:24:47 +01:00
commit 7b6f164130
5573 changed files with 727178 additions and 0 deletions

28
node_modules/jest-websocket-mock/examples/README.md generated vendored Normal file
View File

@@ -0,0 +1,28 @@
# Examples
This folder is here to showcase testing examples of a real application.
To run the tests:
```bash
cd redux-saga # or `cd hooks`...
npm install
npm install jest-websocket-mock
# Or, to run the tests against a local jest-websocket-mock build:
cd ..; npm run build && npm pack; cd examples; npm install ../jest-websocket-mock-*;
SKIP_PREFLIGHT_CHECK=true npm test -- --coverage
```
The websocket tests are under `src/__tests__/saga.test.js` and ``src/**tests**/App.test.js`.
If you want to see the app running locally:
```bash
node server.js # start the server
```
and in another terminal:
```bash
npm start # start the client
```

View File

@@ -0,0 +1,44 @@
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.<br />
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br />
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.<br />
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.<br />
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br />
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).

View File

@@ -0,0 +1,42 @@
{
"name": "hooks",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"@types/jest": "^24.9.1",
"@types/node": "^12.12.50",
"@types/react": "^16.9.43",
"@types/react-dom": "^16.9.8",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1",
"typescript": "^3.7.5"
},
"peerDependencies": {
"jest-websocket-mock": "~2.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@@ -0,0 +1,43 @@
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import WS from "jest-websocket-mock";
import App from "./App";
let ws: WS;
beforeEach(() => {
ws = new WS("ws://localhost:8080");
});
afterEach(() => {
WS.clean();
});
describe("The App component", () => {
it("renders a dot indicating the connection status", async () => {
render(<App />);
expect(screen.getByTitle("disconnected")).toBeInTheDocument();
await ws.connected;
expect(screen.getByTitle("connected")).toBeInTheDocument();
ws.close();
expect(screen.getByTitle("disconnected")).toBeInTheDocument();
});
it("sends and receives messages", async () => {
render(<App />);
await ws.connected;
const input = screen.getByPlaceholderText("type your message here...");
userEvent.type(input, "Hello there");
fireEvent.submit(input);
await expect(ws).toReceiveMessage("Hello there");
expect(screen.getByText("(sent) Hello there")).toBeInTheDocument();
ws.send("[echo] Hello there");
expect(
screen.getByText("(received) [echo] Hello there")
).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,70 @@
import React, {
useState,
ChangeEvent,
FormEvent,
useEffect,
useRef,
} from "react";
type MessageProps = { text: string; side: "sent" | "received" };
const Message = ({ text, side }: MessageProps) => (
<div>{`(${side}) ${text}`}</div>
);
function App() {
const wsRef = useRef<WebSocket>();
const [connected, setConnected] = useState(false);
const [messages, setMessages] = useState<MessageProps[]>([]);
const [currentMessage, setCurrentMessage] = useState("");
useEffect(() => {
const ws = new WebSocket(`ws://${window.location.hostname}:8080`);
ws.onopen = () => {
setConnected(true);
};
ws.onclose = () => setConnected(false);
ws.onmessage = (event) =>
setMessages((m) => [{ side: "received", text: event.data }, ...m]);
wsRef.current = ws;
}, []);
const onChange = (event: ChangeEvent<HTMLInputElement>) =>
setCurrentMessage(event.target.value);
const send = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
wsRef.current!.send(currentMessage);
setCurrentMessage("");
setMessages((m) => [{ side: "sent", text: currentMessage }, ...m]);
};
return (
<div className="App">
<div
className={
connected
? "ConnectionIndicator ConnectionIndicator--connected"
: "ConnectionIndicator ConnectionIndicator--disconnected"
}
title={connected ? "connected" : "disconnected"}
/>
<div className="Messages">
{messages.map((message, i) => (
<Message key={i} {...message} />
))}
</div>
<form className="MessageForm" onSubmit={send}>
<input
autoFocus
className="MessageInput"
value={currentMessage}
onChange={onChange}
placeholder="type your message here..."
/>
</form>
</div>
);
}
export default App;

View File

@@ -0,0 +1,13 @@
const { act } = require("react-dom/test-utils");
describe("The index", () => {
it("can be imported without errors", () => {
const root = document.createElement("div");
root.setAttribute("id", "root");
document.body.appendChild(root);
act(() => {
require("./index.tsx");
});
});
});

View File

@@ -0,0 +1,11 @@
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import App from "./App";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);

View File

@@ -0,0 +1 @@
/// <reference types="react-scripts" />

View File

@@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';

View File

@@ -0,0 +1,54 @@
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.App {
background-color: #282c34;
min-height: 100vh;
color: white;
padding: 2rem;
}
.ConnectionIndicator {
width: 2rem;
height: 2rem;
border-radius: 2rem;
position: fixed;
top: 2rem;
right: 2rem;
}
.ConnectionIndicator--connected {
background-color: green;
}
.ConnectionIndicator--disconnected {
background-color: indianred;
}
.Messages {
margin-bottom: 8rem;
display: flex;
flex-direction: column-reverse;
}
.MessageForm {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 2rem;
background-color: inherit;
}
.MessageInput {
width: 100%;
}

View File

@@ -0,0 +1,48 @@
{
"name": "examples",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-redux": "^7.2.0",
"react-scripts": "^3.4.1",
"redux": "^4.0.5",
"redux-actions": "^2.6.5",
"redux-saga": "^1.1.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"jest": {
"coverageThreshold": {
"global": {
"branches": 100,
"functions": 100,
"lines": 100,
"statements": 100
}
}
},
"devDependencies": {
"@testing-library/jest-dom": "^5.11.0",
"@testing-library/react": "^10.4.6",
"@testing-library/user-event": "^12.0.11",
"mock-socket": "^9.3.0"
},
"peerDependencies": {
"jest-websocket-mock": "~2.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@@ -0,0 +1,15 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@@ -0,0 +1,14 @@
import React from "react";
import ConnectionIndicator from "./ConnectionIndicator";
import Messages from "./Messages";
import MessageInput from "./MessageInput";
const App = () => (
<div className="App">
<ConnectionIndicator />
<Messages />
<MessageInput />
</div>
);
export default App;

View File

@@ -0,0 +1,17 @@
import React from "react";
import { connect } from "react-redux";
const ConnectionIndicator = ({ connected }) => (
<div
className={
connected
? "ConnectionIndicator ConnectionIndicator--connected"
: "ConnectionIndicator ConnectionIndicator--disconnected"
}
title={connected ? "connected" : "disconnected"}
/>
);
export default connect((state) => ({ connected: state.connected }))(
ConnectionIndicator
);

View File

@@ -0,0 +1,35 @@
import React, { PureComponent } from "react";
import { connect } from "react-redux";
import { actions } from "./store/reducer";
class MessageInput extends PureComponent {
state = { message: "" };
onChange = event => this.setState({ message: event.target.value });
onSubmit = event => {
event.preventDefault();
this.props.send(this.state.message);
this.setState({ message: "" });
};
render() {
const { message } = this.state;
return (
<form className="MessageForm" onSubmit={this.onSubmit}>
<input
autoFocus
className="MessageInput"
value={message}
onChange={this.onChange}
placeholder="type your message here..."
/>
</form>
);
}
}
export default connect(
null,
{ send: actions.send }
)(MessageInput);

View File

@@ -0,0 +1,14 @@
import React from "react";
import { connect } from "react-redux";
const Message = ({ text, side }) => <div>{`(${side}) ${text}`}</div>;
const Messages = ({ messages }) => (
<div className="Messages">
{messages.map((message, i) => (
<Message key={i} {...message} />
))}
</div>
);
export default connect((state) => ({ messages: state.messages }))(Messages);

View File

@@ -0,0 +1,35 @@
import React from "react";
import { render, screen, userEvent, fireEvent } from "../test-utils";
import App from "../App";
describe("The App component", () => {
it("renders the app skeleton", async () => {
const { container } = await render(<App />);
expect(container.firstChild).toMatchSnapshot();
});
it("renders a green dot when successfully connected", async () => {
await render(<App />);
expect(screen.getByTitle("connected")).toBeInTheDocument();
});
it("renders a red dot when not connected", async () => {
const { ws } = await render(<App />);
ws.close();
expect(screen.getByTitle("disconnected")).toBeInTheDocument();
});
it("sends the message when submitting the form", async () => {
const { ws } = await render(<App />);
const input = screen.getByPlaceholderText("type your message here...");
userEvent.type(input, "Hello there");
fireEvent.submit(input);
expect(screen.getByText("(sent) Hello there")).toBeInTheDocument();
await expect(ws).toReceiveMessage("Hello there");
ws.send("[echo] Hello there");
expect(
screen.getByText("(received) [echo] Hello there")
).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`The App component renders the app skeleton 1`] = `
<div
class="App"
>
<div
class="ConnectionIndicator ConnectionIndicator--connected"
title="connected"
/>
<div
class="Messages"
/>
<form
class="MessageForm"
>
<input
class="MessageInput"
placeholder="type your message here..."
value=""
/>
</form>
</div>
`;

View File

@@ -0,0 +1,10 @@
import ReactDOM from "react-dom";
import "..";
jest.mock("react-dom");
describe("The index", () => {
it("can be imported without errors", () => {
expect(ReactDOM.render).toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,108 @@
import WS from "jest-websocket-mock";
import makeStore from "../store";
import { actions } from "../store/reducer";
let ws, store;
beforeEach(async () => {
ws = new WS("ws://localhost:8080");
store = makeStore();
await ws.connected;
ws.send("Hello there");
});
afterEach(() => {
WS.clean();
});
describe("The saga", () => {
it("connects to the websocket server", () => {
expect(store.getState().messages).toEqual([
{ side: "received", text: "Hello there" },
]);
});
it("stores new messages", () => {
ws.send("how you doin?");
expect(store.getState().messages).toEqual([
{ side: "received", text: "Hello there" },
{ side: "received", text: "how you doin?" },
]);
});
it("stores new messages received shortly one after the other", () => {
ws.send("hey");
ws.send("hey?");
ws.send("hey??");
ws.send("hey???");
expect(store.getState().messages).toEqual([
{ side: "received", text: "Hello there" },
{ side: "received", text: "hey" },
{ side: "received", text: "hey?" },
{ side: "received", text: "hey??" },
{ side: "received", text: "hey???" },
]);
});
it("sends messages", async () => {
store.dispatch(actions.send("oh hi Mark"));
await expect(ws).toReceiveMessage("oh hi Mark");
expect(ws).toHaveReceivedMessages(["oh hi Mark"]);
expect(store.getState().messages).toEqual([
{ side: "received", text: "Hello there" },
{ side: "sent", text: "oh hi Mark" },
]);
});
it("sends messages in a quick succession", async () => {
store.dispatch(actions.send("hey"));
store.dispatch(actions.send("hey?"));
store.dispatch(actions.send("hey??"));
store.dispatch(actions.send("hey???"));
await expect(ws).toReceiveMessage("hey");
await expect(ws).toReceiveMessage("hey?");
await expect(ws).toReceiveMessage("hey??");
await expect(ws).toReceiveMessage("hey???");
expect(ws).toHaveReceivedMessages(["hey", "hey?", "hey??", "hey???"]);
expect(store.getState().messages).toEqual([
{ side: "received", text: "Hello there" },
{ side: "sent", text: "hey" },
{ side: "sent", text: "hey?" },
{ side: "sent", text: "hey??" },
{ side: "sent", text: "hey???" },
]);
});
it("marks the connection as active when it successfully connects to the ws server", () => {
expect(store.getState().connected).toBe(true);
});
it("marks the connection as inactive after a disconnect", async () => {
ws.close();
await ws.closed;
expect(store.getState().connected).toBe(false);
});
it("marks the connection as inactive after a connection error", async () => {
ws.error();
await ws.closed;
expect(store.getState().connected).toBe(false);
});
it("reconnects after losing the ws connection", async () => {
// We cannot use jest.useFakeTimers because mock-socket has to work around timing issues
jest.spyOn(window, "setTimeout");
ws.error();
await ws.closed;
expect(store.getState().connected).toBe(false);
// Trigger our delayed reconnection
window.setTimeout.mock.calls.forEach(([cb, , ...args]) => cb(...args));
await ws.connected; // reconnected!
expect(store.getState().connected).toBe(true);
window.setTimeout.mockRestore();
});
});

View File

@@ -0,0 +1,15 @@
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import "./styles.css";
import makeStore from "./store";
import App from "./App";
const store = makeStore();
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);

View File

@@ -0,0 +1 @@
import "@testing-library/jest-dom";

View File

@@ -0,0 +1,11 @@
import { createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import reducer from "./reducer";
import saga from "./saga";
export default () => {
const sagaMiddleware = createSagaMiddleware();
const store = createStore(reducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(saga);
return store;
};

View File

@@ -0,0 +1,30 @@
import { createActions, handleActions, combineActions } from "redux-actions";
const defaultState = {
messages: [],
connected: false,
};
export const actions = createActions({
STORE_SENT_MESSAGE: text => ({ text, side: "sent" }),
STORE_RECEIVED_MESSAGE: text => ({ text, side: "received" }),
SEND: undefined,
CONNECTION_SUCCESS: () => ({ connected: true }),
CONNECTION_LOST: () => ({ connected: false }),
});
const reducer = handleActions(
{
[combineActions(actions.storeReceivedMessage, actions.storeSentMessage)]: (
state,
{ payload }
) => ({ ...state, messages: [...state.messages, payload] }),
[combineActions(actions.connectionSuccess, actions.connectionLost)]: (
state,
{ payload: { connected } }
) => ({ ...state, connected }),
},
defaultState
);
export default reducer;

View File

@@ -0,0 +1,54 @@
import { eventChannel, END } from "redux-saga";
import { cancel, call, delay, fork, put, take } from "redux-saga/effects";
import { actions } from "./reducer";
const RECONNECT_TIMEOUT = 6000;
function websocketInitChannel(connection) {
return eventChannel(emitter => {
const closeCallback = () => {
emitter(actions.connectionLost());
return emitter(END);
};
connection.onmessage = e => {
return emitter(actions.storeReceivedMessage(e.data));
};
connection.onclose = closeCallback;
connection.onerror = closeCallback;
return () => {
// unsubscribe function
connection.close();
};
});
}
function* sendMessage(connection) {
while (true) {
const { payload } = yield take(actions.send);
yield put(actions.storeSentMessage(payload));
yield call([connection, connection.send], payload);
}
}
export default function* saga() {
const connection = new WebSocket(`ws://${window.location.hostname}:8080`);
const channel = yield call(websocketInitChannel, connection);
yield put(actions.connectionSuccess());
const sendMessageTask = yield fork(sendMessage, connection);
try {
while (true) {
const action = yield take(channel);
yield put(action);
}
} finally {
// cancel background tasks...
channel.close();
yield cancel(sendMessageTask);
// ...and start again
yield delay(RECONNECT_TIMEOUT);
return yield call(saga);
}
}

View File

@@ -0,0 +1,54 @@
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.App {
background-color: #282c34;
min-height: 100vh;
color: white;
padding: 2rem;
}
.ConnectionIndicator {
width: 2rem;
height: 2rem;
border-radius: 2rem;
position: fixed;
top: 2rem;
right: 2rem;
}
.ConnectionIndicator--connected {
background-color: green;
}
.ConnectionIndicator--disconnected {
background-color: indianred;
}
.Messages {
margin-bottom: 8rem;
display: flex;
flex-direction: column-reverse;
}
.MessageForm {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 2rem;
background-color: inherit;
}
.MessageInput {
width: 100%;
}

View File

@@ -0,0 +1,24 @@
import React from "react";
import { render } from "@testing-library/react";
import { Provider } from "react-redux";
import WS from "jest-websocket-mock";
import makeStore from "./store";
afterEach(() => {
WS.clean();
});
const renderWithStore = async (ui, options = {}) => {
const ws = new WS("ws://localhost:8080");
const store = makeStore();
const rendered = render(<Provider store={store}>{ui}</Provider>, options);
await ws.connected;
return {
ws,
...rendered,
};
};
export * from "@testing-library/react";
export { default as userEvent } from "@testing-library/user-event";
export { renderWithStore as render };

24
node_modules/jest-websocket-mock/examples/server.js generated vendored Normal file
View File

@@ -0,0 +1,24 @@
/**
* This is a simple example server, mostly here for demonstration
* purposes.
* The subfolders in this directory contain actual client code with
* supporting tests.
**/
const WebSocket = require("ws");
const PORT = 8080;
const server = new WebSocket.Server({ port: PORT });
server.on("connection", function connection(ws, req) {
ws.on("message", function incoming(message) {
console.log(`[received] ${message}`);
ws.send(`[echo] ${message}`);
});
const remoteAddress = req.connection.remoteAddress;
console.log(`[connected] Client at ${remoteAddress}`);
ws.send(`Hello ${remoteAddress}`);
});
console.log(`[start] Starting echo server on port ${PORT}.`);