Skip to content

Commit

Permalink
feat: add test
Browse files Browse the repository at this point in the history
  • Loading branch information
Allen Zhang (张涛) committed Mar 4, 2024
1 parent 7591fcc commit bbe7d59
Show file tree
Hide file tree
Showing 21 changed files with 542 additions and 0 deletions.
3 changes: 3 additions & 0 deletions examples/playwright-test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.DS_Store
/node_modules
package-lock.json
19 changes: 19 additions & 0 deletions examples/playwright-test/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const {chromium} = require('playwright');
const main = async () => {
const browser = await chromium.launch()
const page = await browser.newPage();
// 进入被测页面
await page.goto('http://test.com')
// 执行测试用例
// 用例1
await page.click('button')
// 用例2
await page.fill('input', 'test')
// 用例3
await page.click('text=submit')
const coverage = await page.evaluate(`window.__coverage__`)
console.log(coverage)
browser.close()
}

main()
15 changes: 15 additions & 0 deletions examples/playwright-test/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "play-write",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node ./main.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"playwright": "^1.42.1"
}
}
13 changes: 13 additions & 0 deletions examples/todomvc-react/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": ["eslint:recommended", "plugin:react/recommended"],
"settings": {
"react": {
"version": "17.0.2"
}
},
"rules": {
"no-extra-parens": 0,
"react/prop-types": 0,
"react/react-in-jsx-scope": 0
}
}
3 changes: 3 additions & 0 deletions examples/todomvc-react/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.DS_Store
/node_modules
package-lock.json
3 changes: 3 additions & 0 deletions examples/todomvc-react/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports={
plugins: ['istanbul']
}
42 changes: 42 additions & 0 deletions examples/todomvc-react/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "todomvc-react",
"version": "1.0.0",
"description": "A TodoMVC written in React.",
"private": true,
"engines": {
"node": ">=18.13.0",
"npm": ">=8.19.3"
},
"scripts": {
"build": "webpack --config webpack.prod.js",
"dev": "webpack serve --open --config webpack.dev.js",
"serve": "http-server ./dist -p 7002 -c-1 --cors",
"test": "jest"
},
"devDependencies": {
"@babel/core": "^7.24.0",
"@babel/preset-env": "^7.24.0",
"@babel/preset-react": "^7.23.3",
"babel-loader": "^9.1.3",
"babel-plugin-istanbul": "^6.1.1",
"css-loader": "^6.10.0",
"css-minimizer-webpack-plugin": "^4.2.2",
"eslint-plugin-react": "^7.34.0",
"html-webpack-plugin": "^5.6.0",
"http-server": "^14.1.1",
"mini-css-extract-plugin": "^2.8.1",
"style-loader": "^3.3.4",
"webpack": "^5.90.3",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"webpack-merge": "^5.10.0"
},
"dependencies": {
"classnames": "^2.5.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^6.22.2",
"todomvc-app-css": "^2.4.3",
"todomvc-common": "^1.0.5"
}
}
18 changes: 18 additions & 0 deletions examples/todomvc-react/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en" data-framework="react">
<head>
<meta charset="UTF-8" />
<meta name="description" content="A TodoMVC written in React." />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>TodoMVC: React</title>
</head>
<body>
<section class="todoapp" id="root"></section>
<footer class="info">
<p>Double-click to edit a todo</p>
<p>Created by the TodoMVC Team</p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
</body>
</html>
15 changes: 15 additions & 0 deletions examples/todomvc-react/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";
import { render } from "react-dom";
import { HashRouter, Route, Routes } from "react-router-dom";

import { App } from "./todo/app";
import "todomvc-app-css/index.css";

render(
<HashRouter>
<Routes>
<Route path="*" element={<App />} />
</Routes>
</HashRouter>,
document.getElementById("root")
);
24 changes: 24 additions & 0 deletions examples/todomvc-react/src/todo/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* used for things that should be hidden in the ui,
but useful for people who use screen readers */
.visually-hidden {
border: 0;
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
width: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
position: absolute;
white-space: nowrap;
}

.toggle-all {
width: 40px !important;
height: 60px !important;
right: auto !important;
}

.toggle-all-label {
pointer-events: none;
}
20 changes: 20 additions & 0 deletions examples/todomvc-react/src/todo/app.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useReducer } from "react";
import { Header } from "./components/header";
import { Main } from "./components/main";
import { Footer } from "./components/footer";

import { todoReducer } from "./reducer";

import "./app.css";

export function App() {
const [todos, dispatch] = useReducer(todoReducer, []);

return (
<>
<Header dispatch={dispatch} />
<Main todos={todos} dispatch={dispatch} />
<Footer todos={todos} dispatch={dispatch} />
</>
);
}
43 changes: 43 additions & 0 deletions examples/todomvc-react/src/todo/components/footer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useCallback, useMemo } from "react";
import { useLocation } from "react-router-dom";
import classnames from "classnames";

import { REMOVE_COMPLETED_ITEMS } from "../constants";

