├── .env.example ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .husky ├── _ │ ├── .gitignore │ └── pre-commit └── pre-commit ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── LICENSE ├── Makefile ├── README.md ├── benchmark-results └── benchmark-results-20240611215118916.json ├── benchmark ├── benchmark.ts └── examples │ └── shouldFail │ └── todomvc-with-one-bug │ ├── .eslintrc │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── public │ └── index.html │ ├── readme.md │ ├── src │ ├── index.js │ └── todo │ │ ├── app.css │ │ ├── app.jsx │ │ ├── components │ │ ├── footer.jsx │ │ ├── header.jsx │ │ ├── input.jsx │ │ ├── item.jsx │ │ └── main.jsx │ │ ├── constants.js │ │ └── reducer.js │ ├── webpack.common.js │ ├── webpack.dev.js │ └── webpack.prod.js ├── eslint.config.js ├── mocks └── testSpecFile.json ├── package-lock.json ├── package.json ├── playwright.config.js ├── src ├── ai.ts ├── cli.test.ts ├── cli.ts └── index.ts ├── tsconfig.json └── vitest.config.ts /.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY=your-openai-api-key-needs-to-get-pasted-here-in-dot-env-file 2 | GOOGLE_GENERATIVE_AI_API_KEY=your-google-generative-ai-api-key-needs-to-get-pasted-here-in-dot-env-file 3 | ANTHROPIC_API_KEY=your-anthropic-api-key-needs-to-get-pasted-here-in-dot-env-file -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Run Autospec System Tests 2 | 3 | # For now, let's only manually trigger: 4 | on: 5 | workflow_dispatch: 6 | # push: 7 | # branches: 8 | # - main 9 | # pull_request: 10 | # branches: 11 | # - main 12 | 13 | jobs: 14 | run-autospec: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v3 20 | 21 | - name: Set up Node.js 22 | uses: actions/setup-node@v3 23 | with: 24 | node-version: "20" 25 | 26 | - name: Install dependencies 27 | run: npm install 28 | 29 | - name: Install Playwright (chrome only) 30 | run: npx playwright install 31 | 32 | - name: Run Unit Tests 33 | run: npm run test 34 | 35 | - name: Run Autospec Benchmark 36 | env: 37 | OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} 38 | run: node benchmark/benchmark.js 39 | 40 | - name: Output Benchmark Summary to Github Action Summary 41 | run: | 42 | # output some backticks: 43 | echo "\`\`\`\n" 44 | cat benchmark/benchmark-results/*.json > $GITHUB_STEP_SUMMARY 45 | echo "\n\`\`\`" 46 | 47 | - name: Upload Benchmark Results 48 | uses: actions/upload-artifact@v4 49 | with: 50 | name: benchmark-results 51 | path: | 52 | benchmark/benchmark-results/* 53 | trajectories/* 54 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release to NPM 2 | 3 | on: 4 | push: 5 | paths: 6 | - "package.json" 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-node@v3 14 | with: 15 | node-version: 20 16 | registry-url: "https://registry.npmjs.org" 17 | - name: Install dependencies 18 | run: npm install 19 | - name: Publish to NPM 20 | run: npm publish 21 | env: 22 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | .env.* 4 | !.env.example 5 | *.log 6 | .vscode 7 | .ds_store 8 | .next 9 | .vercel 10 | *.webm 11 | trajectories/* 12 | # keep .gitkeep though: 13 | !.gitkeep 14 | playwright-report 15 | test-results 16 | tests/successfulTests* 17 | .aider* 18 | Pipfile 19 | benchmark/benchmark-results/* 20 | build 21 | dist -------------------------------------------------------------------------------- /.husky/_/.gitignore: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /.husky/_/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "${0%/*}/h" -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm install 5 | npm run lint 6 | npm run test 7 | npm run format 8 | npm run build -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.13.1 -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachblume/autospec/196bb575301be8f2a23326f92c8a3269c97840ef/.prettierignore -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Zach Blume 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := todomvc 2 | 3 | clean: 4 | rm -rf trajectories 5 | 6 | realworld: 7 | npm run build && npx autospecai --url "https://demo.realworld.io/" --model gemini-1.5-flash-latest 8 | 9 | todomvc: 10 | npm run build && npx autospecai --url "https://todomvc.com/examples/react/dist/" --model gemini-1.5-flash-latest 11 | 12 | bench: 13 | npm run build && npm run benchmark 14 | 15 | release: 16 | npm run lint 17 | npm run format 18 | npm run test 19 | npm run build 20 | # prompt for otp via bash 21 | @read -p "Enter OTP: " OTP; \ 22 | npm publish --otp=$$OTP -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # autospec 2 | 3 | ### Open source end-to-end (e2e) test generation for web apps 4 | 5 | [Code Entrypoint](https://github.com/zachblume/autospec/blob/main/src/index.ts) 6 | 7 | Autospec is an end-to-end test/QA agent, using vision and text language models 8 | to explore and generate commonsense test specifications for web applications. 9 | It aims to mimic user-like judgement on the entire UI output after each 10 | interaction to decide whether to raise an error about how an application 11 | behaves, instead of catching regressions against rigidly defined previous 12 | behavior. 13 | 14 | - This approach allows autospec to test new features immediately after 15 | implementation, not just check for regressions. 16 | - It requires no configuration, making it straightforward to use. 17 | 18 | ### Quick start 19 | 20 | Generate and run 10 specs on TodoMVC, a classic example web app: 21 | 22 | 23 | ``` 24 | npx autospecai --url https://todomvc.com/examples/react/dist/ --apikey YOUR_OPENAI_API_KEY 25 | ``` 26 | 27 | You'll need to say "yes" to install the autospecai package, and the first run 28 | may take a few minutes to download dependencies like browser binaries that are 29 | used to execute the test environment. 30 | 31 | When the run completes, you'll see a summary of the tests that were run and 32 | whether they passed or failed. 33 | 34 | The successful specs will be saved within the `trajectories` folder in your 35 | working directory. You can re-execute these tests at any time by running: 36 | 37 | 38 | ``` 39 | npx playwright test 40 | ``` 41 | 42 | Depending on your existing Playwright setup, you may need to add "trajectories" 43 | to the testDir in your playwright.config.js file. 44 | 45 | ### Using environment variables instead of passing keys as a flag 46 | 47 | Copy the sample .env file, and fill in the OPENAI_API_KEY 48 | or GOOGLE_GENERATIVE_AI_API_KEY before running the app: 49 | 50 | 51 | ```bash 52 | mv .env.example .env # rename the example to .env 53 | nano .env # edit as you like 54 | ``` 55 | 56 | ### Learn more about configuration 57 | 58 | 59 | ```bash 60 | > npx autospecai --help 61 | Usage: npx autospecai --url [--model ] [--spec_limit ] [--help | -h] 62 | 63 | Required: 64 | --url The target URL to run the autospec tests against. 65 | 66 | Optional: 67 | --help, -h Show this help message. 68 | --spec_limit The max number of specs to generate. Default 10. 69 | --model The model to use for spec generation 70 | * "gpt-4o" (default) 71 | * "gemini-1.5-flash-latest" 72 | * "claude-3-haiku" 73 | * (note: Gemini flash is free up to rate limits) 74 | --apikey The relevant API key for the chosen model's API. 75 | * If not specified, we'll fall back on the 76 | following environment variables: 77 | * OPENAI_API_KEY 78 | * GOOGLE_GENERATIVE_AI_API_KEY 79 | * ANTHROPIC_API_KEY 80 | 81 | ``` 82 | 83 | ## Example output 84 | 85 | Logs are printed to the console like so, as well as to a trajectories/\*\* 86 | folder alongside a video, png and HTML/JSON DOM snapshots: 87 | 88 | 89 | ``` 90 | Test Summary: 91 | ✔ 1. Check if the page loads successfully 92 | 1.1) action: markSpecAsComplete, reason: The spec passed, fullProseExplanationOfReasoning100charmax: The page loaded successfully with all expected elements visible., planningThoughtAboutTheActionIWillTake: The page has loaded successfully as indicated by the presence of the header, input field, and footer. I will mark the spec as complete. 93 | ✔ 2. Check if the title 'todos' is displayed 94 | 2.1) action: markSpecAsComplete, reason: The spec passed, fullProseExplanationOfReasoning100charmax: The title 'todos' is displayed as an h1 element in the header section., planningThoughtAboutTheActionIWillTake: I will verify if the title 'todos' is displayed by checking the presence of the h1 element with the text 'todos'. 95 | ✔ 3. Check if the input box with placeholder 'What needs to be done?' is present 96 | 3.1) action: markSpecAsComplete, reason: The spec passed, fullProseExplanationOfReasoning100charmax: The input box with the placeholder 'What needs to be done?' is present in the HTML snapshot., planningThoughtAboutTheActionIWillTake: I will check if the input box with the placeholder 'What needs to be done?' is present in the HTML snapshot. 97 | ✔ 4. Check if a new todo item can be added 98 | 4.1) action: clickOn, cssSelector: input.new-todo, nth: 0, planningThoughtAboutTheActionIWillTake: I will move the mouse cursor to the input field to add a new todo item. 99 | 4.2) action: keyboardInputString, cssSelector: input.new-todo, nth: 0, string: Buy groceries, planningThoughtAboutTheActionIWillTake: I will type a new todo item into the input field. 100 | 4.3) action: keyboardInputSingleKey, cssSelector: input.new-todo, nth: 0, key: Enter, planningThoughtAboutTheActionIWillTake: I will press the Enter key to add the new todo item. 101 | 4.4) action: markSpecAsComplete, reason: The spec passed, fullProseExplanationOfReasoning100charmax: The new todo item 'Buy groceries' was successfully added to the list., planningThoughtAboutTheActionIWillTake: I will mark the spec as complete because the new todo item 'Buy groceries' has been successfully added. 102 | ✔ 5. Check if a todo item can be marked as completed 103 | 5.1) action: keyboardInputString, cssSelector: input.new-todo, nth: 0, string: Test Todo Item, planningThoughtAboutTheActionIWillTake: To check if a todo item can be marked as completed, I first need to add a new todo item. I will type a new todo item in the input field and press Enter. 104 | 5.2) action: keyboardInputSingleKey, cssSelector: input.new-todo, nth: 0, key: Enter, planningThoughtAboutTheActionIWillTake: I need to press Enter to add the new todo item to the list. 105 | 5.3) action: clickOn, cssSelector: input.toggle, nth: 0, planningThoughtAboutTheActionIWillTake: Now that the todo item is added, I will mark it as completed by clicking the checkbox next to the todo item. 106 | 5.4) action: markSpecAsComplete, reason: The spec passed, fullProseExplanationOfReasoning100charmax: The todo item was successfully marked as completed., planningThoughtAboutTheActionIWillTake: The todo item has been marked as completed, as indicated by the strikethrough text and the checkbox being checked. I will mark the spec as complete. 107 | ... 108 | autospec passed for https://todomvc.com/examples/react/dist/ 109 | ``` 110 | 111 | ### Contributing 112 | 113 | autospec is open-source and we welcome contributors! Please open an 114 | [issue](https://github.com/zachblume/autospec/issues) or 115 | [pull request](https://github.com/zachblume/autospec/pulls) to get started. 116 | 117 | ### Contributors 118 | 119 | 120 | 121 | ### License 122 | 123 | This project is licensed under the MIT License. See [LICENSE](LICENSE) file for details. 124 | -------------------------------------------------------------------------------- /benchmark-results/benchmark-results-20240611215118916.json: -------------------------------------------------------------------------------- 1 | { 2 | "commitSHA": "0141eb802fbc2d737b52d36ef7c3321f3e456170", 3 | "datetime": "2024-06-11T21:51:02.887Z", 4 | "results": [ 5 | { 6 | "testUrl": "https://todomvc.com/examples/react/dist/", 7 | "status": "passed" 8 | } 9 | ], 10 | "metrics": { 11 | "total": 1, 12 | "truePositives": 1, 13 | "falsePositives": 0, 14 | "trueNegatives": 0, 15 | "falseNegatives": 0, 16 | "precision": 1, 17 | "recall": 1 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /benchmark/benchmark.ts: -------------------------------------------------------------------------------- 1 | import { main, stringifyError } from "../src/index.js"; 2 | import fs from "fs"; 3 | import path from "path"; 4 | import { fileURLToPath } from "url"; 5 | import { execSync } from "child_process"; 6 | 7 | const __filename = fileURLToPath(import.meta.url); 8 | const __dirname = path.dirname(__filename); 9 | 10 | const fullCycleExamples = [ 11 | { url: "https://todomvc.com/examples/react/dist/", shouldPass: true }, 12 | { url: "https://demo.realworld.io/", shouldPass: true }, 13 | { url: "https://astexplorer.net/", shouldPass: true }, 14 | { url: "https://excalidraw.com/", shouldPass: true }, 15 | { url: "https://vscode.dev/", shouldPass: true }, 16 | { 17 | url: "https://todomvc-with-one-bug.vercel.app", 18 | shouldPass: false, 19 | humanNote: "The delete button on todos is broken", 20 | }, 21 | ]; 22 | 23 | const specExamples = [ 24 | { 25 | url: "https://todomvc.com/examples/react/dist/", 26 | shouldPass: true, 27 | specToTest: "The user should be able to add todos", 28 | specLimit: 10, 29 | }, 30 | { 31 | url: "https://todomvc-with-one-bug.vercel.app", 32 | shouldPass: false, 33 | specToTest: "The user should be able to delete todos", 34 | specLimit: 10, 35 | }, 36 | ]; 37 | 38 | type SpecExample = { 39 | url: string; 40 | shouldPass: boolean; 41 | specToTest?: string; 42 | specLimit?: number; 43 | }; 44 | 45 | type BenchMarkResult = { 46 | testUrl: string; 47 | status: "passed" | "failed" | "error"; 48 | totalInputTokens?: number; 49 | totalOutputTokens?: number; 50 | error?: string; 51 | }; 52 | const combinedExamples: SpecExample[] = [...fullCycleExamples, ...specExamples]; 53 | 54 | const runBenchmark = async () => { 55 | const results: BenchMarkResult[] = []; 56 | const commitSHA = execSync("git rev-parse HEAD").toString().trim(); 57 | const datetime = new Date().toISOString(); 58 | let truePositives = 0; 59 | let falsePositives = 0; 60 | let trueNegatives = 0; 61 | let falseNegatives = 0; 62 | 63 | for (const example of combinedExamples) { 64 | console.log(`Running autospec on ${example.url}`); 65 | try { 66 | const { testResults, totalInputTokens, totalOutputTokens } = 67 | await main({ 68 | testUrl: example.url, 69 | modelName: "gpt-4o", 70 | specLimit: example.specLimit ?? 10, 71 | specificSpecToTest: example.specToTest, 72 | }); 73 | 74 | const allPassed = testResults.every( 75 | (result) => result.status === "passed", 76 | ); 77 | 78 | if (allPassed) { 79 | results.push({ 80 | testUrl: example.url, 81 | status: "passed", 82 | totalInputTokens, 83 | totalOutputTokens, 84 | }); 85 | if (example.shouldPass) { 86 | truePositives++; 87 | } else { 88 | falsePositives++; 89 | } 90 | } else { 91 | results.push({ testUrl: example.url, status: "failed" }); 92 | if (example.shouldPass) { 93 | falseNegatives++; 94 | } else { 95 | trueNegatives++; 96 | } 97 | } 98 | } catch (error) { 99 | console.error(`Error running autospec on ${example.url}:`, error); 100 | results.push({ 101 | testUrl: example.url, 102 | status: "error", 103 | error: stringifyError(error), 104 | }); 105 | 106 | // If the test is unable to execute, let's classify it as a false 107 | // negative since we can't be sure if it would have passed or failed 108 | falseNegatives++; 109 | } 110 | } 111 | 112 | const precision = truePositives / (truePositives + falsePositives); 113 | const recall = truePositives / (truePositives + falseNegatives); 114 | const totalInputTokens = results.reduce( 115 | (sum, result) => sum + (result.totalInputTokens || 0), 116 | 0, 117 | ); 118 | const totalOutputTokens = results.reduce( 119 | (sum, result) => sum + (result.totalOutputTokens || 0), 120 | 0, 121 | ); 122 | 123 | const metrics = { 124 | total: results.length, 125 | truePositives, 126 | falsePositives, 127 | trueNegatives, 128 | falseNegatives, 129 | precision, 130 | recall, 131 | sensitivity: recall, 132 | specificity: trueNegatives / (trueNegatives + falsePositives), 133 | f1: (2 * precision * recall) / (precision + recall), 134 | totalInputTokens, 135 | totalOutputTokens, 136 | // Approximate cost at gpt-4o pricing of $5.00 / 1M input tokens and 137 | // $15.00 / 1M output tokens: 138 | costInDollars: 139 | (totalInputTokens / 1e6) * 5 + (totalOutputTokens / 1e6) * 15, 140 | }; 141 | 142 | const resultsDir = path.join(__dirname, "benchmark-results"); 143 | if (!fs.existsSync(resultsDir)) { 144 | fs.mkdirSync(resultsDir); 145 | } 146 | const timestamp = new Date().toISOString().replace(/[^0-9]/g, ""); 147 | const resultsPath = path.join( 148 | resultsDir, 149 | `benchmark-results-${timestamp}.json`, 150 | ); 151 | const metadata = { 152 | commitSHA, 153 | datetime, 154 | results, 155 | metrics, 156 | }; 157 | 158 | const jsonString = JSON.stringify(metadata, null, 4); 159 | fs.writeFileSync(resultsPath, jsonString); 160 | console.log(jsonString); 161 | console.log(`Benchmark results and metrics saved to ${resultsPath}`); 162 | }; 163 | 164 | runBenchmark(); 165 | -------------------------------------------------------------------------------- /benchmark/examples/shouldFail/todomvc-with-one-bug/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["eslint:recommended", "plugin:react/recommended"], 3 | "settings": { 4 | "react": { 5 | "version": "17.0.2" 6 | } 7 | }, 8 | "rules": { 9 | "no-extra-parens": 0, 10 | "react/prop-types": 0, 11 | "react/react-in-jsx-scope": 0 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /benchmark/examples/shouldFail/todomvc-with-one-bug/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules 3 | .vercel 4 | -------------------------------------------------------------------------------- /benchmark/examples/shouldFail/todomvc-with-one-bug/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-react", 3 | "version": "1.0.0", 4 | "description": "A TodoMVC written in React.", 5 | "private": true, 6 | "engines": { 7 | "node": ">=18.13.0", 8 | "npm": ">=8.19.3" 9 | }, 10 | "scripts": { 11 | "build": "webpack --config webpack.prod.js", 12 | "dev": "webpack serve --open --config webpack.dev.js", 13 | "serve": "http-server ./dist -p 7002 -c-1 --cors", 14 | "test": "jest" 15 | }, 16 | "devDependencies": { 17 | "@babel/core": "^7.21.0", 18 | "@babel/preset-env": "^7.20.2", 19 | "@babel/preset-react": "^7.18.6", 20 | "babel-loader": "^9.1.2", 21 | "css-loader": "^6.7.3", 22 | "css-minimizer-webpack-plugin": "^4.2.2", 23 | "eslint-plugin-react": "^7.32.2", 24 | "html-webpack-plugin": "^5.5.0", 25 | "http-server": "^14.1.1", 26 | "mini-css-extract-plugin": "^2.7.2", 27 | "style-loader": "^3.3.1", 28 | "webpack": "^5.75.0", 29 | "webpack-cli": "^5.0.1", 30 | "webpack-dev-server": "^4.11.1", 31 | "webpack-merge": "^5.8.0" 32 | }, 33 | "dependencies": { 34 | "classnames": "^2.2.5", 35 | "react": "^17.0.2", 36 | "react-dom": "^17.0.2", 37 | "react-router-dom": "^6.8.2", 38 | "todomvc-app-css": "^2.4.2", 39 | "todomvc-common": "^1.0.5" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /benchmark/examples/shouldFail/todomvc-with-one-bug/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | TodoMVC: React 9 | 10 | 11 |
12 |
13 |

