From 86f7cc0afc9f653e79e8093e5b8c44b9140dba78 Mon Sep 17 00:00:00 2001 From: walief Date: Tue, 26 May 2026 18:25:10 -0700 Subject: [PATCH] Add search feature, tests, and README updates - Implement search_books in samples/book-app-project/books.py - Add CLI `search` command in samples/book-app-project/book_app.py - Add unit tests for search behavior in samples/book-app-project/tests/test_books.py - Update README with search usage examples - Update .github/copilot-instructions.md and add Playwright demo scaffold Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 124 ++-- samples/book-app-project/README.md | 24 +- samples/book-app-project/book_app.py | 59 +- samples/book-app-project/books.py | 17 +- samples/book-app-project/tests/test_books.py | 30 + samples/mcp-configs/playwright-mcp.json | 10 + samples/src/package-lock.json | 708 +++++++++++++++++++ samples/src/package.json | 14 + samples/src/test-results/.last-run.json | 4 + samples/src/tests/playwright.spec.js | 7 + test-results/.last-run.json | 4 + 11 files changed, 907 insertions(+), 94 deletions(-) create mode 100644 samples/mcp-configs/playwright-mcp.json create mode 100644 samples/src/package-lock.json create mode 100644 samples/src/package.json create mode 100644 samples/src/test-results/.last-run.json create mode 100644 samples/src/tests/playwright.spec.js create mode 100644 test-results/.last-run.json diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index b4cbb8b6..b5e5d771 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,70 +1,54 @@ -# Copilot Instructions - -These instructions guide GitHub Copilot when working in this repository. - -## Project Context - -This is a **beginner-friendly educational course** teaching GitHub Copilot CLI. The repo contains Markdown chapters (00–07), Python/C#/JavaScript sample apps, and supporting assets (images, demo GIFs, glossary). It is **not** a software product — it is technical courseware. - -## Writing Conventions - -- **Audience**: Beginners with no AI/ML experience. Explain every technical term on first use. -- **Tone**: Friendly, encouraging, practical. Avoid jargon without explanation. -- **Examples**: All code blocks and `copilot` commands must be copy-paste ready. Test them mentally before including. -- **Naming**: Use kebab-case for session names, file names, and identifiers (e.g., `book-app-review`, not `book app review`). -- **Command syntax**: Standardize flag format — use `--flag=value` consistently when a value is required, `--flag` when boolean. -- **Precision**: Don't over-specify tool behavior that may vary across shells or OS. Describe what the user will see, not implementation details. -- **Fallbacks**: When referencing tool version requirements (e.g., `gh` CLI version), always include upgrade instructions or a manual alternative. - -## Content Conventions (from PR review patterns) - -These patterns were mined from actual PR review feedback and represent recurring maintainer expectations: - -- When showing multi-step workflows, ensure all prerequisite steps are included (e.g., `git add` before `git diff --staged`). -- When introducing a concept with an example, use consistent naming throughout the section — don't mix kebab-case and quoted names. -- When describing a command's behavior, match the level of specificity in the official release notes — don't state behavior that may differ across environments. -- If a feature requires a minimum tool version, mention the version AND provide a fallback path for users who can't upgrade yet. - -## Sample Code Conventions - -- **Primary sample**: Always use `samples/book-app-project/` (Python) for examples in chapters. -- **Test framework**: pytest — test files go in `samples/book-app-project/tests/` and follow `test_*.py` naming. -- **Python version**: 3.10+ (per `samples/book-app-project/pyproject.toml`). -- **Intentional bugs**: Files in `samples/book-app-buggy/` and `samples/buggy-code/` contain **deliberate bugs** for exercises. Never fix them. - -## Chapter Structure - -Every chapter (00–07) follows the same pattern in its `README.md`: - -1. Real-World Analogy -2. Core Concepts -3. Hands-On Examples -4. Assignment -5. What's Next - -Do not deviate from this structure when editing or adding chapter content. - -## Markdown Formatting - -- Use standard GitHub-Flavored Markdown. -- Images go in the repo-root `images/` directory. -- Use relative links for cross-chapter references (e.g., `../03-development-workflows/README.md`). -- Emoji usage is encouraged for section headers (matching existing style). - -## Maintenance Matrix - -| Change Made | Files to Update | -|---|---| -| New chapter added | `README.md` (course table), `AGENTS.md` (structure table), `images/learning-path.png` | -| Chapter content updated | The chapter's `README.md`, verify cross-references in adjacent chapters | -| New sample app variant added | `AGENTS.md` (structure table), `samples/` directory, relevant chapter references | -| Sample app code changed | `samples/book-app-project/tests/` (update/add tests), chapters referencing that code | -| Bug intentionally added to buggy samples | `samples/book-app-buggy/` or `samples/buggy-code/` only — do NOT update tests | -| New skill added | `.github/skills/{skill-name}/SKILL.md`, `samples/skills/` (example copy), Chapter 05 | -| New agent template added | `samples/agents/`, Chapter 04 | -| New MCP config added | `samples/mcp-configs/`, Chapter 06 | -| Glossary term introduced | `GLOSSARY.md` — add definition in alphabetical order | -| npm scripts changed | `package.json`, `AGENTS.md` (build section) | -| Devcontainer updated | `.devcontainer/devcontainer.json`, Chapter 00 (setup instructions) | -| Image or banner changed | `images/` directory, any README referencing the image | -| Copilot CLI version requirements change | Chapter 00, Chapter 01, `.devcontainer/devcontainer.json` | +# Copilot Instructions (project-specific) + +Purpose +- Short guide for GitHub Copilot CLI sessions working on this repository (courseware, not a production product). + +Build, test, and lint commands +- Python (primary sample: samples/book-app-project): + - Run all tests: python -m pytest samples/book-app-project -q + - Run a single test: python -m pytest samples/book-app-project/tests/test_books.py::test_name -q + - Install deps: follow pyproject.toml (e.g., `poetry install`), or `python -m pip install -r samples/book-app-project/requirements.txt` if present + - Lint (if available): python -m flake8 samples/book-app-project + +- JavaScript (samples/book-app-project-js): + - Install & test: cd samples/book-app-project-js && npm install && npm test + - Run a single test (Jest): npm test -- -t "test name" (or use npx jest ) + - Lint: npm run lint (if script exists) + +- C# (samples/book-app-project-cs): + - Run tests: dotnet test samples/book-app-project-cs + - Run a single test: dotnet test samples/book-app-project-cs --filter "FullyQualifiedName~TestName" + +High-level architecture +- Course chapters live in top-level folders 00-07; each chapter is self-contained with a README following the same structure. +- Primary sample: samples/book-app-project (Python). JS and C# variants exist under samples/book-app-project-js and samples/book-app-project-cs. +- Agents and skills: + - .github/agents and .github/skills store agent & skill templates used by course demos + - samples/agents and samples/skills contain example implementations +- CI / automation: .github/workflows and .github/scripts contain helper scripts for building demo assets and translations. +- MCP examples: samples/mcp-configs/ and chapter 06 contain MCP server configs and docs. + +Key conventions (repo-specific) +- Primary-sample-first: Use samples/book-app-project for Python-centric examples in chapters. +- Test file naming: pytest tests are under samples/book-app-project/tests/ and use test_*.py naming. +- Python version: 3.10+ (see samples/book-app-project/pyproject.toml). +- Intentional-bugs: Do NOT fix files in samples/book-app-buggy/ or samples/buggy-code/ — they are exercises. +- Chapter README format: Every chapter README must include: Real-World Analogy; Core Concepts; Hands-On Examples; Assignment; What's Next. +- Filenames & identifiers: prefer kebab-case for session names and examples used in text and commands. +- Command flag style: use `--flag=value` when a value is required and `--flag` for booleans in examples. +- Docs-first edits: When changing sample behavior, update corresponding chapter README and tests. + +Files & places to check for AI sessions +- Primary sample code: samples/book-app-project/ +- Tests: samples/book-app-project/tests/ +- Agent templates: .github/agents/ and samples/agents/ +- Skill templates: .github/skills/ and samples/skills/ +- MCP server examples: samples/mcp-configs/ and 06-mcp-servers/ + +MCP servers +- This repo includes MCP examples (samples/mcp-configs/) and a chapter (06-mcp-servers). Would you like assistance configuring an MCP server for local testing? (Ask to configure: Playwright or other demo servers.) + +Summary +- This file adds actionable build/test/lint commands, a concise architecture overview, and repo-specific conventions to help future Copilot sessions behave usefully and avoid common mistakes. + +Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> diff --git a/samples/book-app-project/README.md b/samples/book-app-project/README.md index d3dd580a..b175ee9a 100644 --- a/samples/book-app-project/README.md +++ b/samples/book-app-project/README.md @@ -27,20 +27,34 @@ It can add, remove, and list books. Also mark them as read. ## Running the App +Available commands (run from samples/book-app-project/): + ```bash -python book_app.py list -python book_app.py add -python book_app.py find -python book_app.py remove +python book_app.py list # Show all books +python book_app.py add # Interactive add +python book_app.py find # Find books by author (exact match) +python book_app.py remove # Remove a book by title +python book_app.py search # Interactive search prompt +python book_app.py search "dune" title # Search titles containing "dune" +python book_app.py search "gibson" author # Search authors containing "gibson" python book_app.py help ``` ## Running Tests +Run the full test suite for the sample: + ```bash -python -m pytest tests/ +python -m pytest samples/book-app-project -q ``` +Run a single test by node (example): + +```bash +python -m pytest samples/book-app-project/tests/test_books.py::test_search_by_title_exact -q +``` + + --- ## Notes diff --git a/samples/book-app-project/book_app.py b/samples/book-app-project/book_app.py index f0100c2d..b8d68ea3 100644 --- a/samples/book-app-project/book_app.py +++ b/samples/book-app-project/book_app.py @@ -1,12 +1,13 @@ import sys from books import BookCollection +from typing import List, Any # Global collection instance collection = BookCollection() -def show_books(books): +def show_books(books: List[Any]) -> None: """Display books in a user-friendly format.""" if not books: print("No books found.") @@ -21,12 +22,12 @@ def show_books(books): print() -def handle_list(): +def handle_list() -> None: books = collection.list_books() show_books(books) -def handle_add(): +def handle_add() -> None: print("\nAdd a New Book\n") title = input("Title: ").strip() @@ -41,7 +42,7 @@ def handle_add(): print(f"\nError: {e}\n") -def handle_remove(): +def handle_remove() -> None: print("\nRemove a Book\n") title = input("Enter the title of the book to remove: ").strip() @@ -50,7 +51,7 @@ def handle_remove(): print("\nBook removed if it existed.\n") -def handle_find(): +def handle_find() -> None: print("\nFind Books by Author\n") author = input("Author name: ").strip() @@ -59,7 +60,26 @@ def handle_find(): show_books(books) -def show_help(): +def handle_search() -> None: + """Search books by title or author. + + Usage: book_app.py search [title|author] + If query is omitted, user is prompted. + """ + # prefer CLI args when provided + if len(sys.argv) >= 3: + query = sys.argv[2] + else: + query = input("Query: ").strip() + + field = 'title' + if len(sys.argv) >= 4: + field = sys.argv[3].lower() + books = collection.search_books(query, field) + show_books(books) + + +def show_help() -> None: print(""" Book Collection Helper @@ -67,28 +87,31 @@ def show_help(): list - Show all books add - Add a new book remove - Remove a book by title - find - Find books by author + find - Find books by author (exact match) + search - Search books by title or author (partial, case-insensitive). Usage: search [title|author] help - Show this help message """) -def main(): +def main() -> None: if len(sys.argv) < 2: show_help() return command = sys.argv[1].lower() - if command == "list": - handle_list() - elif command == "add": - handle_add() - elif command == "remove": - handle_remove() - elif command == "find": - handle_find() - elif command == "help": - show_help() + COMMANDS = { + "list": handle_list, + "add": handle_add, + "remove": handle_remove, + "find": handle_find, + "search": handle_search, + "help": show_help, + } + + handler = COMMANDS.get(command) + if handler: + handler() else: print("Unknown command.\n") show_help() diff --git a/samples/book-app-project/books.py b/samples/book-app-project/books.py index 2110689f..8e510ef3 100644 --- a/samples/book-app-project/books.py +++ b/samples/book-app-project/books.py @@ -68,5 +68,20 @@ def remove_book(self, title: str) -> bool: return False def find_by_author(self, author: str) -> List[Book]: - """Find all books by a given author.""" + """Find all books by a given author (exact match, case-insensitive).""" return [b for b in self.books if b.author.lower() == author.lower()] + + def search_books(self, query: str, field: str = 'title') -> List[Book]: + """Search books by title or author. + + Args: + query: substring to search for (case-insensitive). + field: 'title' or 'author'. Defaults to 'title'. + Returns: + List[Book]: matching books (partial, case-insensitive). + """ + q = query.lower() + if field == 'author': + return [b for b in self.books if q in b.author.lower()] + # default to title + return [b for b in self.books if q in b.title.lower()] diff --git a/samples/book-app-project/tests/test_books.py b/samples/book-app-project/tests/test_books.py index 061149c5..899b6dfb 100644 --- a/samples/book-app-project/tests/test_books.py +++ b/samples/book-app-project/tests/test_books.py @@ -51,3 +51,33 @@ def test_remove_book_invalid(): collection = BookCollection() result = collection.remove_book("Nonexistent Book") assert result is False + + +def test_search_by_title_exact(): + collection = BookCollection() + collection.add_book("Dune", "Frank Herbert", 1965) + collection.add_book("Dune Messiah", "Frank Herbert", 1969) + results = collection.search_books("Dune", field='title') + assert any(b.title == "Dune" for b in results) + + +def test_search_by_title_partial_case_insensitive(): + collection = BookCollection() + collection.add_book("The Hobbit", "J.R.R. Tolkien", 1937) + results = collection.search_books("hob", field='title') + assert any("hob" in b.title.lower() for b in results) + + +def test_search_by_author_partial_case_insensitive(): + collection = BookCollection() + collection.add_book("Neuromancer", "William Gibson", 1984) + collection.add_book("Count Zero", "William Gibson", 1986) + results = collection.search_books("gibson", field='author') + assert len(results) == 2 + + +def test_search_no_results(): + collection = BookCollection() + collection.add_book("Frankenstein", "Mary Shelley", 1818) + results = collection.search_books("notfound", field='title') + assert results == [] diff --git a/samples/mcp-configs/playwright-mcp.json b/samples/mcp-configs/playwright-mcp.json new file mode 100644 index 00000000..ad6d69f3 --- /dev/null +++ b/samples/mcp-configs/playwright-mcp.json @@ -0,0 +1,10 @@ +{ + "mcpServers": { + "playwright-demo": { + "type": "local", + "command": "npx", + "args": ["-y", "http-server", "samples/src", "-p", "3000"], + "tools": ["browser"] + } + } +} diff --git a/samples/src/package-lock.json b/samples/src/package-lock.json new file mode 100644 index 00000000..9ddd0f8a --- /dev/null +++ b/samples/src/package-lock.json @@ -0,0 +1,708 @@ +{ + "name": "samples-src", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "samples-src", + "version": "1.0.0", + "devDependencies": { + "@playwright/test": "^1.60.0", + "http-server": "^14.1.1", + "playwright": "^1.36.0" + } + }, + "node_modules/@playwright/test": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz", + "integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-server": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-auth": "^2.0.1", + "chalk": "^4.1.2", + "corser": "^2.0.1", + "he": "^1.2.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy": "^1.18.1", + "mime": "^1.6.0", + "minimist": "^1.2.6", + "opener": "^1.5.1", + "portfinder": "^1.0.28", + "secure-compare": "3.0.1", + "union": "~0.5.0", + "url-join": "^4.0.1" + }, + "bin": { + "http-server": "bin/http-server" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/playwright": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", + "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/portfinder": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.38.tgz", + "integrity": "sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^3.2.6", + "debug": "^4.3.6" + }, + "engines": { + "node": ">= 10.12" + } + }, + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", + "dev": true, + "license": "MIT" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dev": true, + "dependencies": { + "qs": "^6.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + } + } +} diff --git a/samples/src/package.json b/samples/src/package.json new file mode 100644 index 00000000..6dac4b67 --- /dev/null +++ b/samples/src/package.json @@ -0,0 +1,14 @@ +{ + "name": "samples-src", + "version": "1.0.0", + "private": true, + "scripts": { + "start": "npx http-server ./ -p 3000", + "test:playwright": "npx playwright test" + }, + "devDependencies": { + "@playwright/test": "^1.60.0", + "http-server": "^14.1.1", + "playwright": "^1.36.0" + } +} diff --git a/samples/src/test-results/.last-run.json b/samples/src/test-results/.last-run.json new file mode 100644 index 00000000..cbcc1fba --- /dev/null +++ b/samples/src/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "passed", + "failedTests": [] +} \ No newline at end of file diff --git a/samples/src/tests/playwright.spec.js b/samples/src/tests/playwright.spec.js new file mode 100644 index 00000000..fad19661 --- /dev/null +++ b/samples/src/tests/playwright.spec.js @@ -0,0 +1,7 @@ +const { test, expect } = require('@playwright/test'); + +test('homepage loads', async ({ page }) => { + await page.goto('http://localhost:3000'); + // Basic smoke check: page loaded and has a document title + await expect(page).toHaveTitle(/.*/); +}); diff --git a/test-results/.last-run.json b/test-results/.last-run.json new file mode 100644 index 00000000..5fca3f84 --- /dev/null +++ b/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "failed", + "failedTests": [] +} \ No newline at end of file