export function Footer({ todos, dispatch }) {
const { pathname: route } = useLocation();

const activeTodos = useMemo(() => todos.filter((todo) => !todo.completed), [todos]);

const removeCompleted = useCallback(() => dispatch({ type: REMOVE_COMPLETED_ITEMS }), [dispatch]);

// prettier-ignore
if (todos.length === 0)
return null;

return (
<footer className="footer" data-testid="footer">
<span className="todo-count">{`${activeTodos.length} ${activeTodos.length === 1 ? "item" : "items"} left!`}</span>
<ul className="filters" data-testid="footer-navigation">
<li>
<a className={classnames({ selected: route === "/" })} href="#/">
All
</a>
</li>
<li>
<a className={classnames({ selected: route === "/active" })} href="#/active">
Active
</a>
</li>
<li>
<a className={classnames({ selected: route === "/completed" })} href="#/completed">
Completed
</a>
</li>
</ul>
<button className="clear-completed" disabled={activeTodos.length === todos.length} onClick={removeCompleted}>
Clear completed
</button>
</footer>
);
}
15 changes: 15 additions & 0 deletions examples/todomvc-react/src/todo/components/header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useCallback } from "react";
import { Input } from "./input";

import { ADD_ITEM } from "../constants";

export function Header({ dispatch }) {
const addItem = useCallback((title) => dispatch({ type: ADD_ITEM, payload: { title } }), [dispatch]);

return (
<header className="header" data-testid="header">
<h1>todos</h1>
<Input onSubmit={addItem} label="New Todo Input" placeholder="What needs to be done?" />
</header>
);
}
49 changes: 49 additions & 0 deletions examples/todomvc-react/src/todo/components/input.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useCallback } from "react";

const sanitize = (string) => {
const map = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#x27;",
"/": "&#x2F;",
};
const reg = /[&<>"'/]/gi;
return string.replace(reg, (match) => map[match]);
};

const hasValidMin = (value, min) => {
return value.length >= min;
};

export function Input({ onSubmit, placeholder, label, defaultValue, onBlur }) {
const handleBlur = useCallback(() => {
if (onBlur)
onBlur();
}, [onBlur]);

const handleKeyDown = useCallback(
(e) => {
if (e.key === "Enter") {
const value = e.target.value.trim();

if (!hasValidMin(value, 2))
return;

onSubmit(sanitize(value));
e.target.value = "";
}
},
[onSubmit]
);

return (
<div className="input-container">
<input className="new-todo" id="todo-input" type="text" data-testid="text-input" autoFocus placeholder={placeholder} defaultValue={defaultValue} onBlur={handleBlur} onKeyDown={handleKeyDown} />
<label className="visually-hidden" htmlFor="todo-input">
{label}
</label>
</div>
);
}
53 changes: 53 additions & 0 deletions examples/todomvc-react/src/todo/components/item.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { memo, useState, useCallback } from "react";
import classnames from "classnames";

import { Input } from "./input";

import { TOGGLE_ITEM, REMOVE_ITEM, UPDATE_ITEM } from "../constants";

export const Item = memo(function Item({ todo, dispatch, index }) {
const [isWritable, setIsWritable] = useState(false);
const { title, completed, id } = todo;

const toggleItem = useCallback(() => dispatch({ type: TOGGLE_ITEM, payload: { id } }), [dispatch]);
const removeItem = useCallback(() => dispatch({ type: REMOVE_ITEM, payload: { id } }), [dispatch]);
const updateItem = useCallback((id, title) => dispatch({ type: UPDATE_ITEM, payload: { id, title } }), [dispatch]);

const handleDoubleClick = useCallback(() => {
setIsWritable(true);
}, []);

const handleBlur = useCallback(() => {
setIsWritable(false);
}, []);

const handleUpdate = useCallback(
(title) => {
if (title.length === 0)
removeItem(id);
else
updateItem(id, title);

setIsWritable(false);
},
[id, removeItem, updateItem]
);

return (
<li className={classnames({ completed: todo.completed })} data-testid="todo-item">
<div className="view">
{isWritable ? (
<Input onSubmit={handleUpdate} label="Edit Todo Input" defaultValue={title} onBlur={handleBlur} />
) : (
<>
<input className="toggle" type="checkbox" data-testid="todo-item-toggle" checked={completed} onChange={toggleItem} />
<label data-testid="todo-item-label" onDoubleClick={handleDoubleClick}>
{title}
</label>
<button className="destroy" data-testid="todo-item-button" onClick={removeItem} />
</>
)}
</div>
</li>
);
});
45 changes: 45 additions & 0 deletions examples/todomvc-react/src/todo/components/main.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useMemo, useCallback } from "react";
import { useLocation } from "react-router-dom";

import { Item } from "./item";
import classnames from "classnames";

import { TOGGLE_ALL } from "../constants";

export function Main({ todos, dispatch }) {
const { pathname: route } = useLocation();

const visibleTodos = useMemo(
() =>
todos.filter((todo) => {
if (route === "/active")
return !todo.completed;

if (route === "/completed")
return todo.completed;

return todo;
}),
[todos, route]
);

const toggleAll = useCallback((e) => dispatch({ type: TOGGLE_ALL, payload: { completed: e.target.checked } }), [dispatch]);

return (
<main className="main" data-testid="main">
{visibleTodos.length > 0 ? (
<div className="toggle-all-container">
<input className="toggle-all" type="checkbox" data-testid="toggle-all" checked={visibleTodos.every((todo) => todo.completed)} onChange={toggleAll} />
<label className="toggle-all-label" htmlFor="toggle-all">
Toggle All Input
</label>
</div>
) : null}
<ul className={classnames("todo-list")} data-testid="todo-list">
{visibleTodos.map((todo, index) => (
<Item todo={todo} key={todo.id} dispatch={dispatch} index={index} />
))}
</ul>
</main>
);
}
Loading

0 comments on commit bbe7d59

Please sign in to comment.