Double-click to edit a todo

14 |

Created by the TodoMVC Team

15 |

Part of TodoMVC

16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /benchmark/examples/shouldFail/todomvc-with-one-bug/readme.md: -------------------------------------------------------------------------------- 1 | # TodoMVC: React 2 | 3 | ## Description 4 | 5 | This application uses React 17.0.2 to implement a todo application. 6 | 7 | - [React](https://reactjs.org/) is a JavaScript library for creating user interfaces. 8 | 9 | ## Implementation details 10 | 11 | React focuses mainly on providing composable user interfaces to enable developers to build an appealing website or web app. React does not force the user to utilize a particular design pattern, but it does provide useful hooks to implement an MVC pattern, if desired. 12 | 13 | React:\ 14 | Model: Todo reducer (reducer.js)\ 15 | View: React ui components\ 16 | Controller: App component + useReducer hook 17 | 18 | MVC:\ 19 | Model: Maintains the data and behavior of an application\ 20 | View: Displays the model in the ui\ 21 | Controller: Serves as an interface between view & model components 22 | 23 | ## Build steps 24 | 25 | To build the static files, this application utilizes webpack. It minifies and optimizes output files and copies all necessary files to a `dist` folder. 26 | 27 | ## Requirements 28 | 29 | The only requirement is an installation of Node, to be able to install dependencies and run scripts to serve a local server. 30 | 31 | ``` 32 | * Node (min version: 18.13.0) 33 | * NPM (min version: 8.19.3) 34 | ``` 35 | 36 | ## Local preview 37 | 38 | ``` 39 | terminal: 40 | 1. npm install 41 | 2. npm run start 42 | ``` 43 | -------------------------------------------------------------------------------- /benchmark/examples/shouldFail/todomvc-with-one-bug/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "react-dom"; 3 | import { HashRouter, Route, Routes } from "react-router-dom"; 4 | 5 | import { App } from "./todo/app"; 6 | import "todomvc-app-css/index.css"; 7 | 8 | render( 9 | 10 | 11 | } /> 12 | 13 | , 14 | document.getElementById("root"), 15 | ); 16 | -------------------------------------------------------------------------------- /benchmark/examples/shouldFail/todomvc-with-one-bug/src/todo/app.css: -------------------------------------------------------------------------------- 1 | /* used for things that should be hidden in the ui, 2 | but useful for people who use screen readers */ 3 | .visually-hidden { 4 | border: 0; 5 | clip: rect(0 0 0 0); 6 | clip-path: inset(50%); 7 | height: 1px; 8 | width: 1px; 9 | margin: -1px; 10 | padding: 0; 11 | overflow: hidden; 12 | position: absolute; 13 | white-space: nowrap; 14 | } 15 | 16 | .toggle-all { 17 | width: 40px !important; 18 | height: 60px !important; 19 | right: auto !important; 20 | } 21 | 22 | .toggle-all-label { 23 | pointer-events: none; 24 | } 25 | -------------------------------------------------------------------------------- /benchmark/examples/shouldFail/todomvc-with-one-bug/src/todo/app.jsx: -------------------------------------------------------------------------------- 1 | import { useReducer } from "react"; 2 | import { Header } from "./components/header"; 3 | import { Main } from "./components/main"; 4 | import { Footer } from "./components/footer"; 5 | 6 | import { todoReducer } from "./reducer"; 7 | 8 | import "./app.css"; 9 | 10 | export function App() { 11 | const [todos, dispatch] = useReducer(todoReducer, []); 12 | 13 | return ( 14 | <> 15 |
16 |
17 |