-
Notifications
You must be signed in to change notification settings - Fork 0
Add CodSpeed performance benchmarks #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| name: CodSpeed | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - main | ||
| pull_request: | ||
| # `workflow_dispatch` allows CodSpeed to trigger backtest | ||
| # performance analysis in order to generate initial data. | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
|
|
||
| jobs: | ||
| codspeed: | ||
| name: Run benchmarks | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 22 | ||
| cache: npm | ||
|
|
||
| - name: Install dependencies | ||
| run: npm ci | ||
|
|
||
| - name: Run benchmarks | ||
| uses: CodSpeedHQ/action@v4 | ||
|
Check warning on line 32 in .github/workflows/codspeed.yml
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| with: | ||
| mode: simulation | ||
| run: npx vitest bench --run | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,49 +1,5 @@ | ||
| import GameCard from "@/components/GameCard"; | ||
|
|
||
| const games = [ | ||
| { | ||
| id: 1, | ||
| title: "Chess Master", | ||
| description: "Play chess against AI or friends online in real time.", | ||
| image: "/placeholder.svg?height=200&width=300", | ||
| category: "Strategy", | ||
| }, | ||
| { | ||
| id: 2, | ||
| title: "Quick Quiz", | ||
| description: "Test your knowledge with fun trivia questions.", | ||
| image: "/placeholder.svg?height=200&width=300", | ||
| category: "Trivia", | ||
| }, | ||
| { | ||
| id: 3, | ||
| title: "Memory Match", | ||
| description: "Challenge your memory with this classic card-matching game.", | ||
| image: "/placeholder.svg?height=200&width=300", | ||
| category: "Puzzle", | ||
| }, | ||
| { | ||
| id: 4, | ||
| title: "Snake Game", | ||
| description: "Eat food, grow longer, and avoid running into yourself.", | ||
| image: "/placeholder.svg?height=200&width=300", | ||
| category: "Arcade", | ||
| }, | ||
| { | ||
| id: 5, | ||
| title: "Word Builder", | ||
| description: "Create as many words as you can from scrambled letters.", | ||
| image: "/placeholder.svg?height=200&width=300", | ||
| category: "Word", | ||
| }, | ||
| { | ||
| id: 6, | ||
| title: "Puzzle Solver", | ||
| description: "Solve challenging jigsaw puzzles of increasing difficulty.", | ||
| image: "/placeholder.svg?height=200&width=300", | ||
| category: "Puzzle", | ||
| }, | ||
| ]; | ||
| import { games } from "@/lib/games"; | ||
|
Check warning on line 2 in app/page.tsx
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential runtime error due to lack of validation for imported const validGames = Array.isArray(games) ? games : [];Or handle errors gracefully to improve robustness. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2 | Confidence: Low Speculative: The extracted utility functions ( |
||
|
|
||
| export default function Home() { | ||
| return ( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| import { bench, describe } from "vitest"; | ||
|
Check warning on line 1 in bench/games.bench.ts
|
||
| import { | ||
|
Check warning on line 2 in bench/games.bench.ts
|
||
| games, | ||
| filterByCategory, | ||
|
Check notice on line 4 in bench/games.bench.ts
|
||
| searchGames, | ||
| sortByTitle, | ||
| getCategories, | ||
| getGameById, | ||
|
Check notice on line 8 in bench/games.bench.ts
|
||
| } from "@/lib/games"; | ||
|
Check warning on line 9 in bench/games.bench.ts
|
||
| import type { Game } from "@/lib/games"; | ||
|
Check notice on line 10 in bench/games.bench.ts
|
||
|
|
||
| // Build a larger dataset from the base games for more realistic benchmarks | ||
| function generateLargeGameList(size: number): Game[] { | ||
| const categories = [ | ||
| "Strategy", | ||
| "Trivia", | ||
| "Puzzle", | ||
| "Arcade", | ||
| "Word", | ||
| "RPG", | ||
| "Action", | ||
| "Simulation", | ||
| ]; | ||
| const result: Game[] = []; | ||
| for (let i = 0; i < size; i++) { | ||
| result.push({ | ||
| id: i + 1, | ||
| title: `Game ${i + 1}`, | ||
| description: `Description for game number ${i + 1} with various keywords and details.`, | ||
| image: `/placeholder.svg?height=200&width=300`, | ||
| category: categories[i % categories.length], | ||
| }); | ||
| } | ||
| return result; | ||
| } | ||
|
Comment on lines
+13
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dataset Realism for Benchmarks The Recommended solution: const title = i % 10 === 0 ? "Game Special" : `Game ${i + 1}`;
const description = i % 5 === 0 ? "Common description" : `Description for game number ${i + 1}`; |
||
|
|
||
| const largeGameList = generateLargeGameList(1000); | ||
|
|
||
| describe("filterByCategory", () => { | ||
| bench("filter base games by category", () => { | ||
| filterByCategory(games, "Puzzle"); | ||
| }); | ||
|
Comment on lines
+39
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2 | Confidence: Medium Speculative: The benchmarks for Code Suggestion: bench("filter 1000 games with no match", () => {
filterByCategory(largeGameList, "NonExistentCategory");
});
bench("filter empty list", () => {
filterByCategory([], "Puzzle");
}); |
||
|
|
||
| bench("filter 1000 games by category", () => { | ||
| filterByCategory(largeGameList, "Puzzle"); | ||
| }); | ||
| }); | ||
|
|
||
| describe("searchGames", () => { | ||
| bench("search base games by title", () => { | ||
| searchGames(games, "chess"); | ||
| }); | ||
|
|
||
| bench("search 1000 games by keyword", () => { | ||
| searchGames(largeGameList, "keywords"); | ||
| }); | ||
|
|
||
| bench("search with no results", () => { | ||
| searchGames(largeGameList, "nonexistent-query-string"); | ||
| }); | ||
| }); | ||
|
|
||
| describe("sortByTitle", () => { | ||
| bench("sort base games ascending", () => { | ||
| sortByTitle(games); | ||
| }); | ||
|
|
||
| bench("sort 1000 games ascending", () => { | ||
| sortByTitle(largeGameList); | ||
| }); | ||
|
|
||
| bench("sort 1000 games descending", () => { | ||
| sortByTitle(largeGameList, false); | ||
| }); | ||
| }); | ||
|
|
||
| describe("getCategories", () => { | ||
| bench("get categories from base games", () => { | ||
| getCategories(games); | ||
| }); | ||
|
|
||
| bench("get categories from 1000 games", () => { | ||
| getCategories(largeGameList); | ||
| }); | ||
| }); | ||
|
|
||
| describe("getGameById", () => { | ||
| bench("find first game by id", () => { | ||
| getGameById(games, 1); | ||
| }); | ||
|
|
||
| bench("find last game by id in 1000 games", () => { | ||
| getGameById(largeGameList, 1000); | ||
| }); | ||
| }); | ||
|
Comment on lines
+37
to
+95
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing Edge Case Benchmarks The current benchmarks do not test edge cases such as empty arrays or invalid input values. Including such cases can help ensure the robustness and error handling of the library functions under test. Recommended solution: describe("edge cases", () => {
bench("filter empty array", () => {
filterByCategory([], "Puzzle");
});
bench("search empty array", () => {
searchGames([], "chess");
});
bench("sort empty array", () => {
sortByTitle([]);
});
bench("get categories from empty array", () => {
getCategories([]);
});
bench("get game by invalid id", () => {
getGameById(games, -1);
});
}); |
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,82 @@ | ||||||||||||||||||||||||||||
| export interface Game { | ||||||||||||||||||||||||||||
| id: number; | ||||||||||||||||||||||||||||
| title: string; | ||||||||||||||||||||||||||||
| description: string; | ||||||||||||||||||||||||||||
| image: string; | ||||||||||||||||||||||||||||
| category: string; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export const games: Game[] = [ | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| id: 1, | ||||||||||||||||||||||||||||
| title: "Chess Master", | ||||||||||||||||||||||||||||
| description: "Play chess against AI or friends online in real time.", | ||||||||||||||||||||||||||||
| image: "/placeholder.svg?height=200&width=300", | ||||||||||||||||||||||||||||
| category: "Strategy", | ||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| id: 2, | ||||||||||||||||||||||||||||
| title: "Quick Quiz", | ||||||||||||||||||||||||||||
| description: "Test your knowledge with fun trivia questions.", | ||||||||||||||||||||||||||||
| image: "/placeholder.svg?height=200&width=300", | ||||||||||||||||||||||||||||
| category: "Trivia", | ||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| id: 3, | ||||||||||||||||||||||||||||
| title: "Memory Match", | ||||||||||||||||||||||||||||
| description: "Challenge your memory with this classic card-matching game.", | ||||||||||||||||||||||||||||
| image: "/placeholder.svg?height=200&width=300", | ||||||||||||||||||||||||||||
| category: "Puzzle", | ||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| id: 4, | ||||||||||||||||||||||||||||
| title: "Snake Game", | ||||||||||||||||||||||||||||
| description: "Eat food, grow longer, and avoid running into yourself.", | ||||||||||||||||||||||||||||
| image: "/placeholder.svg?height=200&width=300", | ||||||||||||||||||||||||||||
| category: "Arcade", | ||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| id: 5, | ||||||||||||||||||||||||||||
| title: "Word Builder", | ||||||||||||||||||||||||||||
| description: "Create as many words as you can from scrambled letters.", | ||||||||||||||||||||||||||||
| image: "/placeholder.svg?height=200&width=300", | ||||||||||||||||||||||||||||
| category: "Word", | ||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| id: 6, | ||||||||||||||||||||||||||||
| title: "Puzzle Solver", | ||||||||||||||||||||||||||||
| description: "Solve challenging jigsaw puzzles of increasing difficulty.", | ||||||||||||||||||||||||||||
| image: "/placeholder.svg?height=200&width=300", | ||||||||||||||||||||||||||||
| category: "Puzzle", | ||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||
|
Comment on lines
+9
to
+52
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: Exported The Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export function filterByCategory(gameList: Game[], category: string): Game[] { | ||||||||||||||||||||||||||||
| return gameList.filter( | ||||||||||||||||||||||||||||
| (game) => game.category.toLowerCase() === category.toLowerCase(), | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+54
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Consider normalizing/cleaning the category input (e.g. trimming) before comparison. Currently only case-insensitive differences are handled. If
Suggested change
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export function searchGames(gameList: Game[], query: string): Game[] { | ||||||||||||||||||||||||||||
| const lowerQuery = query.toLowerCase(); | ||||||||||||||||||||||||||||
| return gameList.filter( | ||||||||||||||||||||||||||||
| (game) => | ||||||||||||||||||||||||||||
| game.title.toLowerCase().includes(lowerQuery) || | ||||||||||||||||||||||||||||
| game.description.toLowerCase().includes(lowerQuery), | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+60
to
+67
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: In Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export function sortByTitle(gameList: Game[], ascending = true): Game[] { | ||||||||||||||||||||||||||||
| return [...gameList].sort((a, b) => { | ||||||||||||||||||||||||||||
| const cmp = a.title.localeCompare(b.title); | ||||||||||||||||||||||||||||
| return ascending ? cmp : -cmp; | ||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export function getCategories(gameList: Game[]): string[] { | ||||||||||||||||||||||||||||
| return [...new Set(gameList.map((game) => game.category))].sort(); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export function getGameById(gameList: Game[], id: number): Game | undefined { | ||||||||||||||||||||||||||||
| return gameList.find((game) => game.id === id); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
credential persistence through GitHub Actions artifacts [zizmor:zizmor/artipacked]